Twilio Contactable
Twilio makes voice and SMS interactions easy. But if you want to ensure that your user's phone numbers really belong to them there's a lot of buggy code you'll have to add in your Ruby app. Unless you use this gem.
Don't Write Twilio Ruby Code Like It's PHP
You don't want to be passing strings around between multiple web requests and writing procedural code in your Ruby app. Not for such a simple feature. This gem lets you ask for a phone number from your users (or any ActiveRecord-like Ruby class), confirm their ownership of it via SMS or Voice (or both), and automatically handle number invalidation.
Install It
Install TwilioContactable as a gem:
$ gem install twilio_contactable
# For Rails add it in environment.rb:
config.gem 'twilio_contactable'
# then edit your .gems file or run
$ rake gems:unpack
# to unpack the gem into your vendor/gems directory
Or as a Plugin:
ruby script/plugin install git://github.com/JackDanger/twilio_contactable.git
Connect This Code To Your App
Include Twilio::Contactable into your User class or whatever you're using to represent an entity with a contactable phone number.
class User < ActiveRecord::Base
include TwilioContactable::Contactable
twilio_contactable
end
If you're using custom column names you can easily overwrite any of them by passing in a configuration block:
class User < ActiveRecord::Base
include TwilioContactable::Contactable
twilio_contactable do |config|
config.phone_number_column = :mobile_number
config.formatted_phone_number_column = :formatted_mobile_number
config.sms_blocked_column = :should_we_not_txt_this_user
config.sms_confirmation_code_column = :the_sms_confirmation_code
config.sms_confirmation_attempted_column = :when_was_the_sms_confirmation_attempted
config.sms_confirmed_phone_number_column = :the_mobile_number_thats_been_confirmed_for_sms
config.voice_blocked_column = :should_we_not_call_this_user
config.voice_confirmation_code_column = :the_voice_confirmation_code
config.voice_confirmation_attempted_column = :when_was_the_voice_confirmation_attempted
config.voice_confirmed_phone_number_column = :the_mobile_number_thats_been_confirmed_for_voice
# Defaults to the name on the left (minus the '_column' at the end)
# e.g., the sms_blocked_column is 'sms_blocked'
#
# You don't need all those columns, omit any that you're sure you won't want.
end
end
You'll need to add those columns to your database table using a migration that looks something like this:
change_table :users do |t|
t.string :phone_number
t.string :formatted_phone_number
t.boolean :sms_blocked, :default => false, :null => false
t.string :sms_confirmation_code
t.datetime :sms_confirmation_attempted
t.string :sms_confirmed_phone_number
t.boolean :voice_blocked, :default => false, :null => false
t.string :voice_confirmation_code
t.datetime :voice_confirmation_attempted
t.string :voice_confirmed_phone_number
end
You don't necessarily need all those columns though. Say you have users that want to use SMS and business locations that just need to have their retail phone number to confirm their identity:
change_table :users do |t|
t.string :phone_number
t.string :formatted_phone_number
t.boolean :sms_blocked, :default => false, :null => false
t.string :sms_confirmation_code
t.datetime :sms_confirmation_attempted
t.string :sms_confirmed_phone_number
end
change_table :business_locations do |t|
t.string :phone_number
t.string :formatted_phone_number
t.boolean :voice_blocked, :default => false, :null => false
t.string :voice_confirmation_code
t.datetime :voice_confirmation_attempted
t.string :voice_confirmed_phone_number
end
Both the User and the BusinessLocation models are now prepared for SMS and Voice confirmation, respectively.
You'll also need to create a controller that is capable of receiving connections from Twilio.com. You can reuse an existing controller or create a new one just for this task. TwilioContactable will guess the path from the controller name, so if you include it into a class called CheckPhoneNumbersController:
# app/controllers/check_phone_numbers_controller.rb
class CheckPhoneNumbersController < ActionController::Base
include TwilioContactable::Controller
# any models that you want to have phone numbers confirmed for:
twilio_contactable User, BusinessLocation
end
then Twilio.com would try to contact your site at '/check_phone_numbers'. As long as that route works then you don't need to configure anything else.
Configure It With Your Twilio Account Info
Because it can be expensive to send TXTs or make calls accidentally, it's required that you manually set TwilioContactable.mode in your app. Put this line in config/environments/production.rb or anything that loads only in your production environment:
TwilioContactable.mode = :live
Skipping this step (or adding any other value) will prevent TXTs or phone calls from actually being sent.
You'll need to add a few pieces of important information. Create a file like the following in config/initializers/
# config/initializers/twilio_contactable.rb
TwilioContactable.configure do |config|
# Your Twilio Account Number
config.client_id = 12345
# Your Twilio Account Secret Code
config.client_key = 'ABC123'
# Twilio.com needs to be able to find your site. Add your
# complete Ruby app internet address here:
config.website_address = 'http://myrubyapp.com'
# And, finally, the Twilio-hosted phone number
# that you'd like all your calls/txts to come from:
config.default_from_phone_number = '(206) 555-1234'
end
Confirming Phone Number For SMS And Sending TXTs
When your users first hand you their number it will be unconfirmed:
@user = User.create(:phone_number => '555-222-3333')
@user.send_sms_confirmation! # fires off a TXT to the user with a generated confirmation code
@user.sms_confirmed? # => false, because we've only started the process
The user will read the SMS confirmation code off of their phone and type it into a form on your site (you'll need to build this). When they submit that code to a controller you should pass it in to the user record's sms_confirm_with method:
# params[:code] => '123XYZ'
@user.sms_confirm_with(params[:code])
If the code is correct then the user's current phone number will be automatically marked as confirmed. You can check this at any time with:
@user.sms_confirmed? # => true
@user.send_sms!("Hi! This is a text message.")
If the code is wrong then the user's current phone number will stay unconfirmed.
@user.sms_confirmed? # => false
@user.send_sms!("Hi! This is a text message.") # sends nothing
Confirming Phone Number For Voice
Confirming for Voice is different from confirming for SMS because the user will read the code off your site and enter their Voice confirmation code into the keypad of their phone.
@user = User.create(:phone_number => '555-222-3333')
@user.send_voice_confirmation! # Initiates phone call to user
@user.voice_confirmed? # false
Right after send_voice_confirmation! is called you'll want to display the confirmation code to the user. It's up to you how to do this but you'll probably want to have a screen that shows something like this:
<h1>We're calling you on the phone right now!</h1>
<p>
When you answer the phone, please type in these numbers:
<%= @user.voice_confirmation_code %>
<p>
<%= link_to "Okay, I've finished the phone call", '/' %>
While you display this screen the user will have inputted their voice_confirmation_code to their phone, Twilio.com will have posted that code to your server (defined in config.website_address), and your user will have been updated so that @user.voice_confirmed? is now true! If the code is entered incorrectly then the user's current phone number will stay unconfirmed. You'll need to start over and have them enter the code again.
Receiving TXTs and Voice calls
You can also receive data posted to you from Twilio. This is how you'll receive messages, txts and notices that users have been blocked. All you need is to create a bare controller and include TwilioContactable::Controller into it. Then specify which Ruby class you're using as a contactable user model (likely User)
class TwilioContactableController < ApplicationController
include TwilioContactable::Controller
twilio_contactable User # or whichever class you included TwilioContactable::Contactable into
end
Make sure Twilio.com knows to POST all SMS messages and block notices to you at:
http://myrubyapp.com/twilio_contactable/receive_sms_message
This gem will handle all those incoming messages automatically. Now if your users reply to an SMS with 'STOP' or 'BLOCK' the appropriate record in your database will be updated so that sms messages no longer can be sent to them (i.e.: @user.sms_blocked? will be true)
All other incoming TXTs (besides 'BLOCK' and 'STOP') from a user will automatically be sent to that user's record. If "I love you!" is sent to you from a user with the phone number "555-111-9999" then the following will be executed:
User.find_by_formatted_phone_number('+15551119999').receive_sms("I love you!")
It's up to you to implement the 'receive_sms' method on User.
That's it. Patches welcome, forks celebrated.
Copyright (c) 2010 Jack Danger Canty, released under the MIT license