twilio-ruby 3.11.5 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +13 -5
- data/.gitignore +1 -1
- data/.travis.yml +20 -9
- data/AUTHORS.md +32 -25
- data/CHANGES.md +203 -0
- data/Gemfile +8 -1
- data/{LICENSE → LICENSE.md} +4 -2
- data/Makefile +1 -2
- data/README.md +70 -39
- data/Rakefile +8 -10
- data/docs/faq.rst +3 -3
- data/docs/getting-started.rst +17 -12
- data/docs/index.rst +15 -0
- data/docs/usage/accounts.rst +6 -6
- data/docs/usage/addresses.rst +101 -0
- data/docs/usage/applications.rst +10 -10
- data/docs/usage/basics.rst +17 -4
- data/docs/usage/caller-ids.rst +4 -2
- data/docs/usage/conferences.rst +11 -11
- data/docs/usage/errors.rst +7 -7
- data/docs/usage/messages.rst +48 -20
- data/docs/usage/notifications.rst +6 -4
- data/docs/usage/phone-calls.rst +33 -14
- data/docs/usage/phone-numbers.rst +45 -31
- data/docs/usage/queues.rst +8 -8
- data/docs/usage/recordings.rst +12 -10
- data/docs/usage/sip.rst +15 -14
- data/docs/usage/taskrouter-tokens.rst +98 -0
- data/docs/usage/taskrouter.rst +226 -0
- data/docs/usage/token-generation.rst +19 -4
- data/docs/usage/transcriptions.rst +3 -2
- data/docs/usage/twiml.rst +7 -7
- data/docs/usage/validation.rst +39 -3
- data/examples/examples.rb +44 -20
- data/examples/print-call-log.rb +1 -1
- data/lib/rack/twilio_webhook_authentication.rb +47 -0
- data/lib/twilio-ruby/rest/accounts.rb +2 -1
- data/lib/twilio-ruby/rest/addresses/dependent_phone_numbers.rb +6 -0
- data/lib/twilio-ruby/rest/addresses.rb +12 -0
- data/lib/twilio-ruby/rest/base_client.rb +127 -0
- data/lib/twilio-ruby/rest/call_feedback.rb +28 -0
- data/lib/twilio-ruby/rest/call_feedback_summary.rb +13 -0
- data/lib/twilio-ruby/rest/calls.rb +10 -5
- data/lib/twilio-ruby/rest/client.rb +44 -109
- data/lib/twilio-ruby/rest/conferences/participants.rb +2 -2
- data/lib/twilio-ruby/rest/incoming_phone_numbers.rb +1 -1
- data/lib/twilio-ruby/rest/instance_resource.rb +2 -16
- data/lib/twilio-ruby/rest/list_resource.rb +20 -30
- data/lib/twilio-ruby/rest/lookups/phone_numbers.rb +17 -0
- data/lib/twilio-ruby/rest/lookups_client.rb +99 -0
- data/lib/twilio-ruby/rest/messages.rb +5 -0
- data/lib/twilio-ruby/rest/next_gen_list_resource.rb +36 -0
- data/lib/twilio-ruby/rest/outgoing_caller_ids.rb +1 -1
- data/lib/twilio-ruby/rest/queues/members.rb +1 -1
- data/lib/twilio-ruby/rest/sip.rb +1 -3
- data/lib/twilio-ruby/rest/sms/messages.rb +23 -0
- data/lib/twilio-ruby/rest/task_router/activities.rb +8 -0
- data/lib/twilio-ruby/rest/task_router/events.rb +8 -0
- data/lib/twilio-ruby/rest/task_router/reservations.rb +8 -0
- data/lib/twilio-ruby/rest/task_router/statistics.rb +26 -0
- data/lib/twilio-ruby/rest/task_router/task_queues.rb +17 -0
- data/lib/twilio-ruby/rest/task_router/task_queues_statistics.rb +15 -0
- data/lib/twilio-ruby/rest/task_router/tasks.rb +15 -0
- data/lib/twilio-ruby/rest/task_router/workers.rb +13 -0
- data/lib/twilio-ruby/rest/task_router/workers_statistics.rb +8 -0
- data/lib/twilio-ruby/rest/task_router/workflow_statistics.rb +7 -0
- data/lib/twilio-ruby/rest/task_router/workflows.rb +11 -0
- data/lib/twilio-ruby/rest/task_router/workspace_statistics.rb +7 -0
- data/lib/twilio-ruby/rest/task_router/workspaces.rb +17 -0
- data/lib/twilio-ruby/rest/task_router_client.rb +176 -0
- data/lib/twilio-ruby/rest/tokens.rb +7 -0
- data/lib/twilio-ruby/rest/usage/records.rb +2 -2
- data/lib/twilio-ruby/rest/utils.rb +35 -11
- data/lib/twilio-ruby/task_router/capability.rb +87 -0
- data/lib/twilio-ruby/task_router.rb +0 -0
- data/lib/twilio-ruby/twiml/response.rb +1 -0
- data/lib/twilio-ruby/util/capability.rb +10 -7
- data/lib/twilio-ruby/util/client_config.rb +29 -0
- data/lib/twilio-ruby/util/configuration.rb +7 -0
- data/lib/twilio-ruby/util/request_validator.rb +18 -3
- data/lib/twilio-ruby/version.rb +1 -1
- data/lib/twilio-ruby.rb +48 -0
- data/spec/rack/twilio_webhook_authentication_spec.rb +110 -0
- data/spec/rest/account_spec.rb +51 -20
- data/spec/rest/address_spec.rb +11 -0
- data/spec/rest/call_feedback_spec.rb +12 -0
- data/spec/rest/call_feedback_summary_spec.rb +9 -0
- data/spec/rest/call_spec.rb +8 -4
- data/spec/rest/client_spec.rb +209 -51
- data/spec/rest/conference_spec.rb +4 -2
- data/spec/rest/instance_resource_spec.rb +4 -4
- data/spec/rest/lookups/phone_number_spec.rb +8 -0
- data/spec/rest/message_spec.rb +2 -2
- data/spec/rest/numbers_spec.rb +25 -13
- data/spec/rest/queue_spec.rb +4 -2
- data/spec/rest/recording_spec.rb +4 -2
- data/spec/rest/sms/message_spec.rb +37 -0
- data/spec/rest/sms/messages_spec.rb +31 -0
- data/spec/rest/task_router/reservation_spec.rb +9 -0
- data/spec/rest/task_router/task_queue_spec.rb +9 -0
- data/spec/rest/token_spec.rb +7 -0
- data/spec/rest/utils_spec.rb +45 -0
- data/spec/spec_helper.rb +12 -3
- data/spec/support/fakeweb.rb +2 -0
- data/spec/task_router_spec.rb +114 -0
- data/spec/twilio_spec.rb +15 -0
- data/spec/util/capability_spec.rb +167 -118
- data/spec/util/client_config_spec.rb +21 -0
- data/spec/util/configuration_spec.rb +15 -0
- data/spec/util/request_validator_spec.rb +31 -3
- data/spec/util/url_encode_spec.rb +2 -2
- data/twilio-ruby.gemspec +28 -27
- metadata +93 -71
- data/CHANGES +0 -47
data/docs/usage/twiml.rst
CHANGED
|
@@ -18,7 +18,7 @@ which returns raw TwiML.
|
|
|
18
18
|
require 'twilio-ruby'
|
|
19
19
|
|
|
20
20
|
Twilio::TwiML::Response.new do |r|
|
|
21
|
-
|
|
21
|
+
r.Say "Hello"
|
|
22
22
|
end.text
|
|
23
23
|
|
|
24
24
|
.. code-block:: xml
|
|
@@ -35,14 +35,14 @@ All attributes are keyword arguments.
|
|
|
35
35
|
require 'twilio-ruby'
|
|
36
36
|
|
|
37
37
|
Twilio::TwiML::Response.new do |r|
|
|
38
|
-
|
|
38
|
+
r.Play "https://api.twilio.com/cowbell.mp3", loop: 5
|
|
39
39
|
end.text
|
|
40
40
|
|
|
41
41
|
.. code-block:: xml
|
|
42
42
|
|
|
43
43
|
<?xml version="1.0" encoding="utf-8"?>
|
|
44
44
|
<Response>
|
|
45
|
-
|
|
45
|
+
<Play loop="3">https://api.twilio.com/cowbell.mp3</Play>
|
|
46
46
|
<Response>
|
|
47
47
|
|
|
48
48
|
Any example of nesting nouns in verbs
|
|
@@ -52,10 +52,10 @@ Any example of nesting nouns in verbs
|
|
|
52
52
|
require 'twilio-ruby'
|
|
53
53
|
|
|
54
54
|
Twilio::TwiML::Response.new do |r|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
r.Say "hello"
|
|
56
|
+
r.Gather finishOnKey: => 4 do |g|
|
|
57
|
+
g.Say "world"
|
|
58
|
+
end
|
|
59
59
|
end.text
|
|
60
60
|
|
|
61
61
|
which returns the following
|
data/docs/usage/validation.rst
CHANGED
|
@@ -48,12 +48,11 @@ actually from Twilio.
|
|
|
48
48
|
signature = "HpS7PBa1Agvt4OtO+wZp75IuQa0=" # will look something like that
|
|
49
49
|
|
|
50
50
|
if @validator.validate(url, post_vars, signature)
|
|
51
|
-
|
|
51
|
+
puts "Confirmed to have come from Twilio."
|
|
52
52
|
else
|
|
53
|
-
|
|
53
|
+
puts "NOT VALID. It might have been spoofed!"
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
|
|
57
56
|
Trailing Slashes
|
|
58
57
|
==================
|
|
59
58
|
|
|
@@ -69,3 +68,40 @@ https://mycompany.com/twilio and you may have built the hash using
|
|
|
69
68
|
https://mycompany.com/twilio/. More information can be found in our
|
|
70
69
|
documentation on validating requests.
|
|
71
70
|
|
|
71
|
+
Rack Middleware
|
|
72
|
+
===============
|
|
73
|
+
|
|
74
|
+
If you are serving up your site using a Rack based framework, such as Sinatra or
|
|
75
|
+
Rails, you can use the Rack middleware that is included in the gem to protect
|
|
76
|
+
from spoofing attempts.
|
|
77
|
+
|
|
78
|
+
To use the middleware, you need to set it up with your Twilio Auth Token and a
|
|
79
|
+
set of paths to watch. For example, here is how you would use the middleware in
|
|
80
|
+
a Sinatra application:
|
|
81
|
+
|
|
82
|
+
.. code-block:: ruby
|
|
83
|
+
|
|
84
|
+
require 'sinatra'
|
|
85
|
+
require 'twilio-ruby'
|
|
86
|
+
|
|
87
|
+
auth_token = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
|
|
88
|
+
|
|
89
|
+
use Rack::TwilioWebhookAuthentication, auth_token, /\/messages/
|
|
90
|
+
|
|
91
|
+
post '/messages' do
|
|
92
|
+
# response with TwiML
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
Now, any POST request to /messages in your application that doesn't validate as
|
|
96
|
+
a Twilio request, will automatically respond with a 403 status code and your
|
|
97
|
+
action will not be hit.
|
|
98
|
+
|
|
99
|
+
If you use subaccounts and need to validate with different auth tokens, you can pass a block to the middleware instead of an auth token. The block will be passed the Account Sid making the call.
|
|
100
|
+
|
|
101
|
+
.. code-block:: ruby
|
|
102
|
+
|
|
103
|
+
use Rack::TwilioWebhookAuthentication, nil, /\/messages/ do |account_sid|
|
|
104
|
+
# lookup auth_token from account_sid
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
Ensure you pass `nil` for the auth_token when passing a block, otherwise the block will not be called.
|
data/examples/examples.rb
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
################ ACCOUNTS ################
|
|
9
9
|
|
|
10
10
|
# shortcut to grab your account object (account_sid is inferred from the client's auth credentials)
|
|
11
|
-
@account = @client.account
|
|
11
|
+
@account = @client.account
|
|
12
12
|
|
|
13
13
|
# list your (sub)accounts
|
|
14
14
|
@client.accounts.list
|
|
@@ -19,12 +19,16 @@
|
|
|
19
19
|
puts @account.friendly_name
|
|
20
20
|
|
|
21
21
|
# update an account's friendly name
|
|
22
|
-
@client.accounts.get(@account_sid).update(:
|
|
22
|
+
@client.accounts.get(@account_sid).update(friendly_name: 'A Fabulous Friendly Name')
|
|
23
23
|
|
|
24
24
|
################ CALLS ################
|
|
25
25
|
|
|
26
26
|
# print a list of calls (all parameters optional)
|
|
27
|
-
@account.calls.list(
|
|
27
|
+
@account.calls.list(
|
|
28
|
+
page: 0,
|
|
29
|
+
page_size: 1000,
|
|
30
|
+
start_time: '2010-09-01'
|
|
31
|
+
).each do |call|
|
|
28
32
|
puts call.sid
|
|
29
33
|
end
|
|
30
34
|
|
|
@@ -34,18 +38,23 @@ end
|
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
# make a new outgoing call. returns a call object just like calls.get
|
|
37
|
-
@call = @account.calls.create(
|
|
41
|
+
@call = @account.calls.create(
|
|
42
|
+
from: '+14159341234',
|
|
43
|
+
to: '+18004567890',
|
|
44
|
+
url: 'http://example.com/call-handler'
|
|
45
|
+
)
|
|
38
46
|
|
|
39
47
|
# cancel the call if not already in progress
|
|
40
|
-
@account.calls.get(@call.sid).update(
|
|
48
|
+
@account.calls.get(@call.sid).update(status: 'canceled')
|
|
41
49
|
# or equivalently
|
|
42
|
-
@call.update(
|
|
50
|
+
@call.update(status: 'canceled')
|
|
43
51
|
# or simply
|
|
44
52
|
@call.cancel
|
|
45
53
|
|
|
46
54
|
# redirect and then terminate a call
|
|
47
|
-
@account.calls.get('CA386025c9bf5d6052a1d1ea42b4d16662')
|
|
48
|
-
@
|
|
55
|
+
@call = @account.calls.get('CA386025c9bf5d6052a1d1ea42b4d16662')
|
|
56
|
+
@call.update(url: 'http://example.com/call-redirect')
|
|
57
|
+
@call.update(status: 'completed')
|
|
49
58
|
# or, use the aliases...
|
|
50
59
|
@call.redirect_to('http://example.com/call-redirect')
|
|
51
60
|
@call.hangup
|
|
@@ -53,7 +62,7 @@ end
|
|
|
53
62
|
################ SMS MESSAGES ################
|
|
54
63
|
|
|
55
64
|
# print a list of messages
|
|
56
|
-
@account.messages.list(
|
|
65
|
+
@account.messages.list(date_sent: '2010-09-01').each do |message|
|
|
57
66
|
puts message.body
|
|
58
67
|
end
|
|
59
68
|
|
|
@@ -61,10 +70,18 @@ end
|
|
|
61
70
|
puts @account.messages.get('SMXXXXXXXX').body
|
|
62
71
|
|
|
63
72
|
# send an sms
|
|
64
|
-
@account.messages.create(
|
|
73
|
+
@account.messages.create(
|
|
74
|
+
from: '+14159341234',
|
|
75
|
+
to: '+16105557069',
|
|
76
|
+
body: 'Hey there!'
|
|
77
|
+
)
|
|
65
78
|
|
|
66
79
|
# send an mms
|
|
67
|
-
@account.messages.create(
|
|
80
|
+
@account.messages.create(
|
|
81
|
+
from: '+14159341234',
|
|
82
|
+
to: '+16105557069',
|
|
83
|
+
media_urls: 'http://example.com/media.png'
|
|
84
|
+
)
|
|
68
85
|
|
|
69
86
|
################ PHONE NUMBERS ################
|
|
70
87
|
|
|
@@ -72,22 +89,28 @@ puts @account.messages.get('SMXXXXXXXX').body
|
|
|
72
89
|
@account.available_phone_numbers.list
|
|
73
90
|
|
|
74
91
|
# print some available numbers
|
|
75
|
-
@numbers = @account.available_phone_numbers.get('US').local.list(
|
|
76
|
-
|
|
92
|
+
@numbers = @account.available_phone_numbers.get('US').local.list(
|
|
93
|
+
contains: 'AWESOME'
|
|
94
|
+
)
|
|
95
|
+
@numbers.each { |num| puts num.phone_number }
|
|
77
96
|
|
|
78
97
|
# buy the first one
|
|
79
|
-
@account.incoming_phone_numbers.create(:
|
|
98
|
+
@account.incoming_phone_numbers.create(phone_number: @numbers[0].phone_number)
|
|
80
99
|
|
|
81
100
|
# update an existing phone number's voice url
|
|
82
|
-
@account.incoming_phone_numbers.get('PNdba508c5616a7f5e141789f44f022cc3')
|
|
101
|
+
number = @account.incoming_phone_numbers.get('PNdba508c5616a7f5e141789f44f022cc3')
|
|
102
|
+
number.update(voice_url: 'http://example.com/voice')
|
|
83
103
|
|
|
84
104
|
# decommission an existing phone number
|
|
85
|
-
numbers = @account.incoming_phone_numbers.list(
|
|
105
|
+
numbers = @account.incoming_phone_numbers.list(
|
|
106
|
+
friendly_name: 'A Fabulous Friendly Name'
|
|
107
|
+
)
|
|
86
108
|
numbers[0].delete
|
|
87
109
|
################ CONFERENCES ################
|
|
88
110
|
|
|
89
111
|
# get a particular conference's participants object and stash it
|
|
90
|
-
|
|
112
|
+
conference = @account.conferences.get('CFbbe46ff1274e283f7e3ac1df0072ab39')
|
|
113
|
+
@participants = conference.participants
|
|
91
114
|
|
|
92
115
|
# list participants
|
|
93
116
|
@participants.list.each do |p|
|
|
@@ -95,17 +118,18 @@ numbers[0].delete
|
|
|
95
118
|
end
|
|
96
119
|
|
|
97
120
|
# update a conference participant
|
|
98
|
-
@participants.get('CA386025c9bf5d6052a1d1ea42b4d16662').update(
|
|
121
|
+
@participants.get('CA386025c9bf5d6052a1d1ea42b4d16662').update(muted: 'true')
|
|
99
122
|
# or an easier way
|
|
100
123
|
@participants.get('CA386025c9bf5d6052a1d1ea42b4d16662').mute
|
|
101
124
|
|
|
102
125
|
# and, since we're lazy loading, this would only incur one http request
|
|
103
|
-
@account.conferences.get('CFbbe46ff1274e283f7e3ac1df0072ab39').participants
|
|
126
|
+
@account.conferences.get('CFbbe46ff1274e283f7e3ac1df0072ab39').participants
|
|
127
|
+
.get('CA386025c9bf5d6052a1d1ea42b4d16662').update(muted: 'true')
|
|
104
128
|
|
|
105
129
|
################ QUEUES ###################
|
|
106
130
|
|
|
107
131
|
# create a new queue
|
|
108
|
-
@queue = @account.queues.create(:
|
|
132
|
+
@queue = @account.queues.create(friendly_name: 'MyQueue', max_size: 50)
|
|
109
133
|
|
|
110
134
|
# get a list of queues for this account
|
|
111
135
|
@queues = @account.queues.list
|
data/examples/print-call-log.rb
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Rack
|
|
2
|
+
# Middleware that authenticates webhooks from Twilio using the request
|
|
3
|
+
# validator.
|
|
4
|
+
#
|
|
5
|
+
# The middleware takes an auth token with which to set up the request
|
|
6
|
+
# validator and any number of paths. When a path matches the incoming request
|
|
7
|
+
# path, the request will be checked for authentication.
|
|
8
|
+
#
|
|
9
|
+
# Example:
|
|
10
|
+
#
|
|
11
|
+
# require 'rack'
|
|
12
|
+
# use Rack::TwilioWebhookAuthentication, ENV['AUTH_TOKEN'], /\/messages/
|
|
13
|
+
#
|
|
14
|
+
# The above appends this middleware to the stack, using an auth token saved in
|
|
15
|
+
# the ENV and only against paths that match /\/messages/. If the request
|
|
16
|
+
# validates then it gets passed on to the action as normal. If the request
|
|
17
|
+
# doesn't validate then the middleware responds immediately with a 403 status.
|
|
18
|
+
|
|
19
|
+
class TwilioWebhookAuthentication
|
|
20
|
+
def initialize(app, auth_token, *paths, &auth_token_lookup)
|
|
21
|
+
@app = app
|
|
22
|
+
@auth_token = auth_token
|
|
23
|
+
define_singleton_method(:get_auth_token, auth_token_lookup) if block_given?
|
|
24
|
+
@path_regex = Regexp.union(paths)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call(env)
|
|
28
|
+
return @app.call(env) unless env["PATH_INFO"].match(@path_regex)
|
|
29
|
+
request = Rack::Request.new(env)
|
|
30
|
+
original_url = request.url
|
|
31
|
+
params = request.post? ? request.POST : {}
|
|
32
|
+
auth_token = @auth_token || get_auth_token(params['AccountSid'])
|
|
33
|
+
validator = Twilio::Util::RequestValidator.new(auth_token)
|
|
34
|
+
signature = env['HTTP_X_TWILIO_SIGNATURE'] || ""
|
|
35
|
+
if validator.validate(original_url, params, signature)
|
|
36
|
+
@app.call(env)
|
|
37
|
+
else
|
|
38
|
+
[
|
|
39
|
+
403,
|
|
40
|
+
{'Content-Type' => 'text/plain'},
|
|
41
|
+
["Twilio Request Validation Failed."]
|
|
42
|
+
]
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
@@ -8,7 +8,8 @@ module Twilio
|
|
|
8
8
|
resource :sandbox, :available_phone_numbers, :incoming_phone_numbers,
|
|
9
9
|
:calls, :outgoing_caller_ids, :conferences, :sms, :recordings,
|
|
10
10
|
:transcriptions, :notifications, :applications, :connect_apps,
|
|
11
|
-
:authorized_connect_apps, :queues, :usage, :messages, :media, :sip
|
|
11
|
+
:authorized_connect_apps, :queues, :usage, :messages, :media, :sip,
|
|
12
|
+
:tokens, :addresses
|
|
12
13
|
end
|
|
13
14
|
end
|
|
14
15
|
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module Twilio
|
|
2
|
+
module REST
|
|
3
|
+
class BaseClient
|
|
4
|
+
include Twilio::Util
|
|
5
|
+
include Twilio::REST::Utils
|
|
6
|
+
|
|
7
|
+
HTTP_HEADERS = {
|
|
8
|
+
'Accept' => 'application/json',
|
|
9
|
+
'Accept-Charset' => 'utf-8',
|
|
10
|
+
'User-Agent' => "twilio-ruby/#{Twilio::VERSION}" \
|
|
11
|
+
" (#{RUBY_ENGINE}/#{RUBY_PLATFORM}" \
|
|
12
|
+
" #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Override the default host for a REST Client (api.twilio.com)
|
|
17
|
+
def self.host(host=nil)
|
|
18
|
+
return @host unless host
|
|
19
|
+
@host = host
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
attr_reader :account_sid, :last_request, :last_response
|
|
23
|
+
|
|
24
|
+
def initialize(*args)
|
|
25
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
26
|
+
options[:host] ||= self.class.host
|
|
27
|
+
@config = Twilio::Util::ClientConfig.new options
|
|
28
|
+
|
|
29
|
+
@account_sid = args[0] || Twilio.account_sid
|
|
30
|
+
@auth_token = args[1] || Twilio.auth_token
|
|
31
|
+
if @account_sid.nil? || @auth_token.nil?
|
|
32
|
+
raise ArgumentError, 'Account SID and auth token are required'
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
set_up_connection
|
|
36
|
+
set_up_subresources
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
##
|
|
40
|
+
# Define #get, #put, #post and #delete helper methods for sending HTTP
|
|
41
|
+
# requests to Twilio. You shouldn't need to use these methods directly,
|
|
42
|
+
# but they can be useful for debugging. Each method returns a hash
|
|
43
|
+
# obtained from parsing the JSON object in the response body.
|
|
44
|
+
[:get, :put, :post, :delete].each do |method|
|
|
45
|
+
method_class = Net::HTTP.const_get method.to_s.capitalize
|
|
46
|
+
define_method method do |path, *args|
|
|
47
|
+
params = twilify(args[0])
|
|
48
|
+
params = {} if params.empty?
|
|
49
|
+
# build the full path unless already given
|
|
50
|
+
path = build_full_path(path, params, method) unless args[1]
|
|
51
|
+
request = method_class.new(path, HTTP_HEADERS)
|
|
52
|
+
request.basic_auth(@account_sid, @auth_token)
|
|
53
|
+
request.form_data = params if [:post, :put].include?(method)
|
|
54
|
+
connect_and_send(request)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
protected
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
# Builds up full request path
|
|
62
|
+
# Needs implementation in child classes
|
|
63
|
+
def build_full_path(path, params, method)
|
|
64
|
+
raise NotImplementedError
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
##
|
|
68
|
+
# Set up and cache a Net::HTTP object to use when making requests. This is
|
|
69
|
+
# a private method documented for completeness.
|
|
70
|
+
def set_up_connection # :doc:
|
|
71
|
+
connection_class = Net::HTTP::Proxy @config.proxy_addr,
|
|
72
|
+
@config.proxy_port, @config.proxy_user, @config.proxy_pass
|
|
73
|
+
@connection = connection_class.new @config.host, @config.port
|
|
74
|
+
set_up_ssl
|
|
75
|
+
@connection.open_timeout = @config.timeout
|
|
76
|
+
@connection.read_timeout = @config.timeout
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
##
|
|
80
|
+
# Set up the ssl properties of the <tt>@connection</tt> Net::HTTP object.
|
|
81
|
+
# This is a private method documented for completeness.
|
|
82
|
+
def set_up_ssl # :doc:
|
|
83
|
+
@connection.use_ssl = @config.use_ssl
|
|
84
|
+
if @config.ssl_verify_peer
|
|
85
|
+
@connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
86
|
+
@connection.ca_file = @config.ssl_ca_file
|
|
87
|
+
else
|
|
88
|
+
@connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
##
|
|
93
|
+
# Set up sub resources attributes.
|
|
94
|
+
def set_up_subresources # :doc:
|
|
95
|
+
# To be overridden
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
##
|
|
99
|
+
# Send an HTTP request using the cached <tt>@connection</tt> object and
|
|
100
|
+
# return the JSON response body parsed into a hash. Also save the raw
|
|
101
|
+
# Net::HTTP::Request and Net::HTTP::Response objects as
|
|
102
|
+
# <tt>@last_request</tt> and <tt>@last_response</tt> to allow for
|
|
103
|
+
# inspection later.
|
|
104
|
+
def connect_and_send(request) # :doc:
|
|
105
|
+
@last_request = request
|
|
106
|
+
retries_left = @config.retry_limit
|
|
107
|
+
begin
|
|
108
|
+
response = @connection.request request
|
|
109
|
+
@last_response = response
|
|
110
|
+
if response.kind_of? Net::HTTPServerError
|
|
111
|
+
raise Twilio::REST::ServerError
|
|
112
|
+
end
|
|
113
|
+
rescue Exception
|
|
114
|
+
raise if request.class == Net::HTTP::Post
|
|
115
|
+
if retries_left > 0 then retries_left -= 1; retry else raise end
|
|
116
|
+
end
|
|
117
|
+
if response.body and !response.body.empty?
|
|
118
|
+
object = MultiJson.load response.body
|
|
119
|
+
end
|
|
120
|
+
if response.kind_of? Net::HTTPClientError
|
|
121
|
+
raise Twilio::REST::RequestError.new object['message'], object['code']
|
|
122
|
+
end
|
|
123
|
+
object
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Twilio
|
|
2
|
+
module REST
|
|
3
|
+
class Feedback < ListResource;
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# Get this feedback object.
|
|
7
|
+
#
|
|
8
|
+
# Overridden because GETS to /Feedback
|
|
9
|
+
# returns an instance, not a list.
|
|
10
|
+
def get(params={}, full_path=false)
|
|
11
|
+
raise "Can't fetch feedback without a REST Client" unless @client
|
|
12
|
+
response = @client.get @path, params, full_path
|
|
13
|
+
path = full_path ? @path.split('.')[0] : @path
|
|
14
|
+
@instance_class.new path, @client, response
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# Creates a new Feedback object.
|
|
19
|
+
def create(params={})
|
|
20
|
+
raise "Can't create feedback without a REST Client" unless @client
|
|
21
|
+
response = @client.post @path, params
|
|
22
|
+
@instance_class.new @path, @client, response
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class FeedbackInstance < InstanceResource; end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Twilio
|
|
2
|
+
module REST
|
|
3
|
+
class FeedbackSummary < ListResource
|
|
4
|
+
def initialize(path, client)
|
|
5
|
+
@path, @client = path, client
|
|
6
|
+
@instance_class = Twilio::REST::FeedbackSummaryInstance
|
|
7
|
+
@list_key, @instance_id_key = 'feedback_summary', 'sid'
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class FeedbackSummaryInstance < InstanceResource; end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
module Twilio
|
|
2
2
|
module REST
|
|
3
3
|
class Calls < ListResource
|
|
4
|
+
def initialize(path, client)
|
|
5
|
+
super
|
|
6
|
+
resource :feedback_summary
|
|
7
|
+
end
|
|
8
|
+
|
|
4
9
|
def make(from, to, url)
|
|
5
|
-
create :
|
|
10
|
+
create from: from, to: to, url: url
|
|
6
11
|
end
|
|
7
12
|
end
|
|
8
13
|
|
|
9
14
|
class Call < InstanceResource
|
|
10
15
|
def initialize(path, client, params={})
|
|
11
16
|
super path, client, params
|
|
12
|
-
resource :recordings, :notifications
|
|
17
|
+
resource :recordings, :notifications, :feedback
|
|
13
18
|
end
|
|
14
19
|
|
|
15
20
|
def redirect_to(url)
|
|
16
|
-
update :
|
|
21
|
+
update url: url
|
|
17
22
|
end
|
|
18
23
|
|
|
19
24
|
def cancel
|
|
20
|
-
update :
|
|
25
|
+
update status: 'canceled'
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
def hangup
|
|
24
|
-
update :
|
|
29
|
+
update status: 'completed'
|
|
25
30
|
end
|
|
26
31
|
end
|
|
27
32
|
end
|