├── lib
├── twilio
│ ├── version.rb
│ ├── account.rb
│ ├── twilio_object.rb
│ ├── notification.rb
│ ├── recording.rb
│ ├── sms.rb
│ ├── outgoing_caller_id.rb
│ ├── incoming_phone_number.rb
│ ├── call.rb
│ ├── conference.rb
│ ├── available_phone_numbers.rb
│ └── verb.rb
└── twilio.rb
├── Gemfile
├── .gitignore
├── Rakefile
├── spec
├── fixtures
│ ├── xml
│ │ ├── outgoing_caller_id_new.xml
│ │ ├── conference.xml
│ │ ├── account_renamed.xml
│ │ ├── recording.xml
│ │ ├── outgoing_caller_id.xml
│ │ ├── available_phone_numbers_toll_free_search.xml
│ │ ├── call_new.xml
│ │ ├── conferences.xml
│ │ ├── transcription.xml
│ │ ├── call_redirected.xml
│ │ ├── incoming_phone_number.xml
│ │ ├── conference_participant.xml
│ │ ├── conference_participant_muted.xml
│ │ ├── sms_new.xml
│ │ ├── sms.xml
│ │ ├── call.xml
│ │ ├── available_phone_numbers_toll_free.xml
│ │ ├── sms_new_with_callback.xml
│ │ ├── available_phone_numbers_local_search.xml
│ │ ├── recordings.xml
│ │ ├── notification.xml
│ │ ├── outgoing_caller_ids.xml
│ │ ├── transcriptions.xml
│ │ ├── incoming_phone_numbers.xml
│ │ ├── conference_participants.xml
│ │ ├── available_phone_numbers_local.xml
│ │ ├── sms_messages.xml
│ │ ├── calls.xml
│ │ ├── notifications.xml
│ │ └── account.xml
│ └── yml
│ │ └── verb_responses.yml
├── spec_helper.rb
├── twilio
│ ├── live_connection_spec.rb
│ ├── account_spec.rb
│ ├── notification_spec.rb
│ ├── sms_spec.rb
│ ├── incoming_phone_number_spec.rb
│ ├── outgoing_caller_id_spec.rb
│ ├── recording_spec.rb
│ ├── call_spec.rb
│ ├── conference_spec.rb
│ ├── available_phone_numbers_spec.rb
│ └── verb_spec.rb
└── support
│ └── twilio_helpers.rb
├── LICENSE
├── twilio.gemspec
├── CHANGELOG.rdoc
└── README.rdoc
/lib/twilio/version.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | VERSION = "3.1.1"
3 | end
4 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 | gemspec
3 |
4 | platforms :jruby do
5 | gem 'jruby-openssl'
6 | end
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sw?
2 | .DS_Store
3 | bin
4 | coverage
5 | rdoc
6 | *.gem
7 | .bundle
8 | Gemfile.lock
9 | pkg/*
10 | .rvmrc
11 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler/setup'
2 | Bundler::GemHelper.install_tasks
3 |
4 | require 'rake'
5 | require 'rspec/core/rake_task'
6 |
7 | RSpec::Core::RakeTask.new
8 |
9 | task :default => :spec
--------------------------------------------------------------------------------
/spec/fixtures/xml/outgoing_caller_id_new.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | mysid
4 | 4158675309
5 | 123456
6 |
7 |
--------------------------------------------------------------------------------
/lib/twilio/account.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # The Account resource represents your Twilio Account.
3 | class Account < TwilioObject
4 | def get
5 | Twilio.get('')
6 | end
7 |
8 | def update_name(name)
9 | Twilio.put('', :body => {:FriendlyName => name})
10 | end
11 | end
12 | end
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'bundler'
2 | Bundler.setup
3 |
4 | require 'twilio'
5 |
6 | require 'support/twilio_helpers'
7 | require 'webmock/rspec'
8 |
9 | RSpec.configure do |config|
10 | config.include TwilioHelpers
11 | config.before(:suite) do
12 | WebMock.disable_net_connect!
13 | end
14 | config.after(:suite) do
15 | WebMock.allow_net_connect!
16 | end
17 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/conference.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CF9f2ead1ae43cdabeab102fa30d938378
4 | mysid
5 | 1234
6 | 2
7 | Thu, 03 Sep 2009 23:37:53 -0700
8 | Fri, 04 Sep 2009 00:35:02 -0700
9 |
10 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/account_renamed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | mysid
4 | Bubba
5 | 2
6 | Active
7 | Wed, 02 Apr 2008 17:33:38 -0700
8 | Wed, 02 Apr 2008 17:34:18 -0700
9 | mytoken
10 |
11 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/recording.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | RE41331862605f3d662488fdafda2e175f
4 | mysid
5 | CAcd420fcb3c4b86e360ea0cc27ebc8698
6 | 15
7 | Tue, 01 Apr 2008 01:07:15 -0400
8 | Tue, 01 Apr 2008 01:07:15 -0400
9 |
10 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/outgoing_caller_id.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | PNe536dfda7c6184afab78d980cb8cdf43
4 | mysid
5 | My Home Phone Number
6 | 4158675309
7 | Tue, 01 Apr 2008 11:26:32 -0700
8 | Tue, 01 Apr 2008 11:26:32 -0700
9 |
10 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/available_phone_numbers_toll_free_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | (866) 557-8676
5 | +18665578676
6 | US
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/call_new.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CA42ed11f93dc08b952027ffbc406d0868
4 | AC309475e5fede1b49e100272a8640f438
5 | 4155551212
6 | 4158675309
7 | PN01234567890123456789012345678900
8 | 0
9 | Thu, 03 Apr 2008 04:36:33 -0400
10 |
11 |
12 | 1
13 |
14 |
--------------------------------------------------------------------------------
/spec/twilio/live_connection_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | # uncomment and add your own tests here
4 | =begin
5 | describe "testing with a live connection" do
6 | before(:all) do
7 | WebMock.allow_net_connect!
8 | @sid = 'abc123'
9 | @token = '123'
10 | Twilio.connect(@sid, @token)
11 | end
12 |
13 | after(:all) do
14 | WebMock.disable_net_connect!
15 | end
16 |
17 | it "gets real account" do
18 | Twilio::Account.get.should include("TwilioResponse")
19 | end
20 | end
21 | =end
--------------------------------------------------------------------------------
/spec/fixtures/xml/conferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFd0a50bbe038c437e87f6c82db8f37f21
5 | mysid
6 | 1234
7 | 2
8 | Thu, 03 Sep 2009 23:37:53 -0700
9 | Fri, 04 Sep 2009 00:35:02 -0700
10 |
11 |
12 |
--------------------------------------------------------------------------------
/lib/twilio/twilio_object.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | class TwilioObject #:nodoc: all
3 | def initialize
4 | end
5 |
6 | class << self
7 | def method_missing(method_id, *args) #:nodoc:
8 | o = self.new
9 | o.send(method_id, *args)
10 | rescue HTTParty::UnsupportedURIScheme
11 | raise "You must set Twilio.connect before calling #{self.inspect}##{method_id}"
12 | end
13 | end
14 |
15 | def connected?
16 | self.class.base_uri
17 | end
18 | end
19 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/transcription.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | TRbdece5b75f2cd8f6ef38e0a10f5c4447
4 | 1235986685
5 | 1235957924
6 | mysid
7 | completed
8 | RE3870404da563592ef6a72136438a879c
9 | 9
10 | This is the body a transcribed recording
11 | -0.03000
12 |
13 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/call_redirected.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CA42ed11f93dc08b952027ffbc406d0868
4 |
5 | AC309475e5fede1b49e100272a8640f438
6 | 4155551234
7 | 4158675309
8 | PN01234567890123456789012345678900
9 | 1
10 | Thu, 03 Apr 2008 04:36:33 -0400
11 |
12 |
13 | 1
14 |
15 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/incoming_phone_number.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | PNe536dfda7c6184afab78d980cb8cdf43
4 | mysid
5 | My Home Phone Number
6 | 4158675309
7 | http://mycompany.com/handleMainLineCall.asp
8 | GET
9 | Tue, 01 Apr 2008 11:26:32 -0700
10 | Tue, 01 Apr 2008 11:26:32 -0700
11 |
12 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/conference_participant.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CF9f2ead1ae43cdabeab102fa30d938378
4 | mysid
5 | CA9ae8e040497c0598481c2031a154919e
6 | false
7 | true
8 | false
9 | Wed, 31 Dec 1969 16:33:29 -0800
10 | Wed, 31 Dec 1969 16:33:29 -0800
11 |
12 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/conference_participant_muted.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CF9f2ead1ae43cdabeab102fa30d938378
4 | mysid
5 | CA9ae8e040497c0598481c2031a154919e
6 | true
7 | true
8 | false
9 | Wed, 31 Dec 1969 16:33:29 -0800
10 | Wed, 31 Dec 1969 16:33:29 -0800
11 |
12 |
--------------------------------------------------------------------------------
/spec/twilio/account_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe 'Account' do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | end
7 |
8 | it "gets an account" do
9 | response, url = stub_get(:account)
10 |
11 | Twilio::Account.get.should eql response
12 | WebMock.should have_requested(:get, twilio_url)
13 | end
14 |
15 | it "updates name" do
16 | response, url = stub_put(:account_renamed)
17 |
18 | Twilio::Account.update_name('Bubba').should eql response
19 | WebMock.should have_requested(:put, twilio_url).with(:body => {:FriendlyName => 'Bubba'})
20 | end
21 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/sms_new.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | SM872fb94e3b358913777cdb313f25b46f
4 | Sun, 04 Oct 2009 03:48:08 -0700
5 | Sun, 04 Oct 2009 03:48:10 -0700
6 | Sun, 04 Oct 2009 03:48:10 -0700
7 | AC5ea872f6da5a21de157d80997a64bd33
8 | 5558675309
9 | 4155551212
10 | Hi Jenny! Want to grab dinner?
11 | sent
12 | 2
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/sms.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | SM872fb94e3b358913777cdb313f25b46f
4 | Sun, 04 Oct 2009 03:48:08 -0700
5 | Sun, 04 Oct 2009 03:48:10 -0700
6 | Sun, 04 Oct 2009 03:48:10 -0700
7 | AC5ea872f6da5a21de157d80997a64bd33
8 | 4158675309
9 | 6505551212
10 | Hi there Jenny! Why didn't you call me back?
11 | sent
12 | 2
13 |
14 |
15 |
--------------------------------------------------------------------------------
/lib/twilio/notification.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # A Notification represenents a log entry made by Twilio in the course of handling
3 | # your calls or using the REST API.
4 | # Example:
5 | # Twilio.connect('my_twilio_sid', 'my_auth_token')
6 | # Twilio::Notification.list
7 | class Notification < TwilioObject
8 | def list(opts = {})
9 | Twilio.get('/Notifications', :query => (opts.empty? ? nil : opts))
10 | end
11 |
12 | def get(notification_sid)
13 | Twilio.get("/Notifications/#{notification_sid}")
14 | end
15 |
16 | def delete(notification_sid)
17 | Twilio.delete("/Notifications/#{notification_sid}")
18 | end
19 | end
20 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/call.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | CA42ed11f93dc08b952027ffbc406d0868
4 | Sat, 07 Feb 2009 13:15:19 -0800
5 | Sat, 07 Feb 2009 13:15:19 -0800
6 |
7 | mysid
8 | 4159633717
9 | 4156767925
10 | PN01234567890123456789012345678900
11 | 2
12 | Thu, 03 Apr 2008 04:36:33 -0400
13 | Thu, 03 Apr 2008 04:36:47 -0400
14 | 14
15 |
16 | 1
17 |
18 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/available_phone_numbers_toll_free.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | (866) 583-8815
5 | +18665838815
6 | US
7 |
8 |
9 | (866) 583-0795
10 | +18665830795
11 | US
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/sms_new_with_callback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | SM872fb94e3b358913777cdb313f25b46f
4 | Sun, 04 Oct 2009 03:48:08 -0700
5 | Sun, 04 Oct 2009 03:48:10 -0700
6 | Sun, 04 Oct 2009 03:48:10 -0700
7 | AC5ea872f6da5a21de157d80997a64bd33
8 | 5558675309
9 | 4155551212
10 | Hi Jenny! Want to grab dinner?
11 | http://example.com/callback
12 | sent
13 | 2
14 |
15 |
16 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/available_phone_numbers_local_search.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | (510) 555-1214
5 | +15105551214
6 | 722
7 | OKLD0349T
8 | 37.806940
9 | -122.270360
10 | CA
11 | 94612
12 | US
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/lib/twilio/recording.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # Recordings are generated when you use the Record Verb. Those recordings are
3 | # hosted on Twilio's REST API for you to access.
4 | # Example:
5 | # Twilio.connect('my_twilio_sid', 'my_auth_token')
6 | # Twilio::Recording.list
7 | class Recording < TwilioObject
8 | def list(opts = {})
9 | Twilio.get("/Recordings", :query => (opts.empty? ? nil : opts))
10 | end
11 |
12 | def get(recording_sid)
13 | Twilio.get("/Recordings/#{recording_sid}")
14 | end
15 |
16 | def delete(recording_sid)
17 | Twilio.delete("/Recordings/#{recording_sid}")
18 | end
19 |
20 | def transcriptions(recording_sid, transcription_sid = nil)
21 | Twilio.get("/Recordings/#{recording_sid}/Transcriptions#{ '/' + transcription_sid if transcription_sid }")
22 | end
23 | end
24 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/recordings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RE41331862605f3d662488fdafda2e175f
5 | mysid
6 | CAcd420fcb3c4b86e360ea0cc27ebc8698
7 | 123
8 | Tue, 01 Apr 2008 01:07:15 -0400
9 | Tue, 01 Apr 2008 01:07:15 -0400
10 |
11 |
12 | RE50358f2565ad3c542e004161c3aecfd2
13 | mysid
14 | CAcd420fcb3c4b86e360ea0cc27ebc8698
15 | 45
16 | Tue, 01 Apr 2008 01:07:10 -0400
17 | Tue, 01 Apr 2008 01:07:10 -0400
18 |
19 |
20 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/notification.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | NO1fb7086ceb85caed2265f17d7bf7981c
4 | Sat, 07 Feb 2009 13:15:19 -0800
5 | Sat, 07 Feb 2009 13:15:19 -0800
6 | mysid
7 | CA42ed11f93dc08b952027ffbc406d0868
8 | 0
9 | 12345
10 | http://www.twilio.com/docs/errors/12345
11 | Unable to parse XML response
12 | Thu, 03 Apr 2008 04:36:32 -0400
13 | http://yourserver.com/handleCall.php
14 | POST
15 | From=4158675309&To=4155551212...
16 | Content-Length: 500
17 | <h1>Error parsing PHP script</h1>
18 |
19 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/outgoing_caller_ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PNe536dfda7c6184afab78d980cb8cdf43
5 | mysid
6 | Bob Cell Phone
7 | 4158675309
8 | Tue, 01 Apr 2008 11:26:32 -0700
9 | Tue, 01 Apr 2008 11:26:32 -0700
10 |
11 |
12 | PNe536dfda7c6DDd455fed980cb83345FF
13 | mysid
14 | Company Main Line
15 | 4158675310
16 | Tue, 01 Apr 2008 11:26:32 -0700
17 | Tue, 01 Apr 2008 11:26:32 -0700
18 |
19 |
20 |
--------------------------------------------------------------------------------
/lib/twilio/sms.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # An SMS message resource represents an Inbound or Outbound SMS message. When someone sends a text message from
3 | # or to your application, either via the REST API, or during a call via the verb, an SMS message resource is created.
4 | class Sms < TwilioObject
5 | # Example:
6 | # Twilio.connect('my_twilio_sid', 'my_auth_token')
7 | # Twilio::Sms.message(CALLER_ID, user_number, 'This is my simple SMS message', 'http://example.com/sms_callback')
8 | def message(from, to, body, callback_url=nil)
9 | callback = callback_url ? {:StatusCallback => callback_url} : {}
10 | Twilio.post("/SMS/Messages", :body => {:From => from, :To => to, :Body => body}.merge(callback))
11 | end
12 |
13 | def list(opts = {})
14 | Twilio.get("/SMS/Messages", :query => (opts.empty? ? nil : opts))
15 | end
16 |
17 | def get(sms_message_sid)
18 | Twilio.get("/SMS/Messages/#{sms_message_sid}")
19 | end
20 |
21 | end
22 | end
--------------------------------------------------------------------------------
/spec/twilio/notification_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Notification" do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | @notification_sid = 'NO1fb7086ceb85caed2265f17d7bf7981c'
7 | end
8 |
9 | it "gets a list of notifications" do
10 | response, url = stub_get(:notifications, 'Notifications')
11 |
12 | Twilio::Notification.list.should eql response
13 | WebMock.should have_requested(:get, url)
14 | end
15 |
16 | it "gets a specific notification" do
17 | response, url = stub_get(:notification, "Notifications/#{@notification_sid}")
18 |
19 | Twilio::Notification.get(@notification_sid).should eql response
20 | WebMock.should have_requested(:get, url)
21 | end
22 |
23 | it "is deleted" do
24 | response, url = stub_delete(:notification, "Notifications/#{@notification_sid}")
25 |
26 | Twilio::Notification.delete(@notification_sid).should eql response
27 | WebMock.should have_requested(:delete, url)
28 | end
29 | end
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Phil Misiowiec, Webficient LLC
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/twilio.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | $:.push File.expand_path("../lib", __FILE__)
3 | require "twilio/version"
4 |
5 | Gem::Specification.new do |s|
6 | s.name = "twilio"
7 | s.version = Twilio::VERSION
8 | s.platform = Gem::Platform::RUBY
9 | s.authors = ["Phil Misiowiec", "Jonathan Rudenberg", "Alex K Wolfe", "Kyle Daigle", "Jeff Wigal", "Yuri Gadow", "Vlad Moskovets"]
10 | s.email = ["github@webficient.com"]
11 | s.homepage = ""
12 | s.summary = %q{Twilio API Client}
13 | s.description = %q{Twilio API wrapper}
14 |
15 | s.rubyforge_project = "twilio"
16 |
17 | s.files = `git ls-files`.split("\n")
18 | s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19 | s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20 | s.require_paths = ["lib"]
21 |
22 | s.add_dependency "builder", ">= 2.1.2"
23 | s.add_dependency "httparty", ">= 0.8"
24 |
25 | {
26 | 'rake' => '~> 0.8.7',
27 | 'rspec' => '~> 2.12',
28 | 'webmock' => '~> 1.6.2'
29 | }.each { |l, v| s. add_development_dependency l, v }
30 | end
31 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/transcriptions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TR685e9a2bdf89b978491b1afada63f078
5 | 1235986685
6 | 1235957975
7 | mysid
8 | completed
9 | RE3870404da563592ef6a72136438a879c
10 | 9
11 | This is the body of one transcribed recording
12 | -0.25000
13 |
14 |
15 | TRbdece5b75f2cd8f6ef38e0a10f5c4447
16 | 1235986685
17 | 1235957924
18 | mysid
19 | completed
20 | RE3870404da563592ef6a72136438a879c
21 | 9
22 | This is the body of another transcribed recording
23 | -0.03000
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/incoming_phone_numbers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PNe536dfda7c6184afab78d980cb8cdf43
5 | mysid
6 | Company Main Line
7 | 4158675309
8 | http://mycompany.com/handleMainLineCall.asp
9 | GET
10 | Tue, 01 Apr 2008 11:26:32 -0700
11 | Tue, 01 Apr 2008 11:26:32 -0700
12 |
13 |
14 | PNe536dfda7c6DDd455fed980cb83345FF
15 | mysid
16 | Company Support Line
17 | 4158675310
18 | http://mycompany.com/handleSupportCall.php
19 | POST
20 | Tue, 01 Apr 2008 11:26:32 -0700
21 | Tue, 01 Apr 2008 11:26:32 -0700
22 |
23 |
24 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/conference_participants.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CF9f2ead1ae43cdabeab102fa30d938378
5 | mysid
6 | CA9ae8e040497c0598481c2031a154919e
7 | false
8 | true
9 | false
10 | Wed, 31 Dec 1969 16:33:29 -0800
11 | Wed, 31 Dec 1969 16:33:29 -0800
12 |
13 |
14 | CF9f2ead1ae43cdabeab102fa30d938378
15 | mysid
16 | CA3c3c002e06c4cdaa5367e814ade05c76
17 | false
18 | true
19 | false
20 | Wed, 31 Dec 1969 16:33:29 -0800
21 | Wed, 31 Dec 1969 16:33:29 -0800
22 |
23 |
24 |
--------------------------------------------------------------------------------
/CHANGELOG.rdoc:
--------------------------------------------------------------------------------
1 | = Twilio Gem Changelog
2 |
3 | == 3.1.1
4 |
5 | * Loosens dependency on httparty gem to address a potential vulnerability
6 |
7 | == 3.1.0
8 |
9 | * Better handling of Builder version for greater compat
10 | * Reject verb
11 | * Dependency on recent, improved version of HTTParty
12 |
13 | == 3.0.1
14 |
15 | * Bumped down Builder version requirement for Rails compat
16 |
17 | == 3.0
18 |
19 | * SSL validation (thanks Kyle Humberto)
20 | * Available phone numbers search support (thanks Mark Turner)
21 | * Deprecated Twilio::Connection method has been removed, use Twilio.connect(...)
22 | * LocalPhoneNumber and TollFreeNumber have been removed to reflect API changes
23 | * New phone numbers are now provisioned via the IncomingPhoneNumber class
24 | * Several classes improved to avoid adding a stray "?" when no params were set
25 | * Compatibility with 1.9.2 (and tested on 1.8.7 MRI and REE)
26 | * Now uses Bundler, Rspec 2, and WebMock
27 |
28 | == 2.9
29 |
30 | * Compatibility with Twilio's 2010-04-01 API publish
31 |
32 | == 2.8
33 |
34 | * SMS callback URLs
35 | * Ability to delete phone numbers
36 |
37 | == 2.7
38 |
39 | * SMS functionality
40 |
41 | For earlier versions, see https://github.com/webficient/twilio/commits/master
--------------------------------------------------------------------------------
/lib/twilio/outgoing_caller_id.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # An OutgoingCallerId resource represents an outgoing Caller ID that you have
3 | # registered with Twilio for use when making an outgoing call or using the Dial Verb.
4 | # Example:
5 | # Twilio.connect('my_twilio_sid', 'my_auth_token')
6 | # Twilio::OutgoingCallerId.list
7 | class OutgoingCallerId < TwilioObject
8 | def create(phone_number, friendly_name = phone_number, call_delay = 0, extension = nil)
9 | Twilio.post("/OutgoingCallerIds", :body => {
10 | :PhoneNumber => phone_number,
11 | :FriendlyName => friendly_name,
12 | :CallDelay => call_delay,
13 | :Extension => extension
14 | })
15 | end
16 |
17 | def list(opts = {})
18 | Twilio.get("/OutgoingCallerIds", :query => (opts.empty? ? nil : opts))
19 | end
20 |
21 | def get(callerid_sid)
22 | Twilio.get("/OutgoingCallerIds/#{callerid_sid}")
23 | end
24 |
25 | def update_name(callerid_sid, name)
26 | Twilio.put("/OutgoingCallerIds/#{callerid_sid}", :body => {:FriendlyName => name})
27 | end
28 |
29 | def delete(callerid_sid)
30 | Twilio.delete("/OutgoingCallerIds/#{callerid_sid}")
31 | end
32 | end
33 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/available_phone_numbers_local.xml:
--------------------------------------------------------------------------------
1 | TwilioResponse>
2 |
3 |
4 | (510) 564-7903
5 | +15105647903
6 | 722
7 | OKLD TRNID
8 | 37.780000
9 | -122.380000
10 | CA
11 | 94703
12 | US
13 |
14 |
15 | (510) 488-4379
16 | +15104884379
17 | 722
18 | OKLD FRTVL
19 | 37.780000
20 | -122.380000
21 | CA
22 | 94602
23 | US
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/spec/twilio/sms_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "SMS" do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | @sms_sid = 'SM872fb94e3b358913777cdb313f25b46f'
7 | end
8 |
9 | it "gets a list of SMS messages" do
10 | response, url = stub_get(:sms_messages, 'SMS/Messages')
11 |
12 | Twilio::Sms.list.should eql response
13 | WebMock.should have_requested(:get, url)
14 | end
15 |
16 | it "gets a specific SMS message" do
17 | response, url = stub_get(:sms, "SMS/Messages/#{@sms_sid}")
18 |
19 | Twilio::Sms.get(@sms_sid).should eql response
20 | WebMock.should have_requested(:get, url)
21 | end
22 |
23 | it "is created" do
24 | response, url = stub_post(:sms_new, "SMS/Messages")
25 |
26 | Twilio::Sms.message('4155551212', '5558675309', 'Hi Jenny! Want to grab dinner?').should eql response
27 | WebMock.should have_requested(:post, url)
28 | end
29 |
30 | it "is created with a callback URL" do
31 | response, url = stub_post(:sms_new_with_callback, "SMS/Messages")
32 |
33 | Twilio::Sms.message('4155551212', '5558675309', 'Hi Jenny! Want to grab dinner?', 'http://example.com/callback').should eql response
34 | WebMock.should have_requested(:post, url)
35 | end
36 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/sms_messages.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | SM872fb94e3b358913777cdb313f25b46f
6 | Sun, 04 Oct 2009 03:48:08 -0700
7 | Sun, 04 Oct 2009 03:48:10 -0700
8 | Sun, 04 Oct 2009 03:48:10 -0700
9 | AC5ea872f6da5a21de157d80997a64bd33
10 | 4158675309
11 | 6505551212
12 | Hi there Jenny! Want to grab dinner?
13 | sent
14 | 2
15 |
16 |
17 | SM47dfd824add482e1fee25ee3ce216113
18 | Sun, 04 Oct 2009 03:48:07 -0700
19 | Sun, 04 Oct 2009 03:48:07 -0700
20 | Sun, 04 Oct 2009 03:48:07 -0700
21 | AC5ea872f6da5a21de157d80997a64bd33
22 | 6505551212
23 | 4158675309
24 | The judge said you can't text me anymore.
25 | sent
26 | 1
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/lib/twilio/incoming_phone_number.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # An IncomingPhoneNumber resource represents a phone number given to you by
3 | # Twilio to receive incoming phone calls.
4 | # Example:
5 | # Twilio.connect('my_twilio_sid', 'my_auth_token')
6 | # Twilio::IncomingPhoneNumber.list
7 | class IncomingPhoneNumber < TwilioObject
8 | def list(opts = {})
9 | Twilio.get("/IncomingPhoneNumbers", :query => (opts.empty? ? nil : opts))
10 | end
11 |
12 | def get(incoming_sid)
13 | Twilio.get("/IncomingPhoneNumbers/#{incoming_sid}")
14 | end
15 |
16 | # Creates a phone number in Twilio. You must first find an existing number using
17 | # the AvailablePhoneNumber class before creating one here.
18 | #
19 | # Required: you must either set PhoneNumber or AreaCode as a required option
20 | # For additional options, see http://www.twilio.com/docs/api/rest/incoming-phone-numbers
21 | def create(opts)
22 | raise "You must set either :PhoneNumber or :AreaCode" if !opts.include?(:AreaCode) && !opts.include?(:PhoneNumber)
23 | Twilio.post("/IncomingPhoneNumbers", :body => opts)
24 | end
25 |
26 | def update(incoming_sid, opts)
27 | Twilio.post("/IncomingPhoneNumbers/#{incoming_sid}", :body => opts)
28 | end
29 |
30 | def delete(incoming_sid)
31 | Twilio.delete("/IncomingPhoneNumbers/#{incoming_sid}")
32 | end
33 | end
34 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/calls.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CA42ed11f93dc08b952027ffbc406d0868
5 | Sat, 07 Feb 2009 13:15:19 -0800
6 | Sat, 07 Feb 2009 13:15:19 -0800
7 |
8 | mysid
9 | 4159633717
10 | 4156767925
11 | PN01234567890123456789012345678900
12 | 2
13 | Thu, 03 Apr 2008 04:36:33 -0400
14 | Thu, 03 Apr 2008 04:36:47 -0400
15 | 14
16 |
17 | 1
18 |
19 |
20 | CA751e8fa0a0105cf26a0d7a9775fb4bfb
21 | Sat, 07 Feb 2009 13:15:19 -0800
22 | Sat, 07 Feb 2009 13:15:19 -0800
23 |
24 | mysid
25 | 2064287985
26 | 4156767925
27 | PNd59c2ba27ef48264773edb90476d1674
28 | 2
29 | Thu, 03 Apr 2008 01:37:05 -0400
30 | Thu, 03 Apr 2008 01:37:40 -0400
31 | 35
32 |
33 | 1
34 |
35 |
36 |
--------------------------------------------------------------------------------
/lib/twilio/call.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # A Call represenents a connection between a telephone and Twilio. This may be
3 | # inbound, when a person calls your application, or outbound when your application
4 | # initiates the call, either via the REST API, or during a call via the Dial Verb.
5 | class Call < TwilioObject
6 | # Example:
7 | # Twilio.connect('my_twilio_sid', 'my_auth_token')
8 | # Twilio::Call.make(CALLER_ID, user_number, 'http://myapp.com/twilio_response_handler')
9 | def make(from, to, url, opts = {})
10 | Twilio.post("/Calls", :body => {:From => from, :To => to, :Url => url}.merge(opts))
11 | end
12 |
13 | def list(opts = {})
14 | Twilio.get("/Calls", :query => (opts.empty? ? nil : opts))
15 | end
16 |
17 | def get(call_sid)
18 | Twilio.get("/Calls/#{call_sid}")
19 | end
20 |
21 | def redirect(call_sid, new_url, url_action = 'POST')
22 | Twilio.post("/Calls/#{call_sid}", :body => {:CurrentUrl => new_url, :CurrentMethod => url_action})
23 | end
24 |
25 | def segments(call_sid, call_segment_sid = nil)
26 | Twilio.get("/Calls/#{call_sid}/Segments#{ '/' + call_segment_sid if call_segment_sid }")
27 | end
28 |
29 | def recordings(call_sid)
30 | Twilio.get("/Calls/#{call_sid}/Recordings")
31 | end
32 |
33 | def notifications(call_sid)
34 | Twilio.get("/Calls/#{call_sid}/Notifications")
35 | end
36 | end
37 | end
--------------------------------------------------------------------------------
/lib/twilio/conference.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # The Conference REST resource allows you to query and manage the state of conferences.
3 | # When a caller joins a conference via the Dial verb and Conference noun,
4 | # a Conference Instance Resource is created to represent the conference room
5 | # and a Participant Instance Resource is created to represent the caller who joined.
6 | class Conference < TwilioObject
7 | def list(opts = {})
8 | Twilio.get("/Conferences", :query => (opts.empty? ? nil : opts))
9 | end
10 |
11 | def get(conference_sid)
12 | Twilio.get("/Conferences/#{conference_sid}")
13 | end
14 |
15 | def participants(conference_sid, opts = {})
16 | Twilio.get("/Conferences/#{conference_sid}/Participants", :query => (opts.empty? ? nil : opts))
17 | end
18 |
19 | def participant(conference_sid, call_sid)
20 | Twilio.get("/Conferences/#{conference_sid}/Participants/#{call_sid}")
21 | end
22 |
23 | def mute_participant(conference_sid, call_sid)
24 | Twilio.post("/Conferences/#{conference_sid}/Participants/#{call_sid}", :body => {:Muted => true})
25 | end
26 |
27 | def unmute_participant(conference_sid, call_sid)
28 | Twilio.post("/Conferences/#{conference_sid}/Participants/#{call_sid}", :body => {:Muted => false})
29 | end
30 |
31 | def kick_participant(conference_sid, call_sid)
32 | Twilio.delete("/Conferences/#{conference_sid}/Participants/#{call_sid}")
33 | end
34 | end
35 | end
--------------------------------------------------------------------------------
/spec/support/twilio_helpers.rb:
--------------------------------------------------------------------------------
1 | module TwilioHelpers #:nodoc:
2 |
3 | def stub_http_request(http_method, fixture_name, *opts)
4 | if opts
5 | request_options = opts.pop if opts.last.is_a?(Hash)
6 | resource = opts.pop
7 | end
8 |
9 | fake_response = fixture(fixture_name)
10 | url = twilio_url(resource)
11 |
12 | if request_options
13 | stub_request(http_method, url).with(request_options).to_return(:body => fake_response)
14 | else
15 | stub_request(http_method, url).to_return(:body => fake_response)
16 | end
17 |
18 | return fake_response, url
19 | end
20 |
21 | def stub_get(fixture, *opts)
22 | stub_http_request(:get, fixture, *opts)
23 | end
24 |
25 | def stub_post(fixture, *opts)
26 | stub_http_request(:post, fixture, *opts)
27 | end
28 |
29 | def stub_put(fixture, *opts)
30 | stub_http_request(:put, fixture, *opts)
31 | end
32 |
33 | def stub_delete(fixture, *opts)
34 | stub_http_request(:delete, fixture, *opts)
35 | end
36 |
37 | def verb_response(verb)
38 | path = File.join(File.dirname(__FILE__), "../fixtures/yml/verb_responses.yml")
39 | YAML.load_file(path)[verb.to_s]['response']
40 | end
41 |
42 | private
43 |
44 | def twilio_url(url=nil)
45 | "https://mysid:mytoken@api.twilio.com:443/2010-04-01/Accounts/mysid#{'/' + url if url}"
46 | end
47 |
48 | def fixture(filename)
49 | path = File.join(File.dirname(__FILE__), "../fixtures/xml/#{filename}.xml")
50 | File.read path
51 | end
52 | end
--------------------------------------------------------------------------------
/spec/twilio/incoming_phone_number_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Incoming Phone Number" do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | @incoming_sid = 'PNe536dfda7c6184afab78d980cb8cdf43'
7 | end
8 |
9 | it "gets a specific phone number" do
10 | response, url = stub_get(:incoming_phone_number, "IncomingPhoneNumbers/#{@incoming_sid}")
11 |
12 | Twilio::IncomingPhoneNumber.get(@incoming_sid).should eql response
13 | WebMock.should have_requested(:get, url)
14 | end
15 |
16 | it "gets a list of phone numbers" do
17 | response, url = stub_get(:incoming_phone_numbers, 'IncomingPhoneNumbers')
18 |
19 | Twilio::IncomingPhoneNumber.list.should eql response
20 | WebMock.should have_requested(:get, url)
21 | end
22 |
23 | context "creating" do
24 | it "is created" do
25 | response, url = stub_post(:incoming_phone_number, 'IncomingPhoneNumbers')
26 |
27 | Twilio::IncomingPhoneNumber.create(:PhoneNumber => '8055551212').should eql response
28 | WebMock.should have_requested(:post, url)
29 | end
30 |
31 | it "raises an exception if PhoneNumber or AreaCode are not set" do
32 | expect { Twilio::IncomingPhoneNumber.create(:FriendlyName => 'Booyah') }.to raise_exception
33 | end
34 | end
35 |
36 | it "is deleted" do
37 | response, url = stub_delete(:incoming_phone_number, "IncomingPhoneNumbers/#{@incoming_sid}")
38 |
39 | Twilio::IncomingPhoneNumber.delete(@incoming_sid).should eql response
40 | WebMock.should have_requested(:delete, url)
41 | end
42 | end
--------------------------------------------------------------------------------
/spec/twilio/outgoing_caller_id_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Outgoing Caller ID" do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | @callerid_sid = 'PNe536dfda7c6184afab78d980cb8cdf43'
7 | end
8 |
9 | it "gets a list of caller id's" do
10 | response, url = stub_get(:outgoing_caller_ids, 'OutgoingCallerIds')
11 |
12 | Twilio::OutgoingCallerId.list.should eql response
13 | WebMock.should have_requested(:get, url)
14 | end
15 |
16 | it "gets a specific caller id" do
17 | response, url = stub_get(:outgoing_caller_id, "OutgoingCallerIds/#{@callerid_sid}")
18 |
19 | Twilio::OutgoingCallerId.get(@callerid_sid).should eql response
20 | WebMock.should have_requested(:get, url)
21 | end
22 |
23 | it "is created" do
24 | response, url = stub_post(:outgoing_caller_id_new, 'OutgoingCallerIds')
25 |
26 | Twilio::OutgoingCallerId.create('4158675309', 'My Home Phone').should eql response
27 | WebMock.should have_requested(:post, url)
28 | end
29 |
30 | it "is deleted" do
31 | response, url = stub_delete(:outgoing_caller_id, "OutgoingCallerIds/#{@callerid_sid}")
32 |
33 | Twilio::OutgoingCallerId.delete(@callerid_sid).should eql response
34 | WebMock.should have_requested(:delete, url)
35 | end
36 |
37 | it "updates name" do
38 | response, url = stub_put(:outgoing_caller_id, "OutgoingCallerIds/#{@callerid_sid}")
39 |
40 | Twilio::OutgoingCallerId.update_name(@callerid_sid, 'My office line').should eql response
41 | WebMock.should have_requested(:put, url)
42 | end
43 | end
--------------------------------------------------------------------------------
/spec/fixtures/xml/notifications.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | NO1fb7086ceb85caed2265f17d7bf7981c
5 | Sat, 07 Feb 2009 13:15:19 -0800
6 | Sat, 07 Feb 2009 13:15:19 -0800
7 | mysid
8 | CA42ed11f93dc08b952027ffbc406d0868
9 | 0
10 | 12345
11 | http://www.twilio.com/docs/errors/12345
12 | http://yourserver.com/handleCall.php
13 | POST
14 | Unable to parse XML response
15 | Thu, 03 Apr 2008 04:36:32 -0400
16 |
17 |
18 | NOe936fdac57d238e56fd346b89820d342
19 | Sat, 07 Feb 2009 13:15:19 -0800
20 | Sat, 07 Feb 2009 13:15:19 -0800
21 | mysid
22 |
23 | 1
24 | 67890
25 | http://www.twilio.com/docs/errors/67890
26 | http://api.twilio.com/2008-08-01/Accounts/AC309475e5fede1b49e100272a8640f438/Calls
27 | POST
28 | Unknown parameter received by the REST API: foo=bar
29 | Thu, 03 Apr 2008 04:36:32 -0400
30 |
31 |
32 |
--------------------------------------------------------------------------------
/spec/fixtures/xml/account.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | mysid
4 | Do you like my friendly name?
5 | active
6 | Wed, 04 Aug 2010 21:37:41 +0000
7 | Fri, 06 Aug 2010 01:15:02 +0000
8 | mytoken
9 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d
10 |
11 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/AvailablePhoneNumbers
12 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/Calls
13 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/Conferences
14 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/IncomingPhoneNumbers
15 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/Notifications
16 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/OutgoingCallerIds
17 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/Recordings
18 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/Sandbox
19 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/SMS/Messages
20 | /2010-04-01/Accounts/ACba8bc05eacf94afdae398e642c9cc32d/Transcriptions
21 |
22 |
23 |
--------------------------------------------------------------------------------
/spec/twilio/recording_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Recording" do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | @recording_sid = 'RE41331862605f3d662488fdafda2e175f'
7 | @transcription_sid = 'TRbdece5b75f2cd8f6ef38e0a10f5c4447'
8 | end
9 |
10 | it "gets a list of recordings" do
11 | response, url = stub_get(:recordings, 'Recordings')
12 |
13 | Twilio::Recording.list.should eql response
14 | WebMock.should have_requested(:get, url)
15 | end
16 |
17 | it "gets a specific recording" do
18 | response, url = stub_get(:recording, "Recordings/#{@recording_sid}")
19 |
20 | Twilio::Recording.get(@recording_sid).should eql response
21 | WebMock.should have_requested(:get, url)
22 | end
23 |
24 | it "is deleted" do
25 | response, url = stub_delete(:recording, "Recordings/#{@recording_sid}")
26 |
27 | Twilio::Recording.delete(@recording_sid).should eql response
28 | WebMock.should have_requested(:delete, url)
29 | end
30 |
31 | it "gets a list of transcriptions" do
32 | response, url = stub_get(:transcriptions, "Recordings/#{@recording_sid}/Transcriptions")
33 |
34 | Twilio::Recording.transcriptions(@recording_sid).should eql response
35 | WebMock.should have_requested(:get, url)
36 | end
37 |
38 | it "gets a specific transcription" do
39 | response, url = stub_get(:transcriptions, "Recordings/#{@recording_sid}/Transcriptions/#{@transcription_sid}")
40 |
41 | Twilio::Recording.transcriptions(@recording_sid, @transcription_sid).should eql response
42 | WebMock.should have_requested(:get, url)
43 | end
44 | end
--------------------------------------------------------------------------------
/README.rdoc:
--------------------------------------------------------------------------------
1 | = Twilio Gem
2 |
3 | The Twilio gem provides two major pieces of functionality: (1) a Ruby wrapper for the Twilio REST API and (2) response handlers based on the Twilio Markup XML (TwiML).
4 |
5 | See http://www.twilio.com/docs/index for Twilio's API documentation.
6 |
7 | == Calling the Twilio REST API
8 |
9 | First set your credentials by calling the connect method:
10 |
11 | Twilio.connect('my_twilio_sid', 'my_auth_token')
12 |
13 | Now call any of the Twilio classes:
14 |
15 | Twilio::Call.make('1234567890', '9876543210', 'http://mysite.com/connected_call')
16 | Twilio::Recording.list
17 |
18 | == Responding to Twilio
19 |
20 | When Twilio calls your application URL, your response must use the Twilio Markup XML (http://www.twilio.com/docs/api_reference/TwiML/). The Twilio gem makes this very easy
21 | by providing a Twilio Verb class.
22 |
23 | For example, in a Ruby on Rails application, you could do the following inside a controller class:
24 |
25 | Twilio::Verb.dial '415-123-4567'
26 |
27 | and you can nest multiple verbs inside a block:
28 |
29 | verb = Twilio::Verb.new { |v|
30 | v.say "The time is #{Time.now}"
31 | v.hangup
32 | }
33 | verb.response
34 |
35 | == Installation
36 |
37 | gem install twilio
38 |
39 | == Contributing
40 |
41 | 1. Run 'bundle' from the command line to install dependencies
42 | 2. Write test(s) for your patch
43 | 3. Submit a pull request
44 |
45 | Note: don't require 'rubygems' in any file (http://www.rubyinside.com/why-using-require-rubygems-is-wrong-1478.html)
46 |
47 | == Testing
48 |
49 | * Currently using RSpec
50 | * Tested with REE 1.8.7, MRI 1.9.2, MRI 1.9.3, and JRuby 1.6.5
51 |
52 | == Copyright
53 |
54 | Copyright Phil Misiowiec, Webficient LLC. See LICENSE for details.
55 |
56 | == Contributors
57 |
58 | * Kyle Daigle
59 | * Yuri Gadow
60 | * Kyle Humberto
61 | * Vlad Moskovets
62 | * Jonathan Rudenberg
63 | * Mark Turner
64 | * Jeff Wigal
65 | * Alex K Wolfe
--------------------------------------------------------------------------------
/spec/twilio/call_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Call" do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | @call_sid = 'CA42ed11f93dc08b952027ffbc406d0868'
7 | end
8 |
9 | it "can be made" do
10 | response, url = stub_post(:call_new, 'Calls')
11 |
12 | Twilio::Call.make('4158675309', '4155551212', 'http://localhost:3000/call_handler').should eql response
13 | WebMock.should have_requested(:post, url)
14 | end
15 |
16 | it "can be redirected" do
17 | response, url = stub_post(:call_redirected, "Calls/#{@call_sid}")
18 |
19 | Twilio::Call.redirect(@call_sid, 'http://localhost:3000/redirect_handler').should eql response
20 | WebMock.should have_requested(:post, url)
21 | end
22 |
23 | it "gets a list of calls" do
24 | response, url = stub_get(:calls, 'Calls')
25 |
26 | Twilio::Call.list.should eql response
27 | WebMock.should have_requested(:get, url)
28 | end
29 |
30 | it "gets a specific call" do
31 | response, url = stub_get(:calls, "Calls/#{@call_sid}")
32 |
33 | Twilio::Call.get(@call_sid).should eql response
34 | WebMock.should have_requested(:get, url)
35 | end
36 |
37 | it "gets a list of call segments" do
38 | response, url = stub_get(:calls, "Calls/#{@call_sid}/Segments")
39 |
40 | Twilio::Call.segments(@call_sid).should eql response
41 | WebMock.should have_requested(:get, url)
42 | end
43 |
44 | it "gets a specific call segment" do
45 | response, url = stub_get(:calls, "Calls/#{@call_sid}/Segments/abc123")
46 |
47 | Twilio::Call.segments(@call_sid, 'abc123').should eql response
48 | WebMock.should have_requested(:get, url)
49 | end
50 |
51 | it "gets a list of call recordings" do
52 | response, url = stub_get(:recordings, "Calls/#{@call_sid}/Recordings")
53 |
54 | Twilio::Call.recordings(@call_sid).should eql response
55 | WebMock.should have_requested(:get, url)
56 | end
57 |
58 | it "gets a list of call notifications" do
59 | response, url = stub_get(:notifications, "Calls/#{@call_sid}/Notifications")
60 |
61 | Twilio::Call.notifications(@call_sid).should eql response
62 | WebMock.should have_requested(:get, url)
63 | end
64 | end
--------------------------------------------------------------------------------
/lib/twilio.rb:
--------------------------------------------------------------------------------
1 | #--
2 | # Copyright (c) 2009 Phil Misiowiec, phil@webficient.com
3 | #
4 | # Permission is hereby granted, free of charge, to any person obtaining
5 | # a copy of this software and associated documentation files (the
6 | # "Software"), to deal in the Software without restriction, including
7 | # without limitation the rights to use, copy, modify, merge, publish,
8 | # distribute, sublicense, and/or sell copies of the Software, and to
9 | # permit persons to whom the Software is furnished to do so, subject to
10 | # the following conditions:
11 | #
12 | # The above copyright notice and this permission notice shall be
13 | # included in all copies or substantial portions of the Software.
14 | #
15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 | #++
23 |
24 | require 'httparty'
25 |
26 | require 'twilio/twilio_object'
27 |
28 | require 'twilio/account'
29 | require 'twilio/available_phone_numbers'
30 | require 'twilio/call'
31 | require 'twilio/conference'
32 | require 'twilio/incoming_phone_number'
33 | require 'twilio/notification'
34 | require 'twilio/outgoing_caller_id'
35 | require 'twilio/recording'
36 | require 'twilio/sms'
37 | require 'twilio/verb'
38 |
39 | module Twilio
40 | include HTTParty
41 | TWILIO_URL = "https://api.twilio.com/2010-04-01/Accounts"
42 | SSL_CA_PATH = "/etc/ssl/certs"
43 |
44 | # The connect method caches your Twilio account id and authentication token
45 | # Example:
46 | # Twilio.connect('AC309475e5fede1b49e100272a8640f438', '3a2630a909aadbf60266234756fb15a0')
47 | def self.connect(account_sid, auth_token)
48 | self.base_uri "#{TWILIO_URL}/#{account_sid}"
49 | self.basic_auth account_sid, auth_token
50 | self.default_options[:ssl_ca_path] ||= SSL_CA_PATH unless self.default_options[:ssl_ca_file]
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/lib/twilio/available_phone_numbers.rb:
--------------------------------------------------------------------------------
1 | module Twilio
2 | # An AvailablePhoneNumbers resources represents the available phone numbers
3 | # that Twilio can provide you based on your search criteria. The valid
4 | # query terms are outlined in the search methods.
5 | # Example:
6 | # Twilio.connect('my_twilio_sid', 'my_auth_token')
7 | # Twilio.AvailablePhoneNumbers.search_local(:area_code => 541)
8 | class AvailablePhoneNumbers < TwilioObject
9 |
10 | # The Search method handles the searching of both local and toll-free
11 | # numbers.
12 | def search(opts={})
13 | iso_country_code = opts[:iso_country_code] || 'US'
14 | resource = opts.delete(:resource)
15 |
16 | params = {
17 | :AreaCode => opts[:area_code],
18 | :InPostalCode => opts[:postal_code],
19 | :InRegion => opts[:in_region],
20 | :Contains => opts[:contains],
21 | :NearLatLong => opts[:near_lat_long],
22 | :NearNumber => opts[:near_number],
23 | :InLata => opts[:in_lata],
24 | :InRateCenter => opts[:in_rate_center],
25 | :Distance => opts[:distance],
26 | :Page => opts[:page],
27 | :PageSize => opts[:page_size]
28 | }.reject {|k,v| v == nil} unless opts.empty?
29 |
30 | Twilio.get("/AvailablePhoneNumbers/#{iso_country_code}/#{resource}", :query => params)
31 | end
32 |
33 | # The search_local method searches for numbers in local areas (i.e. state, zip, etc..)
34 | # Search Options:
35 | # :area_code
36 | # :postal_code
37 | # :in_region
38 | # :contains
39 | # :near_lat_long
40 | # :near_number
41 | # :in_lata
42 | # :in_rate_center
43 | # :distance
44 | # :page
45 | # :page_size
46 | def search_local(opts ={})
47 | opts = {:resource => 'Local'}.merge(opts)
48 | search(opts)
49 | end
50 |
51 | # The search_toll_free method searches for available toll-free numbers
52 | # Search Options
53 | # :area_code
54 | # :contains
55 | # :page
56 | # :page_size
57 | def search_toll_free(opts ={})
58 | opts = {:resource => 'TollFree'}.merge(opts)
59 | search(opts)
60 | end
61 | end
62 | end
63 |
64 |
--------------------------------------------------------------------------------
/spec/twilio/conference_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Conference" do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | @conference_sid = 'CF9f2ead1ae43cdabeab102fa30d938378'
7 | @call_sid = 'CA9ae8e040497c0598481c2031a154919e'
8 | end
9 |
10 | it "gets a list of conferences" do
11 | response, url = stub_get(:conferences, 'Conferences')
12 |
13 | Twilio::Conference.list.should eql response
14 | WebMock.should have_requested(:get, url)
15 | end
16 |
17 | it "gets a specific conference" do
18 | response, url = stub_get(:conference, "Conferences/#{@conference_sid}")
19 |
20 | Twilio::Conference.get(@conference_sid).should eql response
21 | WebMock.should have_requested(:get, url)
22 | end
23 |
24 | it "gets a list of participants" do
25 | response, url = stub_get(:conference_participants, "Conferences/#{@conference_sid}/Participants")
26 |
27 | Twilio::Conference.participants(@conference_sid).should eql response
28 | WebMock.should have_requested(:get, url)
29 | end
30 |
31 | it "gets a specific participant" do
32 | response, url = stub_get(:conference_participant, "Conferences/#{@conference_sid}/Participants/#{@call_sid}")
33 |
34 | Twilio::Conference.participant(@conference_sid, @call_sid).should eql response
35 | WebMock.should have_requested(:get, url)
36 | end
37 |
38 | it "can mute a particant" do
39 | response, url = stub_post(:conference_participant_muted, "Conferences/#{@conference_sid}/Participants/#{@call_sid}", :body => 'Muted=true')
40 |
41 | Twilio::Conference.mute_participant(@conference_sid, @call_sid).should eql response
42 | WebMock.should have_requested(:post, url)
43 | end
44 |
45 | it "can unmute a participant" do
46 | response, url = stub_post(:conference_participant, "Conferences/#{@conference_sid}/Participants/#{@call_sid}", :body => 'Muted=false')
47 |
48 | Twilio::Conference.unmute_participant(@conference_sid, @call_sid).should eql response
49 | WebMock.should have_requested(:post, url)
50 | end
51 |
52 | it "can be kicked" do
53 | response, url = stub_delete(:conference_participant, "Conferences/#{@conference_sid}/Participants/#{@call_sid}")
54 |
55 | Twilio::Conference.kick_participant(@conference_sid, @call_sid).should eql response
56 | WebMock.should have_requested(:delete, url)
57 | end
58 | end
--------------------------------------------------------------------------------
/spec/twilio/available_phone_numbers_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Available Phone Numbers" do
4 | before(:all) do
5 | Twilio.connect('mysid', 'mytoken')
6 | end
7 |
8 | context "Local Number" do
9 | it "is searchable" do
10 | response, url = stub_get(:available_phone_numbers_local, 'AvailablePhoneNumbers/US/Local')
11 |
12 | Twilio::AvailablePhoneNumbers.search_local.should eql response
13 | WebMock.should have_requested(:get, url)
14 | end
15 |
16 | it "is searchable by area code" do
17 | response, url = stub_get(:available_phone_numbers_local_search, 'AvailablePhoneNumbers/US/Local?AreaCode=510')
18 |
19 | Twilio::AvailablePhoneNumbers.search_local(:area_code => 510).should eql response
20 | WebMock.should have_requested(:get, url)
21 | end
22 |
23 | it "is searchable by postal code" do
24 | response, url = stub_get(:available_phone_numbers_local_search, 'AvailablePhoneNumbers/US/Local?InPostalCode=94612')
25 |
26 | Twilio::AvailablePhoneNumbers.search_local(:postal_code => 94612).should eql response
27 | WebMock.should have_requested(:get, url)
28 | end
29 |
30 | it "is searchable using multiple parameters" do
31 | response, url = stub_get(:available_phone_numbers_local_search, 'AvailablePhoneNumbers/US/Local?NearLatLong=37.806940%2C-122.270360&InRateCenter=OKLD0349T&NearNumber=15105551213&Distance=50&InRegion=CA&InLata=722&Contains=510555****&Page=2&PageSize=30')
32 |
33 | Twilio::AvailablePhoneNumbers.search_local(:in_region => 'CA', :contains => '510555****', :near_lat_long => '37.806940,-122.270360', :near_number => '15105551213', :in_lata => 722, :in_rate_center => 'OKLD0349T', :distance => 50, :page => 2, :page_size => 30).should eql response
34 | WebMock.should have_requested(:get, url)
35 | end
36 | end
37 |
38 | context "Toll-free Number" do
39 | it "is searchable" do
40 | response, url = stub_get(:available_phone_numbers_toll_free, 'AvailablePhoneNumbers/US/TollFree')
41 |
42 | Twilio::AvailablePhoneNumbers.search_toll_free.should eql response
43 | WebMock.should have_requested(:get, url)
44 | end
45 |
46 | it "is searchable as a vanity number" do
47 | response, url = stub_get(:available_phone_numbers_toll_free_search, 'AvailablePhoneNumbers/US/TollFree?Contains=STORM')
48 |
49 | Twilio::AvailablePhoneNumbers.search_toll_free(:contains => 'STORM').should eql response
50 | WebMock.should have_requested(:get, url)
51 | end
52 | end
53 | end
--------------------------------------------------------------------------------
/spec/fixtures/yml/verb_responses.yml:
--------------------------------------------------------------------------------
1 | play_mp3:
2 | response: http://foo.com/cowbell.mp3
3 |
4 | play_mp3_two_times:
5 | response: http://foo.com/cowbell.mp3
6 |
7 | play_mp3_two_times_with_pause:
8 | response: http://foo.com/cowbell.mp3http://foo.com/cowbell.mp3
9 |
10 | gather:
11 | response:
12 |
13 | gather_with_action:
14 | response:
15 |
16 | gather_with_get_method:
17 | response:
18 |
19 | gather_with_timeout:
20 | response:
21 |
22 | gather_with_finish_key:
23 | response:
24 |
25 | gather_with_num_digits:
26 | response:
27 |
28 | record:
29 | response:
30 |
31 | record_with_action:
32 | response:
33 |
34 | record_with_get_method:
35 | response:
36 |
37 | record_with_timeout:
38 | response:
39 |
40 | record_with_finish_key:
41 | response:
42 |
43 | record_with_max_length:
44 | response:
45 |
46 | dial:
47 | response: 415-123-4567
48 |
49 | dial_with_action:
50 | response: 415-123-4567
51 |
52 | dial_with_get_method:
53 | response: 415-123-4567
54 |
55 | dial_with_timeout:
56 | response: 415-123-4567
57 |
58 | dial_with_hangup_on_star:
59 | response: 415-123-4567
60 |
61 | dial_with_time_limit:
62 | response: 415-123-4567
63 |
64 | dial_with_caller_id:
65 | response: 415-123-4567
66 |
67 | dial_with_redirect:
68 | response: 415-123-4567http://www.foo.com/nextInstructions
69 |
70 | dial_with_number_and_send_digits:
71 | response: 415-123-4567
72 |
73 | dial_multiple_numbers:
74 | response: 415-123-4567415-123-4568415-123-4569
75 |
76 | dial_conference:
77 | response: MyRoom
78 |
79 | dial_muted_conference:
80 | response: MyRoom
81 |
82 | hangup:
83 | response:
84 |
85 | reject:
86 | response:
87 |
--------------------------------------------------------------------------------
/spec/twilio/verb_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "Verb" do
4 |
5 | context "Say" do
6 | it "says 'hi'" do
7 | Twilio::Verb.say('hi').should match %r{hi}
8 | end
9 |
10 | it "says 'hi' with female voice" do
11 | Twilio::Verb.say('hi', :voice => 'woman').should match %r{hi}
12 | end
13 |
14 | it "says 'hola' in Spanish with female voice" do
15 | Twilio::Verb.say('hola', :voice => 'woman', :language => 'es').should match %r{hola}
16 | end
17 |
18 | it "says 'hi' three times" do
19 | Twilio::Verb.say('hi', :loop => 3).should match %r{hi}
20 | end
21 |
22 | it "says 'hi' three times with pause" do
23 | Twilio::Verb.say('hi', :loop => 3, :pause => true).should match %r{hihihi}
24 | end
25 |
26 | it "says 'hi' with pause and say 'bye'" do
27 | verb = Twilio::Verb.new { |v|
28 | v.say 'hi', :loop => 1
29 | v.pause
30 | v.say 'bye'
31 | }.response.should match %r{hibye}
32 | end
33 |
34 | it "says 'hi' with 2 second pause and say 'bye'" do
35 | verb = Twilio::Verb.new { |v|
36 | v.say 'hi'
37 | v.pause :length => 2
38 | v.say 'bye'
39 | }.response.should match %r{hibye}
40 | end
41 | end
42 |
43 | context "Play" do
44 | it "plays mp3 response" do
45 | Twilio::Verb.play('http://foo.com/cowbell.mp3').should == verb_response(:play_mp3)
46 | end
47 |
48 | it "plays mp3 response two times" do
49 | Twilio::Verb.play('http://foo.com/cowbell.mp3', :loop => 2).should == verb_response(:play_mp3_two_times)
50 | end
51 |
52 | it "plays mp3 response two times with pause" do
53 | Twilio::Verb.play('http://foo.com/cowbell.mp3', :loop => 2, :pause => true).should == verb_response(:play_mp3_two_times_with_pause)
54 | end
55 | end
56 |
57 | context "Gather" do
58 | it "gathers" do
59 | Twilio::Verb.gather.should == verb_response(:gather)
60 | end
61 |
62 | it "gathers with action" do
63 | Twilio::Verb.gather(:action => 'http://foobar.com').should == verb_response(:gather_with_action)
64 | end
65 |
66 | it "gathers with GET method" do
67 | Twilio::Verb.gather(:method => 'GET').should == verb_response(:gather_with_get_method)
68 | end
69 |
70 | it "gathers with timeout" do
71 | Twilio::Verb.gather(:timeout => 10).should == verb_response(:gather_with_timeout)
72 | end
73 |
74 | it "gathers with finish key" do
75 | Twilio::Verb.gather(:finishOnKey => '*').should == verb_response(:gather_with_finish_key)
76 | end
77 |
78 | it "gathers with num digits" do
79 | Twilio::Verb.gather(:numDigits => 5).should == verb_response(:gather_with_num_digits)
80 | end
81 |
82 | it "gathers with all options set" do
83 | Twilio::Verb.gather(:action => 'http://foobar.com',
84 | :finishOnKey => '*',
85 | :method => 'GET',
86 | :numDigits => 5,
87 | :timeout => 10).should match %r{}
88 | end
89 |
90 | it "gathers and says instructions" do
91 | verb = Twilio::Verb.new { |v|
92 | v.gather {
93 | v.say 'Please enter your account number followed by the pound sign'
94 | }
95 | v.say "We didn't receive any input. Goodbye!"
96 | }.response.should match %r{Please enter your account number followed by the pound signWe didn't receive any input. Goodbye!}
97 | end
98 |
99 | it "gathers with timeout and says instructions" do
100 | verb = Twilio::Verb.new { |v|
101 | v.gather(:timeout => 10) {
102 | v.say 'Please enter your account number followed by the pound sign'
103 | }
104 | v.say "We didn't receive any input. Goodbye!"
105 | }.response.should match %r{Please enter your account number followed by the pound signWe didn't receive any input. Goodbye!}
106 | end
107 | end
108 |
109 | context "Record" do
110 | it "records" do
111 | Twilio::Verb.record.should == verb_response(:record)
112 | end
113 |
114 | it "records with action" do
115 | Twilio::Verb.record(:action => 'http://foobar.com').should == verb_response(:record_with_action)
116 | end
117 |
118 | it "records with GET method" do
119 | Twilio::Verb.record(:method => 'GET').should == verb_response(:record_with_get_method)
120 | end
121 |
122 | it "records with timeout" do
123 | Twilio::Verb.record(:timeout => 10).should == verb_response(:record_with_timeout)
124 | end
125 |
126 | it "records with finish key" do
127 | Twilio::Verb.record(:finishOnKey => '*').should == verb_response(:record_with_finish_key)
128 | end
129 |
130 | it "records with max length" do
131 | Twilio::Verb.record(:maxLength => 1800).should == verb_response(:record_with_max_length)
132 | end
133 |
134 | it "records with transcribe" do
135 | Twilio::Verb.record(:transcribe => true, :transcribeCallback => '/handle_transcribe').should match %r{}
136 | end
137 | end
138 |
139 | context "Dial" do
140 | it "dials" do
141 | Twilio::Verb.dial('415-123-4567').should == verb_response(:dial)
142 | end
143 |
144 | it "dials with action" do
145 | Twilio::Verb.dial('415-123-4567', :action => 'http://foobar.com').should == verb_response(:dial_with_action)
146 | end
147 |
148 | it "dials with GET method" do
149 | Twilio::Verb.dial('415-123-4567', :method => 'GET').should == verb_response(:dial_with_get_method)
150 | end
151 |
152 | it "dials with timeout" do
153 | Twilio::Verb.dial('415-123-4567', :timeout => 10).should == verb_response(:dial_with_timeout)
154 | end
155 |
156 | it "dials with hangup on star" do
157 | Twilio::Verb.dial('415-123-4567', :hangupOnStar => true).should == verb_response(:dial_with_hangup_on_star)
158 | end
159 |
160 | it "dials with time limit" do
161 | Twilio::Verb.dial('415-123-4567', :timeLimit => 3600).should == verb_response(:dial_with_time_limit)
162 | end
163 |
164 | it "dials with caller id" do
165 | Twilio::Verb.dial('415-123-4567', :callerId => '858-987-6543').should == verb_response(:dial_with_caller_id)
166 | end
167 |
168 | it "dials with timeout and caller id" do
169 | Twilio::Verb.dial('415-123-4567', :timeout => 10, :callerId => '858-987-6543').should match %r{415-123-4567}
170 | end
171 |
172 | it "dials with redirect" do
173 | verb = Twilio::Verb.new { |v|
174 | v.dial '415-123-4567'
175 | v.redirect 'http://www.foo.com/nextInstructions'
176 | }.response.should == verb_response(:dial_with_redirect)
177 | end
178 |
179 | it "dials with number and send digits" do
180 | verb = Twilio::Verb.new { |v|
181 | v.dial {
182 | v.number('415-123-4567', :sendDigits => 'wwww1928')
183 | }
184 | }.response.should == verb_response(:dial_with_number_and_send_digits)
185 | end
186 |
187 | it "dials multiple numbers" do
188 | verb = Twilio::Verb.new { |v|
189 | v.dial {
190 | v.number '415-123-4567'
191 | v.number '415-123-4568'
192 | v.number '415-123-4569'
193 | }
194 | }.response.should == verb_response(:dial_multiple_numbers)
195 | end
196 |
197 | it "dials a conference" do
198 | verb = Twilio::Verb.new { |v|
199 | v.dial {
200 | v.conference 'MyRoom'
201 | }
202 | }.response.should == verb_response(:dial_conference)
203 | end
204 |
205 | it "dials a muted conference" do
206 | verb = Twilio::Verb.new { |v|
207 | v.dial {
208 | v.conference 'MyRoom', :mute => :true
209 | }
210 | }.response.should == verb_response(:dial_muted_conference)
211 | end
212 | end
213 |
214 | context "Hang Up" do
215 | it "hangs up" do
216 | Twilio::Verb.hangup.should == verb_response(:hangup)
217 | end
218 |
219 | it "says hi and hangs up" do
220 | verb = Twilio::Verb.new { |v|
221 | v.say 'hi'
222 | v.hangup
223 | }.response.should match %r{hi}
224 | end
225 | end
226 |
227 | context "Reject" do
228 | it "rejects" do
229 | Twilio::Verb.reject.should == verb_response(:reject)
230 | end
231 |
232 | it "just rejects incoming call" do
233 | verb = Twilio::Verb.new { |v|
234 | v.reject
235 | }.response.should match %r{}
236 | end
237 |
238 | it "just rejects incoming call with 'busy' status" do
239 | verb = Twilio::Verb.new { |v|
240 | v.reject :reason => 'busy'
241 | }.response.should match %r{}
242 | end
243 | end
244 |
245 | context "SMS" do
246 | it "sends a simple SMS message" do
247 | verb = Twilio::Verb.new { |v|
248 | v.sms 'Join us at the bar', :to => "8005554321", :from => "9006661111", :action => "/smsService", :method => "GET"
249 | }.response.should match %r{Join us at the bar}
250 | end
251 | end
252 |
253 | end
254 |
--------------------------------------------------------------------------------
/lib/twilio/verb.rb:
--------------------------------------------------------------------------------
1 | require 'builder'
2 |
3 | module Twilio
4 | # Twilio Verbs enable your application to respond to Twilio requests (to your app) with XML responses.
5 | # There are 5 primary verbs (say, play, gather, record, dial) and 3 secondary (hangup, pause, redirect).
6 | # Verbs can be chained and, in some cases, nested.
7 | #
8 | # If your response consists of a single verb, you can call a Verb class method:
9 | #
10 | # Twilio::Verb.say 'The time is 9:35 PM.'
11 | #
12 | # But if you need to chain several verbs together, just wrap them in an instance block and call the 'response' attribute:
13 | #
14 | # verb = Twilio::Verb.new { |v|
15 | # v.dial '415-123-4567'
16 | # v.redirect 'http://www.foo.com/nextInstructions'
17 | # }
18 | # verb.response
19 | class Verb
20 |
21 | attr_reader :response
22 |
23 | class << self
24 | def method_missing(method_id, *args) #:nodoc:
25 | v = Verb.new
26 | v.send(method_id, *args)
27 | end
28 | end
29 |
30 | def initialize(&block)
31 | @xml = Builder::XmlMarkup.new
32 | @xml.instruct!
33 |
34 | if block_given?
35 | @chain = true
36 | @response = @xml.Response { block.call(self) }
37 | end
38 | end
39 |
40 | # The Say verb converts text to speech that is read back to the caller.
41 | # Say is useful for dynamic text that is difficult to prerecord.
42 | #
43 | # Examples:
44 | # Twilio::Verb.say 'The time is 9:35 PM.'
45 | # Twilio::Verb.say 'The time is 9:35 PM.', :loop => 3
46 | #
47 | # With numbers, 12345 will be spoken as "twelve thousand three hundred forty five" while
48 | # 1 2 3 4 5 will be spoken as "one two three four five."
49 | #
50 | # Twilio::Verb.say 'Your PIN is 1234', :loop => 4
51 | # Twilio::Verb.say 'Your PIN is 1 2 3 4', :loop => 4
52 | #
53 | # If you need a longer pause between each loop, instead of explicitly calling the Pause
54 | # verb within a block, you can set the convenient pause option:
55 | #
56 | # Twilio::Verb.say 'Your PIN is 1 2 3 4', :loop => 4, :pause => true
57 | #
58 | # Options (see http://www.twilio.com/docs/api_reference/TwiML/say) are passed in as a hash:
59 | #
60 | # Twilio::Verb.say 'The time is 9:35 PM.', :voice => 'woman'
61 | # Twilio::Verb.say 'The time is 9:35 PM.', :voice => 'woman', :language => 'es'
62 | def say(*args)
63 | options = {:voice => 'man', :language => 'en', :loop => 1}
64 | args.each do |arg|
65 | case arg
66 | when String
67 | options[:text_to_speak] = arg
68 | when Hash
69 | options.merge!(arg)
70 | else
71 | raise ArgumentError, 'say expects String or Hash argument'
72 | end
73 | end
74 |
75 | output {
76 | if options[:pause]
77 | loop_with_pause(options[:loop], @xml) do
78 | @xml.Say(options[:text_to_speak], :voice => options[:voice], :language => options[:language])
79 | end
80 | else
81 | @xml.Say(options[:text_to_speak], :voice => options[:voice], :language => options[:language], :loop => options[:loop])
82 | end
83 | }
84 | end
85 |
86 | # The Play verb plays an audio URL back to the caller.
87 | # Examples:
88 | # Twilio::Verb.play 'http://foo.com/cowbell.mp3'
89 | # Twilio::Verb.play 'http://foo.com/cowbell.mp3', :loop => 3
90 | #
91 | # If you need a longer pause between each loop, instead of explicitly calling the Pause
92 | # verb within a block, you can set the convenient pause option:
93 | #
94 | # Twilio::Verb.play 'http://foo.com/cowbell.mp3', :loop => 3, :pause => true
95 | #
96 | # Options (see http://www.twilio.com/docs/api_reference/TwiML/play) are passed in as a hash,
97 | # but only 'loop' is currently supported.
98 | def play(*args)
99 | options = {:loop => 1}
100 | args.each do |arg|
101 | case arg
102 | when String
103 | options[:audio_url] = arg
104 | when Hash
105 | options.merge!(arg)
106 | else
107 | raise ArgumentError, 'play expects String or Hash argument'
108 | end
109 | end
110 |
111 | output {
112 | if options[:pause]
113 | loop_with_pause(options[:loop], @xml) do
114 | @xml.Play(options[:audio_url])
115 | end
116 | else
117 | @xml.Play(options[:audio_url], :loop => options[:loop])
118 | end
119 | }
120 | end
121 |
122 | # The Gather verb collects digits entered by a caller into their telephone keypad.
123 | # When the caller is done entering data, Twilio submits that data to a provided URL,
124 | # as either a HTTP GET or POST request, just like a web browser submits data from an HTML form.
125 | #
126 | # Options (see http://www.twilio.com/docs/api_reference/TwiML/gather) are passed in as a hash
127 | #
128 | # Examples:
129 | # Twilio::Verb.gather
130 | # Twilio::Verb.gather :action => 'http://foobar.com'
131 | # Twilio::Verb.gather :finishOnKey => '*'
132 | # Twilio::Verb.gather :action => 'http://foobar.com', :finishOnKey => '*'
133 | #
134 | # Gather also lets you nest the Play, Say, and Pause verbs:
135 | #
136 | # verb = Twilio::Verb.new { |v|
137 | # v.gather(:action => '/process_gather', :method => 'GET) {
138 | # v.say 'Please enter your account number followed by the pound sign'
139 | # }
140 | # v.say "We didn't receive any input. Goodbye!"
141 | # }
142 | # verb.response # represents the final xml output
143 | def gather(*args, &block)
144 | options = args.shift || {}
145 | output {
146 | if block_given?
147 | @xml.Gather(options) { block.call}
148 | else
149 | @xml.Gather(options)
150 | end
151 | }
152 | end
153 |
154 | #play, say, pause
155 |
156 | # The Record verb records the caller's voice and returns a URL that links to a file
157 | # containing the audio recording.
158 | #
159 | # Options (see http://www.twilio.com/docs/api_reference/TwiML/record) are passed in as a hash
160 | #
161 | # Examples:
162 | # Twilio::Verb.record
163 | # Twilio::Verb.record :action => 'http://foobar.com'
164 | # Twilio::Verb.record :finishOnKey => '*'
165 | # Twilio::Verb.record :transcribe => true, :transcribeCallback => '/handle_transcribe'
166 | def record(*args)
167 | options = args.shift
168 | output { @xml.Record(options) }
169 | end
170 |
171 | # The Dial verb connects the current caller to an another phone. If the called party picks up,
172 | # the two parties are connected and can communicate until one hangs up. If the called party does
173 | # not pick up, if a busy signal is received, or the number doesn't exist, the dial verb will finish.
174 | #
175 | # If an action verb is provided, Twilio will submit the outcome of the call attempt to the action URL.
176 | # If no action is provided, Dial will fall through to the next verb in the document.
177 | #
178 | # Note: this is different than the behavior of Record and Gather. Dial does not submit back to the
179 | # current document URL if no action is provided.
180 | #
181 | # Options (see http://www.twilio.com/docs/api_reference/TwiML/dial) are passed in as a hash
182 | #
183 | # Examples:
184 | # Twilio::Verb.dial '415-123-4567'
185 | # Twilio::Verb.dial '415-123-4567', :action => 'http://foobar.com'
186 | # Twilio::Verb.dial '415-123-4567', :timeout => 10, :callerId => '858-987-6543'
187 | #
188 | # Twilio also supports an alternate form in which a Number object is nested inside Dial:
189 | #
190 | # verb = Twilio::Verb.new { |v|
191 | # v.dial { |v|
192 | # v.number '415-123-4567'
193 | # v.number '415-123-4568'
194 | # v.number '415-123-4569'
195 | # }
196 | # }
197 | # verb.response # represents the final xml output
198 | def dial(*args, &block)
199 | number_to_dial = ''
200 | options = {}
201 | args.each do |arg|
202 | case arg
203 | when String
204 | number_to_dial = arg
205 | when Hash
206 | options.merge!(arg)
207 | else
208 | raise ArgumentError, 'dial expects String or Hash argument'
209 | end
210 | end
211 |
212 | output {
213 | if block_given?
214 | @xml.Dial(options) { block.call }
215 | else
216 | @xml.Dial(number_to_dial, options)
217 | end
218 | }
219 | end
220 |
221 | # The verb's noun allows you to connect to a conference room.
222 | # Much like how the noun allows you to connect to another phone number,
223 | # the noun allows you to connect to a named conference room and talk
224 | # with the other callers who have also connected to that room.
225 | #
226 | # Options (see http://www.twilio.com/docs/api_reference/TwiML/conference) are passed in as a hash
227 | #
228 | # Examples:
229 | # verb = Twilio::Verb.new { |v|
230 | # v.dial {
231 | # v.conference 'MyRoom', :muted => true
232 | # }
233 | # }
234 | # verb.response
235 | def conference(*args)
236 | conference_name = ''
237 | options = {}
238 | args.each do |arg|
239 | case arg
240 | when String
241 | conference_name = arg
242 | when Hash
243 | options.merge!(arg)
244 | else
245 | raise ArgumentError, 'conference expects String or Hash argument'
246 | end
247 | end
248 |
249 | output { @xml.Conference(conference_name, options)}
250 | end
251 |
252 | # The Pause (secondary) verb waits silently for a number of seconds.
253 | # It is normally chained with other verbs.
254 | #
255 | # Options (see http://www.twilio.com/docs/api_reference/TwiML/pause) are passed in as a hash
256 | #
257 | # Examples:
258 | # verb = Twilio::Verb.new { |v|
259 | # v.say 'greetings'
260 | # v.pause :length => 2
261 | # v.say 'have a nice day'
262 | # }
263 | # verb.response
264 | def pause(*args)
265 | options = args.shift
266 | output { @xml.Pause(options) }
267 | end
268 |
269 | # The Redirect (secondary) verb transfers control to a different URL.
270 | # It is normally chained with other verbs.
271 | #
272 | # Options (see http://www.twilio.com/docs/api_reference/TwiML/redirect) are passed in as a hash
273 | #
274 | # Examples:
275 | # verb = Twilio::Verb.new { |v|
276 | # v.dial '415-123-4567'
277 | # v.redirect 'http://www.foo.com/nextInstructions'
278 | # }
279 | # verb.response
280 | def redirect(*args)
281 | redirect_to_url = ''
282 | options = {}
283 | args.each do |arg|
284 | case arg
285 | when String
286 | redirect_to_url = arg
287 | when Hash
288 | options.merge!(arg)
289 | else
290 | raise ArgumentError, 'dial expects String or Hash argument'
291 | end
292 | end
293 |
294 | output { @xml.Redirect(redirect_to_url, options) }
295 | end
296 |
297 | # The verb sends a SMS message to a phone number.
298 | #
299 | # Options (see http://www.twilio.com/docs/api/2008-08-01/twiml/sms/sms) ars passed in as a hash
300 | #
301 | # Examples:
302 | # verb = Twilio::Verb.new { |v|
303 | # v.sms 'Meet at South Street'
304 | # }
305 | #
306 | def sms(*args)
307 | message = ''
308 | options = {}
309 | args.each do |arg|
310 | case arg
311 | when String
312 | message = arg
313 | when Hash
314 | options.merge!(arg)
315 | else
316 | raise ArgumentError, 'sms expects STring or Hash argument'
317 | end
318 | end
319 |
320 | output { @xml.Sms(message, options) }
321 | end
322 |
323 | # The Hangup (secondary) verb ends the call.
324 | #
325 | # Examples:
326 | # If your response is only a hangup:
327 | #
328 | # Twilio::Verb.hangup
329 | #
330 | # If your response is chained:
331 | #
332 | # verb = Twilio::Verb.new { |v|
333 | # v.say "The time is #{Time.now}"
334 | # v.hangup
335 | # }
336 | # verb.response
337 | def hangup
338 | output { @xml.Hangup }
339 | end
340 |
341 | # The Number element specifies a phone number. The number element has two optional attributes: sendDigits and url.
342 | # Number elements can only be nested in Dial verbs
343 | def number(*args)
344 | number_to_dial = ''
345 | options = {}
346 | args.each do |arg|
347 | case arg
348 | when String
349 | number_to_dial = arg
350 | when Hash
351 | options.merge!(arg)
352 | else
353 | raise ArgumentError, 'dial expects String or Hash argument'
354 | end
355 | end
356 |
357 | output { @xml.Number(number_to_dial, options) }
358 | end
359 |
360 |
361 | # The Reject verb rejects an incoming call to your Twilio number without billing you
362 | # (see http://www.twilio.com/docs/api/twiml/reject)
363 | # Examples:
364 | #
365 | # Twilio::Verb.reject
366 | #
367 | # If reject is called with an argument:
368 | #
369 | # Twilio::Verb.reject :reason => "busy"
370 | #
371 | def reject options = {}
372 | output { @xml.Reject(options) }
373 | end
374 |
375 | private
376 |
377 | def output
378 | @chain ? yield : @xml.Response { yield }
379 | end
380 |
381 | def loop_with_pause(loop_count, xml, &verb_action)
382 | last_iteration = loop_count-1
383 | loop_count.times do |i|
384 | yield verb_action
385 | xml.Pause unless i == last_iteration
386 | end
387 | end
388 | end
389 | end
390 |
--------------------------------------------------------------------------------