How Flxt.it Uses Twilio To Make Sure You Never Miss A Good Movie

Ever been out at dinner and had someone mention a movie you have to watch (Like, Gattaca.), and then went home and promptly forgot the name? Flxt.it wants to make sure a good movie never goes unseen. Simply text the name of the movie to Flxt.it’s phone number and it’ll be added to your Netflix queue. This is a guest post by Flxt.it’s creator, John Clayton, on how he used Twilio to develop Flxt.it.

170144_499191063185_829363185_6042328_6760532_oA few weeks ago a challenge came across my Twitter stream – build an application that combines Twilio, a cloud communication platform, with AppHarbor, the easiest way to deploy and host .NET applications. This seemed like the perfect fit for an idea that’d been bouncing around in my head for some time. Five days later,  long after the Super Bowl ended, I submitted my entry.

The other morning another tweet popped up on my desktop…my entry won!

And now…I’m proud to introduce Flxt.it — a simple tool to manage your Netflix queue.

The Idea

How many times have you been away from a computer and thought of a movie you don’t want to miss? This is where Flxt.it comes in — if you can send a text message, you can add movies to your Netflix queue. There are existing applications to accomplish this, but sending a text message is faster, and you don’t need a smart phone.

The Implementation

When you sign up for Flxt.it, your Netflix account is linked to your cell phone, and you’re given a phone number to send movie titles to. This number is provided by Twilio and it’s where the magic begins.

When Twilio receives a text message, it sends the information on to the web application, which is running ASP.NET MVC 3. In Flxt.it there is a single endpoint that receives the message, looks up your Netflix account information, takes care of business, and responds with a text message to let you know the movie was added to your queue.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Message
{
public string AccountSid { get ; set ; }
public string Body { get ; set ; }
public string From { get ; set ; }
public string FromCity { get ; set ; }
public string FromCountry { get ; set ; }
public string FromState { get ; set ; }
public string FromZip { get ; set ; }
public string SmsSid { get ; set ; }
public string To { get ; set ; }
public string ToCity { get ; set ; }
public string ToCountry { get ; set ; }
public string ToState { get ; set ; }
public string ToZip { get ; set ; }
}
[ValidateTwilioSignature( "YourTwilioAuthToken" )]
public class SmsController : TwilioController
{
[HttpPost]
public ActionResult Search(Message message)
{
var user = db.Single<User>(u => u.PhoneHash == message.From.NormalizeAndHash());
var movie = netflix.Search(message.Body, user.NetflixApiToken);
if (netflix.AddToQueue(movie, user.NetflixApiToken))
return Sms( "{0} has been added to your queue!" , movie.Title);
return Sms( "Unable to add to your queue." );
}
protected override void OnException(ExceptionContext filterContext)
{
Log.Write(filterContext.Exception);
filterContext.Result = Sms( "Whoops! Something went wrong. Please try again later." );
filterContext.ExceptionHandled = true ;
}
}

MVC’s model binding turns all the parameters Twilio sends into an easy to use class. To respond to the incoming message there are two options — TwiML or REST. The former is the only option if you want to do more than one action, like send multiple SMS messages and redirect to another TwiML document. The later is the easier option when you just want to send a response — set your response’s content type to text/plain and Twilio will send the response body to the sender as an SMS message!

This was the approach used by Flxt.it. A custom SmsResult sets the content type and makes sure that the message doesn’t exceed 160 characters. If you don’t want to send a reply message simply send an empty response.

It’s also important to handle any errors properly so that Twilio always gets a valid response from your application. MVC makes this crazy easy without needing to wrap your entire action in a try/catch. By either overloading the OnException method or providing a custom filter to the action or controller you can return an SmsResult no matter what happens during the action.

Security & Privacy

We are all concerned about privacy, so handling phone numbers should not be taken lightly. Even in a simple application like this I want to take every precaution to keep users’ phone numbers safe.

The first, and most effective, precaution is Flxt.it does not store the full phone numbers. Because Flxt.it will always be responding to incoming messages, it will store a hash of the phone number and the last four digits and still be able to link your Netflix account to your incoming movie request.

The second way to protect the Twilio endpoint is to ensure that Flxt.it will respond to requests from Twilio and nobody else. In every request, Twilio will sign the request using your secret authentication token and send it in a HTTP header. To validate it you perform the same operation and if the request is valid you will get the exact same value.

The following is an example of how you can create an action filter in MVC to easily validate an action or every action in a controller. It’s based on John Sheehan’s most excellent presentation at mvcConf last week.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)]
public class ValidateTwilioSignatureAttribute : ActionFilterAttribute
{
public ValidateTwilioSignatureAttribute( string authToken)
{
authToken.ThrowIfNullOrEmpty( "authToken" );
AuthToken = authToken;
}
public string AuthToken { get ; private set ; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.HttpContext.Request;
var fullUrl = String.Format( "<a href="http:///">http://</a>{0}{1}" , request.Url.Host, request.Url.PathAndQuery);
var valueToHash = new StringBuilder(fullUrl);
if (request.HttpMethod == "POST" )
{
var sortedParams = request.Form.AllKeys.OrderBy(p => p).ToList();
foreach (var param in sortedParams)
{
valueToHash.Append(param);
valueToHash.Append(request.Form[param]);
}
}
var sha1 = new HMACSHA1(Encoding.UTF8.GetBytes(AuthToken));
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(valueToHash.ToString()));
var encodedHash = Convert.ToBase64String(hash);
var twilioSignature = request.Headers[ "X-Twilio-Signature" ];
if (encodedHash != twilioSignature)
filterContext.Result = new HttpForbiddenResult();
}
}

One thing to note here is that when you are in a load-balanced environment like AppHarbor the web server your request is being served by is likely not the exact address that Twilio calls from the outside. Since it is part of the signature we need to be sure to use the host header value and not the raw URL as seen by ASP.NET.

Couldn’t Be Easier

In Flxt.it I’m barely scratching the surface of Twilio’s capabilities. In addition to sending and receiving SMS messages, they have an incredibly powerful platform for making and receiving phone calls. Best of all it’s available on a pay as you go basis — no contracts! You no longer need to be a Twitter-sized company to be able to add phone and SMS capabilities to your application.

Go ahead and try it out for yourself — you get $30 worth of credits just for signing up!