Hello, This is Your Rails App Calling You

Editor’s note: we absolutely love open source and the free spirit of contribution. We’ve simplified our latest version based on some great suggestions from Jonas Nicklas. Thanks!

Telephony applications can be developed easier than ever before with Twilio, a cloud-based API for voice services. During the first half of this year, we’ve been playing, building, and now wrapping in the form of a Ruby gem we’ve finally polished off. I’d like to take you through an example to show off their amazing service offering.

Twilio: How it Works

Twilio’s REST API enables your application to make calls, playback audio, transcribe calls, and much more. With most operations such as outgoing and incoming calls, Twilio communicates with your application by pinging a URL that you designate. Your application then needs to respond according to the Twilio Markup XML (TwiML) specification, which is comprised of Twilio “verbs” such as dial, gather, play, record, and say. These verbs instruct Twilio what to do during the call.

Example Use Case: Phone-Based Identity Verification

In two recent customer Web applications, we were required to add functionality to be able to verify the ownership of business phone numbers. The idea was simple: during the user registration process, the system phones the listed business number, user answers phone and hears a 4-digit PIN number, and then is required to enter this code in the Web form to continue with registration. With Twilio’s service and our Twilio Gem, this is ridiculously easy, especially within a Ruby on Rails application.

Inside your Rails user registration controller, you’ll need to generate the 4-digit random code and stick it inside a session object:

@random_code = rand.to_s[2..5]
session[:random_code] = @random_code

Now, set your Twilio credentials by calling the connect method. Because these get cached in the Twilio module, you don’t have to deal with connection instances.

Twilio.connect(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)

Finally, tell your app to call Twilio’s service. We’ve wrapped all of the Twilio REST resources into Ruby classes, each with their own methods that wrap the entire interface. In this example, we’re set the “from” number to a constant representing the phone number associated with our Twilio account. However, Twilio allows you to use other registered Twilio numbers, thus this value can be dynamic. The last parameter is where it gets interesting and represents the callback URL that Twilio should contact to communicate with your application.

Twilio::Call.make(CALLER_ID, user.phone, 
  verify_phone_url(:pin => @random_code))

The above command instructs Twilio to phone the user’s number, showing your registered number as the caller id. As soon as Twilio connects to the number, it will also ping your URL.

Now we need to handle the response. In our example, we need to “say” the PIN code to the person on the phone. Before we created our gem, we would use XML Builder to render a response inside a .builder template. But the gem simplifies things greatly. Inside another controller action, just add:

random_code = params[:pin].gsub(/\d/) {|s| s + ' '}
render :xml => Twilio::Verb.say("Your pin code is #{random_code}", 
  :loop => 3, 
  :pause => true)

Remember that the callback URL we initially sent to Twilio included a ‘pin’ parameter, so Twilio sends this back to us. The reason we add spaces between the digits is that Twilio speech module will speak “1234″ as “one thousand two hundred thirty four” while “1 2 3 4″ will be spoken as “one two three four.” The loop and pause options result in a better experience for the person on the phone. Other options include the ability to set the voice to female, as well as use other languages.

The last thing remaining is to handle the user’s response. At this point, they hear the PIN code over the phone and enter them into the Web registration form. All that you need to do is confirm that their input matches the random_code session value.

Testing Strategies

With functional testing, you can resort to mock interfaces to guide you along. If you want to emulate the XML response handlers in your controllers, then take a look at the FakeWeb gem, which makes things very easy. In fact, most of Twilio gem’s tests leverage it.

But at some point, you’ll want to play around with real network connectivity. What I recommend is creating an SSH tunnel between a server you may be operating and your own development machine. This is painless if you manage with a rake task. First, create tunnel.yml and place in your Rails application’s config folder.

development:
  public_host_username: sugar
  public_host: staging.webficient.com
  public_port: 8868
  local_port: 3000

Now, save this rake task as lib/tasks/tunnel.rake:

namespace :tunnel do
 
  desc "Start SSH tunnel to remote host"
  task :start => :environment do
    SSH_TUNNEL = YAML.load_file("#{RAILS_ROOT}/config/tunnel.yml")[RAILS_ENV]
 
    public_host_username = SSH_TUNNEL['public_host_username']
    public_host          = SSH_TUNNEL['public_host']
    public_port          = SSH_TUNNEL['public_port']
    local_port           = SSH_TUNNEL['local_port']
 
    puts "Starting tunnel #{public_host}:#{public_port} to 0.0.0.0:#{local_port}"
    system "ssh -nNT -g -R *:#{public_port}:0.0.0.0:#{local_port} #{public_host_username}@#{public_host}"
  end
 
end

Then, it’s as easy as typing rake tunnel:start in your command line. If it doesn’t work, be sure your server has the following values set in the sshd_config file:

AllowTcpForwarding yes
GatewayPorts clientspecified

Once the tunnel is running, point your browser to the public URL, and you’ll be actually browsing your local machine. This setup is very handy when debugging Twilio interactions.

Endless Possibilities

Kyle, one of our lead engineers, created an app that calls you when a server is down. In another project, we implemented click-to-call functionality, in which a user can enter their phone number and the application will connect them to the business they are calling. You may have seen this in action on sites like YellowPages.com — same idea and wonderfully easy with Twilio. In the near future, I’ll share a few more technical examples and other capabilities of the Twilio gem.


About this entry