Building a simple voicemail system with Twilio and ASP.NET MVC, Part 2
In Part 1, I demonstrated getting started with Twilio and ASP.NET MVC. In this post, I’ll go over the remaining controller action implementations.
So far we’ve got up to the point where we play the current greeting for a caller. Coaches and umpires use the hotline to report any issues the League Director needs to be made aware of. In Part 1 we handle generating the response needed to play the greeting and then beep to let the caller know they can leave a message. When a message is left by a caller, a POST request is made to our /RecordVoicemail action:
public ActionResult RecordVoicemail(string CallGuid, string RecordingUrl, string Caller) {
try {
var msg = new MailMessage();
msg.To.Add(Settings.VoicemailEmailToAddress);
msg.From = new MailAddress(Settings.VoicemailEmailFromAddress);
msg.Subject = "New voicemail received from " + Caller;
var client = new WebClient();
string filename = string.Format("{0}-{1}.wav", Caller, DateTime.Now.ToString("MMddyyyhhmmss"));
msg.Attachments.Add(new Attachment(client.OpenRead(RecordingUrl), filename));
msg.Body = "Received " + DateTime.Now;
var smtp = new SmtpClient();
smtp.Send(msg);
}
catch (SmtpException ex) {
Log(CallGuid, "Could not send voicemail notification email: " + ex.Message);
}
return new EmptyResult();
}
This method downloads the message from Twilio’s servers, attaches it to an email and sends it off to the Director. He can listen to these messages on his BlackBerry and take care of any issues reported.
If there’s a problem we log the issue to a file. The other methods have logging as well, but I’ve excluded them from my examples for brevity.
Lastly, the League Director needs to be able to record a new greeting from his phone in case he’s out at the fields and the weather forces a cancelation. While the greeting is being played, the Director can enter the PIN followed by # to record a new greeting. Once the PIN has been entered, we send the required Twilio response to prompt for the recording:
public ActionResult Greeting(string CallGuid, string Digits) {
var doc = new XDocument();
var response = new XElement("Response");
if (Digits != Settings.PIN) {
response.Add(Verb("Say", "Invalid pin number. Please try again."));
response.Add(Verb("Gather", "",
new { action = ActionUrl.Greeting,
method = "POST",
finishOnKey = "#"
}));
}
else {
response.Add(Verb("Say", "Record your greeting after the tone. Hang up to save the greeting or press a key to start over."));
response.Add(Verb("Record", "",
new { maxLength = 120,
action = ActionUrl.RecordGreeting,
method = "POST"
}));
}
doc.Add(response);
return new XmlResult(doc);
}
When the PIN has been entered, a POST request is sent to this method. We use parameter binding again to get the digits entered. These are checked against the settings file. If the PIN is invalid, we notify the caller and ask for the PIN again. This will loop until the right PIN is entered (or the caller hangs up).
Once the correct PIN is entered, we return some short instructions and set up the Record verb. Once the new greeting is recorded, a POST is sent to our RecordGreeting controller action:
public ActionResult RecordGreeting(string CallGuid, string RecordingUrl, string Digits) {
if (Digits != "hangup") {
var doc = new XDocument();
var response = new XElement("Response");
response.Add(Verb("Say", "Record your greeting after the tone. Hang up to save the greeting or press a key to start over."));
response.Add(Verb("Record", "",
new { maxLength = 120,
action = ActionUrl.RecordGreeting,
method = "POST"
}));
doc.Add(response);
return new XmlResult(doc);
}
Settings.GreetingUrl = RecordingUrl;
return new EmptyResult();
}
Because it can sometimes take a few tries to record the greeting properly, pressing a number during recording will restart the recording. Once the Director is satisfied with the updated greeting, he hangs up and the new greeting URL is saved to the settings.
My first implementation of the recording restart was to redirect back to /Greeting and including the correct PIN as a query string parameter which was bound to the Digits method parameter. While this worked, I didn’t like passing the PIN in a request so I just rebuild the response XML and return it instead.
That’s all there is too it. Twilio offers a lot more options including a complete REST API for accessing recordings, provisioning phone numbers, etc. So far I’m very impressed with the service and didn’t run into any major hangups (ba-dum-dum).




