Monday, January 24, 2011

How-To: SMS Q+A with Twilio, App Engine, and CherryPy

When learning a new API or programming language, I often get a bit nostalgic for the first programs I wrote in BASIC. Those programs looked a lot like this:

10 INPUT "What is your name? "; U$
20 PRINT"Hello, ", U$


To illustrate the simplicity of Twilio, Google's App Engine and CherryPy, I'll take this simple program and show you how to build it for an SMS interface.

Twilio allows you to automate placing and receiving phone calls, and placing and receiving SMS-messages (or TXTs). For this tutorial, we'll focus on placing and receiving SMS-messages. App Engine is a way to inexpensively (free to start) run web applications in Google's cloud. CherryPy is an extremely simple HTTP server for Python. CherryPy has nominal configuration and reminds me a *lot* of the simplicity of the original Java Servlet specification.

Before we get started, you need to signup for Twilio and Google App Engine accounts. Both are free and easy to setup. Once you've done that, swing on back, and we'll walk through the rest.

The Spec

  • A user sends an SMS to your sample Twilio account phone number with the text "?".
  • Twilio sends that request to your new Google App Engine App.
  • Google App Engine dispatches the request to your CherryPy app.
  • CherryPy reads the body of the request.
  • If the request field Body is "?", CherryPy responds with "What is your name?" 
  • If the request field Body is not "?", CherryPy responds with "Hello, [Body]."
  • The user that sent the original SMS message receives the response on their phone.

The Development Process

First, we're going to get a simple app running in CherryPy on your local machine. Then, we'll get it running on  App Engine locally, and in the cloud. Finally, we'll connect Twilio to App Engine and you can watch it all in action.


Get your app running in CherryPy

First, create a directory for your app:
$ mkdir twilio_qa

Download cherrypy

Unzip cherrypy in the twilio_qadirectory.

Then, use your favorite editor to create twilio_qa.py.

#!/usr/bin/env python
import cherrypy

class TwilioQA:
  # the main entry point, this is the method that will get called
  # when someone hits your server.
  def index(self, Body="?", **kwargs):
    if (Body == "?"):
      return self.instructions()
    else:
      return self.wrap("Hello, %s.\r\nShall we play a game?" %Body)

  # print out the instructions
  def instructions(self):
    s = "What is your name?"
    return self.wrap(s)
  
  # helper function to wrap response with twilio SMS Response XML
  def wrap(self, response):
    s = """<?xml version="1.0" encoding="UTF-8" ?>   
<Response>
  <Sms>%s</Sms> 
</Response>
""" % (self.trim(response))
    return s

  MAX_SMS_LENGTH = 160

  # remove all chars after 160, otherwise Twiilo will reject the
  # response.
  def trim(self, s):
    return s[0:self.MAX_SMS_LENGTH]

  # tell cherrypy which methods it can call.
  index.exposed = True

# setup cherrypy
app = cherrypy.tree.mount(TwilioQA(), "/")  
cherrypy.quickstart(app)
Make twilio_qa.py executable and run it.

$ chmod +x twilio_qa.py
$ ./twilio_qa.py

Test your app

With your app running, open a browser and go to http://localhost:8080/.

The page should return the instructions:

What is your name?
Add the parameter for the body to see how it works with a response  http://localhost:8080/?Body=Pete. # Note case for form fields matters, cherrypy automatically maps query paramaters to variables in your index method.

You should see the response:

Hello, Pete. Shall we play a game?
Viewing the source will show you the XML that you will be sending to Twilio in just a little bit.

The cherrypy setup is really simple. You import cherrypy, mount the default URL "/" to your TwilioQA() class, and run the server with the quickstart() method.

Run your app in App Engine

Download and install the App Engine SDK for Python.
Create an app.yml in your twilio_qa directory.
application: twilio-qa-CHANGE-ME
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: twilio_qa.py

In twilio_qa.py, import wsgiref.handlers to work with appengine:

...
import cherrypy
import wsgiref.handlers
...

In twilio_qa.py, comment out quickstart and add the App Engine handler:
... 
app = cherrypy.tree.mount(root, "/")
# cherrypy.quickstart(app)
wsgiref.handlers.CGIHandler().run(app)

Run your app in app engine's localserver:

$ dev_appserver.py --port=8080 ../twilio_qa/

Follow the steps in Test your app and scroll back here when you're done.

Deploy on App Engine

Now that you've tested locally, you can deploy to the cloud in a few easy steps.


Create a new application at appengine.google.com:
  1. Click [Create Application].
  2. Choose an [application identifier] (you'll use this for the application field in app.yml later). This needs to be unique across the entire appspot.com domain. Good luck :-)
  3. Create a title.
  4. Update your app.yml, set your application identifier as the value in application.
  5. Deploy your app by typing:
    $ appcfy.py update ../twilio_qa/
  6. Go test your app, instead of localhost, substitute http://[your-application-id].appspot.com/ for http://localhost:8080

For more on appengine, definitely check out their great knowledge base for getting started in Python.

Connect to Twilio

Now that you have your twilio trial account and your app ready to be connected, you just need to connect the 2.

In the Developer Tools section of your account, set you SMS URL to:

http://[your-application-id].appspot.com/

Click [Save].

Send a TXT to the Sandbox Phone Number you configured with the Body of "[PIN] ?".

You'll need to put the [PIN] in for the next question as well.


The end

Go forth and create inexpensive Question and Answer apps using SMS, Twilio and App Engine.

If you have any questions, issues, or create something awesome with this tutorial, please drop me an a comment.

6 comments:

  1. Hi Pete, Thanks for this tutorial. I'm getting this error:


    WARNING 2012-02-11 20:20:29,721 rdbms_mysqldb.py:94] The rdbms API is not available because the MySQLdb library could not be loaded.
    ERROR 2012-02-11 20:20:29,783 dev_appserver_main.py:581] Fatal error when loading application configuration:
    Unable to assign value 'Paris Jones Music' to attribute 'application':
    Value 'Paris Jones Music' for application does not match expression '^(?:[a-z\d\-]{1,100}\~)?(?:(?!\-)[a-z\d\-\.]{1,100}:)?(?!-)[a-z\d\-]{1,100}$'
    in "../twilio_qa/app.yml", line 1, column 14

    Any Help would be appreciated.

    Thanks!

    ReplyDelete
    Replies
    1. I don't believe you can have spaces in the name of your application. I would recommend just adding dashes. My regex reading isn't great, but I think it needs to be all lower case, too:

      paris-jones-music

      or something similar.

      good luck.

      Delete
  2. How would I build it so that several responses at the end are emailed as a form to email?

    ReplyDelete
    Replies
    1. Hi Yev, sorry for the delay in responding, but your question is beyond the scope of this blogpost. I've never tried to send email from google app engine. best of luck.
      Pete

      Delete
  3. Nice Post! Thank you so much for sharing this one really Good topic Professional SEO all Kind of peaceful info,Love it- Twilio API Integration

    ReplyDelete
  4. A phone call is a phone bring in which somebody converses with a few individuals in the meantime. The phone calls might be intended to permit the called gathering to partake amid the call, or the ring might be set so that the called party simply listens into the call and can't talk. It is some of the time called ATC (sound video chat).
    Conference Calling Plugins

    ReplyDelete