Voicemail is a telephony application with which everyone is familiar. Twilio makes building a voicemail system simple, and makes integrating it with the web or other messaging technologies just as straight forward. This recipe will describe the building of a simple voicemail box application.
A voicemail system consists of two applications. One application allows people to record messages and leave them in a mailbox. The other application allows the owner of the mailbox to listen to and manage the messages he has received. This system assigns each mailbox an extension and a passcode and has two entry points, one for leaving messages and one for checking them. If you were to integrate this into your own system or the Company Directory recipe, you would most likely wire the entry points into your own application, bypassing the step where you ask the caller for their mailbox extension.
This demo utilizes the Twilio Response Library to facilitate TwiML creation. If you haven't already, visit the Response Library introduction to familiarize yourself. Make sure to download a copy of the PHP library that contains 'twilio.php' which is referenced in this HowTo.
We first provide a HTML form to the user containing a field for the user's phone number.
howtos/voicemail/voicemail.sqlCREATE TABLE voicemailbox ( vmb_extension varchar(8) primary key not null, vmb_description varchar(32) not null, vmb_passcode varchar(8) not null, vmb_last_checked datetime ); insert into voicemailbox ( vmb_extension, vmb_description, vmb_passcode )values ( '1234', 'Test voicemail', '9411' ); CREATE TABLE messages ( message_id int not null primary key auto_increment, message_frn_vmb_extension varchar(8) not null, message_date datetime not null, message_from varchar(16), message_audio_url varchar(1024), message_flag int(1) default 0 );
Next we'll need to define our data access functions. We'll need a couple functions here: a function for adding a new voicemail to the messages table, functions for retrieving messages, and a function for updating the voicemail flag (new,saved,deleted). We'll only show the function headers below, follow the link below to the full code.
howtos/voicemail/messages.php<?php
//DB Constants - Change to your settings
$db_host='localhost';
$db_name='company_directory';
$db_user='db_username';
$db_passwd='db_password';
//function for retrieving voicemail box by exten
function getMailbox($voicemail_exten) {
global $db_name, $db_host,$db_user,$db_passwd;
mysql_connect($db_host, $db_user, $db_passwd)
or die('Could not connect: ' . mysql_error());
mysql_select_db($db_name) or die('Could not select database');
//make sure inputs are db safe
$voicemail_exten = mysql_real_escape_string($voicemail_exten);
// Performing SQL query
$query = sprintf("select * from voicemailbox where vmb_extension='%s'",$voicemail_exten);
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
$mailbox = false;
if($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
$mailbox = array();
$mailbox['exten'] = $line['vmb_extension'];
$mailbox['desc'] = $line['vmb_description'];
$mailbox['passcode'] = $line['vmb_passcode'];
}
mysql_close();
return $mailbox;
}
function addMessage($voicemail_exten, $caller_id, $recording_url){
global $db_name, $db_host,$db_user,$db_passwd;
mysql_connect($db_host, $db_user, $db_passwd)
or die('Could not connect: ' . mysql_error());
mysql_select_db($db_name) or die('Could not select database');
//make sure inputs are db safe
$voicemail_exten = mysql_real_escape_string($voicemail_exten);
$caller_id = mysql_real_escape_string($caller_id);
$recording_url = mysql_real_escape_string($recording_url);
// Performing SQL query
$query = sprintf("insert into messages (message_frn_vmb_extension,message_date,message_from,message_audio_url,message_flag)".
" values ('%s',now(),'%s','%s',0)",$voicemail_exten,$caller_id, $recording_url);
mysql_query($query) or die('Query failed: ' . mysql_error());
$id = mysql_insert_id();
mysql_close();
return $id;
}
function updateMessageFlag($msg_id, $flag=0){
global $db_name, $db_host,$db_user,$db_passwd;
mysql_connect($db_host, $db_user, $db_passwd)
or die('Could not connect: ' . mysql_error());
mysql_select_db($db_name) or die('Could not select database');
//make sure inputs are db safe
$msg_id = mysql_real_escape_string($msg_id);
$flag = mysql_real_escape_string($flag);
// Performing SQL query
$query = sprintf("update messages set message_flag=%d where message_id=%d",$flag, $msg_id);
mysql_query($query) or die('Query failed: ' . mysql_error());
mysql_close();
}
function getMessages($voicemail_exten,$flag=0){
global $db_name, $db_host,$db_user,$db_passwd;
mysql_connect($db_host, $db_user, $db_passwd)
or die('Could not connect: ' . mysql_error());
mysql_select_db($db_name) or die('Could not select database');
//make sure inputs are db safe
$voicemail_exten = mysql_real_escape_string($voicemail_exten);
$flag = mysql_real_escape_string($flag);
// Performing SQL query
$query = sprintf("select * from messages where message_flag=%d and message_frn_vmb_extension='%s' order by message_date",$flag,$voicemail_exten);
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
$messages = array();
while($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
$messages[]=$line['message_id'];
}
mysql_close();
return $messages;
}
function getMessage($msg_id){
global $db_name, $db_host,$db_user,$db_passwd;
mysql_connect($db_host, $db_user, $db_passwd)
or die('Could not connect: ' . mysql_error());
mysql_select_db($db_name) or die('Could not select database');
//make sure inputs are db safe
$msg_id = mysql_real_escape_string($msg_id);
// Performing SQL query
$query = sprintf("select * from messages where message_id=%d",$msg_id);
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
$message = array();
if($line = mysql_fetch_array($result, MYSQL_ASSOC)) {
$message['id']=$line['message_id'];
$message['date']=$line['message_date'];
$message['from']=$line['message_from'];
$message['url']=$line['message_audio_url'];
}
mysql_close();
return $message;
}
?>
Next we will define the voice application for leaving a voicemail. It has three parts, one to gather and verify the target mailbox, one to prompt the user to record their message, and one part to save the message in the database. If you wished to integrate leaving a voicemail into another application, you'd redirect the call to leave_a_message.php?exten=[the mailbox extension] rather than asking the caller to provide the mailbox.
howtos/voicemail/pick_mailbox.php<?php
include "twilio.php";
include "messages.php";
$error = false;
//if we received an extension, attempt to look it up from the voicemailbox table
if(strlen($_REQUEST['Digits'])) {
$exten = $_REQUEST['Digits'];
//if the mailbox exists, redirect the call to the leave_a_message.php
if(getMailbox($exten)) {
header("location: leave_a_message.php?exten=$exten");
exit();
} else {
$error=true;
}
}
$r = new Response();
$g = $r->append(new Gather());
if($error)
$g->append(new Say("Mailbox for extension $exten was not found"));
$g->append(new Say("Enter the extension you wish to leave a message for, followed by the #sign"));
$r->append(new Say('I did not receive an extension.'));
$r->append(new Redirect('pick_mailbox.php'));
$r->Respond();
?>
Prompt the caller to leave a message and record them.
howtos/voicemail/leave_a_message.php<?php
include "twilio.php";
include "messages.php";
if(strlen($_REQUEST['exten'])) {
$exten = $_REQUEST['exten'];
$mailbox = getMailbox($exten);
//output TwiML to record the message
$r = new Response();
$r->append(new Say('Leave a message for ' . $mailbox['desc'] . ' at the beep'));
$r->append(new Record(array('action' => "handle_message.php?exten=$exten", 'maxLength' => '120')));
//record will post to this url if it receives a message, otherwise it falls through to the next verb
$g = $r->append(new Gather());
$g->append(new Say("A message was not received, press any key to try again"));
$r->Respond();
}
?>
Save the message they left into the database
howtos/voicemail/handle_message.php<?php
include "twilio.php";
include "messages.php";
$exten = $_REQUEST['exten'];
$url = $_REQUEST['RecordingUrl'];
$caller_id = $_REQUEST['Caller'];
if(strlen($exten)&&strlen($url)) {
//save recording url and callerid as a message for that mailbox extension
addMessage($exten, $caller_id, $url);
$r = new Response();
$r->append(new Say('Thank you, good bye'));
$r->Respond();
}
?>
That's it! If you point your Twilio incoming phone number at pick_mailbox.php and call it, you will be prompted to pick a mailbox, prompted to leave a message, and the message will be saved and flagged as new.
Next lets define the php application that handles the phone call. Here we use the Twilio Response Library to facilitate TwiML creation. We no longer have to mix PHP and XML, keep the code clean and understandable. If you haven't already, visit the Response Library introduction to familiarize yourself.
howtos/voicemail/get_mailbox.php<?php
require "twilio.php";
require "messages.php";
$error=false;
if(strlen($_REQUEST['Digits'])) {
$exten = $_REQUEST['Digits'];
$mailbox = getMailbox($exten);
if($mailbox===false) {
$error=true;
}
else {
header("location: get_passcode.php?exten=$exten");
exit();
}
}
$r = new Response();
if($error)
$r->append(new Say('Mailbox not found'));
else {
$g = $r->append(new Gather());
$g->append(new Say('Enter your mailbox extension and press the pound key'));
}
$r->Respond();
?>
Once we have the mailbox, ask for the passcode. Look up the passcode for the mailbox and compare it to what the caller entered. If they match, move on the message_menu.php
howtos/voicemail/get_passcode.php<?php
include "twilio.php";
include "messages.php";
if(strlen($_REQUEST['exten'])){
$exten=$_REQUEST['exten'];
} else {
die("<Response><Say>An error occured in the voicemail system. no mailbox exten</Say></Response>");
}
if(strlen($_REQUEST['Digits'])){
$code = $_REQUEST['Digits'];
$mailbox = getMailbox($exten);
if($mailbox['passcode']!=$code){
$error=true;
} else {
header("location: message_menu.php?exten=$exten");
exit();
}
}
$r = new Response();
if($error)
$r->append(new Say('Passcode incorrect, please try again'));
else {
$g = $r->append(new Gather());
$g->append(new Say("Enter the passcode for mailbox $exten"));
}
$r->Respond();
?>
Listen.php is passed an array of message ids and a starting point. As a caller listens to messages, he can interrupt the message with instructions like skip, save, or delete. If they hit 2 or 3, the code updates the message record, marking it saved or deleted. Each time the Gather is submitted or the Redirect is hit, the current message is incremented so you hear the next message.
howtos/voicemail/listen.php<?php
include "twilio.php";
include "messages.php";
$first=true;
if(strlen($_REQUEST['exten'])){
$exten=$_REQUEST['exten'];
} else {
die("<Response><Say>An error occured in the voicemail system. no mailbox exten</Say></Response>");
}
$messages = array();
$total_messages = 0;
if(strlen($_REQUEST['messages'])) {
$msg_list = $_REQUEST['messages'];
$messages=split(",",$_REQUEST['messages']);
$total_messages=count($messages);
}
$current_msg=0;
if(strlen($_REQUEST['current_msg'])) {
$current_msg=$_REQUEST['current_msg'];
$first=false;
}
if(strlen($_REQUEST['Digits'])) {
$digits = $_REQUEST['Digits'];
if($digits==1){
//skip
} else if ($digits==2){
//save
updateMessageFlag($messages[$current_msg],1);
} else if ($digits==3){
//delete
updateMessageFlag($messages[$current_msg],2);
}
if($current_msg+1<$total_messages) {
$current_msg++;
} else {
$r = new Response();
$r->append(new Say("There are no more messages"));
$r->append(new Say("Main Menu"));
$r->append(new Redirect("get_mailbox.php"));
$r->Respond();
exit();
}
}
$flag = 0;
if(strlen($_REQUEST['flag'])){
$flag = $_REQUEST['flag'];
}
$msg = getMessage($messages[$current_msg]);
$url = $msg['url'];
$from = $msg['from'];
$date = $msg['date'];
$msg_list = urlencode($msg_list);
$r = new Response();
$g = $r->append(new Gather(array("action" => "listen.php?exten=$exten&messages=$msg_list¤t_msg=$current_msg", "numDigits" => "1", "timeout" => "5")));
if($first) {
$g->append(new Say("Press 1 to skip, 2 to save, 3 to delete"));
} else {
$g->append(new Say("Next Message"));
}
$g->append(new Say("Message from $from received on " . date('l jS \of F Y h:i A',strtotime($date))));
$g->append(new Play($url));
$r->append(new Redirect("listen.php?exten=$exten&messages=$msg_list¤t_msg=$current_msg&Digits=1"));
$r->Respond();
exit();
?>
There are many, many ways to extend and improve on this example. You could add a hook to the handle_message.php file, having it email the audio URL when a message is saved. You could build a web interface using the data access functions in messages.php which would let you check and manage your voicemail in a web browser rather than over the phone. You can add custom greetings and audio prompts using your own recordings and the Play verb. Experiment!