Sat, 03 Jul 10

Twilio on Rails

A few weeks ago, I looked up Twilio in conjunction with a telephony project. I was intrigued by the promise of a simple REST web API for building SMS and voice based interactions within web apps. In prototyping, I ended up developing a Rails plugin called Twilioflow which allows simplified development and testing of telephony apps.

Having spent some time at AT&T/Lucent Bell Labs, I had heard my share of war stories from the programmers and managers who worked on the venerable Audix system. From that perspective, Twilio is yet another example of creative destruction made possible by improvements in the general purpose computing platform coupled with an extensible network protocol (HTTP). Operationally, Twilio goes even further by harnessing Amazon’s EC2 infrastructure and we can be sure they have numerous other tricks up their sleeve.

In short, for web apps, the Twilio server acts like a well-behaved browser client, supporting cookies and redirects, and providing call control parameters mimicking form submissions. I started prototyping based off their examples, but soon ran into style as well as design issues. Its fine to have Ruby/Rails examples which follow the PHP form, but special purpose controller methods containing repetitive if-then-else code redirecting to other special purpose controller methods is quite far from being DRY or RESTful or testable when it comes to building a decent Rails app. In developing an alternative approach, I applied these guiding principles:

The phone tree is really a graph and can be represented as a FSM. I looked around and found the excellent Workflow library, which provides a great and intuitive way for specifying state machines and event transitions. Using additional IVR specifiers the phone tree can be contained within the model class allowing it to be unit-tested independently. Here is an excerpt of on particular Interaction model, inspired by Twilio’s Hello Monkey examples.

class Interaction < ActiveRecord::Base
  include Workflow   
  workflow do
     state :hello do
       say "Gorillas. Wasssaaap!"
       prompt 1, "If you are a jungle-dweller, press 1", :primal, :go => :jungle_dweller
       prompt 2, "Captured primates, please press 2", :captured, :go => :zoo_habitant
       prompt 9, "To end this call, press 9", :end_call, :go => :goodbye
    end
    #.... Other states and model methods_ ...
  end
end

There are many more aspects about the IVR DSL outlined in a detailed example, such as dynamic key press options, multiple digit handling, response decisions and more. The goal is to make it easy to build real-world personalized phone menus integrated with databases or other web services. The controller response is an encoding of the current interaction state. So, the twilio request handler can provide a simplified generic response such as this one:

keys = @interaction.accepts
numDigits = (keys and not keys['#']) ? "1" : ""
xml.instruct!
xml.Response do
  @interaction.says.each do |sentence|
    xml.Say(sentence, :voice => 'woman', :language => current_locale.to_s)
  end
  if not keys.blank?
    xml.Gather :action => action, :method => "POST",  :numDigits => numDigits do
    end
  end
end

Finally, it is possible to package all this functionality as a Rails plugin (or a gem). Adding a Twilio interaction to a web app can then be jump-started as follows:

$ ruby script/generate twilio interaction

In addition to stand-alone unit testing of your models, you can also easily test the interactions in your development environment from within the browser. Functional testing with rake is also enabled. Twilioflow is available on github for your use and improvements.

Sat, 03 Jul 10