Voicemail-via-email with Twilio and Heroku in 30 minutes.
I recently decided to give my development company Refinry a toll-free number. Really classes the joint up. I decided to use Twilio, since their API is so impressive (and since Google Voice doesn't exist in Canada). And since Twilio makes it just so easy, I went ahead and set up voicemail-to-email, which can be done at no extra charge.
Twilio has got to be one of the most disruptive services out there right now. I remember not long ago when Asterisk was considered the way to go for telephony, and the configuration nightmare that ensued. This is night and day -- for $1/month, plus $0.01 per minute ($2/$0.03 for toll-free) I can develop a totally customized service, incredibly easily. And if you're here, for the rest of the post I'm going to assume that you want to.
The following is a wee twilio endpoint that will do the following
- Try to forward the call to my cell phone
- If I don't answer within 10 seconds, prompt the user to record a message
- Email the message, as an attachment, to my own email address.
You will need
- A Twilio account
- A Heroku account
- Heroku configured on your machine
- Ruby and Bundler installed on your machine.
Creating the App
First, make a directory for your project, and create a Gemfile for bundler:
1 2 3 4 5 6 |
#### Gemfile
source "https://rubygems.org"
gem "sinatra"
gem "twilio-ruby"
gem "pony"
gem "thin"
|
Sinatra is the web framework we'll be using, twilio-ruby is Twilio's ruby library, Pony is a nice email wrapper, and Thin is a snappier webserver than the default WEBrick. Run "bundle install" to install those packages.
Then, create your app.rb file:
1 2 3 4 5 6 7 8 9 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 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
require 'rubygems'
require 'twilio-ruby'
require 'sinatra'
require 'pony'
require 'open-uri'
# Config
MY_NUMBER = "+19995551234"
TO_EMAIL = "voicemail@adambard.com"
SMTP_HOST = "<SMTP hostname here>"
SMTP_USER = "<your username here>"
SMTP_PASSWORD = "<your password here>"
# Send a message with an mp3 voicemail attachment
def send_message(subject, message, file_url)
Pony.mail({
:to => TO_EMAIL
:body => message,
:via => :smtp,
:via_options => {
:address => SMTP_HOST,
:user_name => SMTP_USER,
:password => SMTP_PASSWORD,
:port => SMTP_PORT,
:authentication => :plain },
:attachments => {"message.mp3" => open(file_url) {|f| f.read }}
})
end
# Answer the call and try to call MY_NUMBER
get '/answer' do
Twilio::TwiML::Response.new do |r|
r.Say "Thank you for calling. Please wait while we attempt to connect you."
r.Dial :timeout => "10", :action => "/record-or-hangup", :method => "get" do |d|
r.Number MY_NUMBER
end
end.text
end
# If DialCallStatus is "completed", just hang up. Otherwise, record a message
get '/record-or-hangup' do
Twilio::TwiML::Response.new do |r|
if params['DialCallStatus'] == 'completed'
r.Hangup
else
r.Say "Sorry, looks like nobody's around. Please leave a message."
r.Record :action => "/save-recording", :method => "get"
end
end.text
end
# Email the message, then thank the caller and hang up
get '/save-recording' do
send_message("New Message on the hotline",
"Message length: #{params['RecordingDuration']}",
params['RecordingUrl'] + '.mp3')
Twilio::TwiML::Response.new do |r|
r.Say "Thank you for calling. Bye!"
r.Hangup
end.text
end
|
I'm not here to explain the details of how Twilio's API or ruby libraries work, but here's the broad strokes:
"/answer" is the endpoint that Twilio will contact first. It plays a nice greeting then tries to connect whoever called with another number (in my case, my cell phone). After that, it heads over to "/record-or-hangup". Notice the ".text" at the end of the Response block? That's so that the response gets rendered as XML, which is what Twilio wants.
"/record-or-hangup" will check the "DialCallStatus" parameter. If it's "completed", it means that I answered the phone and then disconnected. Otherwise, the call timed out. If the call times out, a recording is prompted for and taken, redirecting to "/save-recording" when it's done.
"/save-recording" fetches the .mp3 url of the message, and uses the "send_message" function to attach it to an email that I send to myself. By attaching it, I can let gmail worry about storing it. It also send a Hangup to Twilio just to make sure the call doesn't go long.
I set just a 10-second timeout because my cell phone has its own voicemail, and I want the call to disconnect before that kicks in.
Configuring for Heroku
Like Twilio, Heroku is a product that took an existing system (Ruby hosting) and made it absurdly, comically, unbelievably easy. Run the following commands to create a git repo, add your files to it, and make a heroku instance for it.
$ git init $ git add . $ git commit -m "Initial commit" $ heroku create
Heroku also requires a few config files: config.ru and a Procfile. Here's what to put in them:
1 2 3 4 5 6 7 |
#### config.ru
require './app'
run Sinatra::Application
#### Procfile
web: bundle exec thin -R config.ru -p $PORT start
|
Running the following...
$ foreman start
...will let you test out the basics. Go to http://localhost:5000/answer to check if everything's working.
Deploying to Heroku
$ git push heroku master
Connecting it to Twilio
When you ran heroku create before, it would have told you what your server name is. Something like http://moonlit-night-1234.herokuapp.com/. (I really enjoy their naming scheme)
You made the endpoint "/answer", so go to https://www.twilio.com/user/account/phone-numbers/incoming (log in to Twilio first), select the number you want to hook up, and use a url like ...
http://moonlit-night-1234.herokuapp.com/answer
... as your voice request URL.
That's it! You can test it out right away.