twilio-ruby 7.11.3 → 7.13.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 +4 -4
- data/CHANGES.md +277 -0
- data/Gemfile.lock +2 -2
- data/README.md +2 -2
- data/examples/WebhookSignatureValidation.md +61 -0
- data/lib/rack/twilio_webhook_authentication.rb +30 -5
- data/lib/twilio-ruby/rest/api/v2010/account/call.rb +7 -0
- data/lib/twilio-ruby/rest/api/v2010/account/conference.rb +4 -4
- data/lib/twilio-ruby/rest/client.rb +5 -0
- data/lib/twilio-ruby/rest/content/v1/content/approval_create.rb +108 -1
- data/lib/twilio-ruby/rest/content/v1/content.rb +10 -4
- data/lib/twilio-ruby/rest/conversations/v2/action.rb +9 -2
- data/lib/twilio-ruby/rest/conversations/v2/communication.rb +4 -1
- data/lib/twilio-ruby/rest/conversations/v2/configuration.rb +21 -0
- data/lib/twilio-ruby/rest/conversations/v2/conversation.rb +226 -7
- data/lib/twilio-ruby/rest/insights/v1/call/call_summary.rb +1 -1
- data/lib/twilio-ruby/rest/insights/v1/call_summaries.rb +1 -1
- data/lib/twilio-ruby/rest/insights/v3/custom_field_mapping.rb +512 -17
- data/lib/twilio-ruby/rest/insights/v3/{capacity.rb → custom_field_mappings_capacity.rb} +26 -26
- data/lib/twilio-ruby/rest/insights/v3.rb +17 -7
- data/lib/twilio-ruby/rest/memory/v1/bulk.rb +22 -1
- data/lib/twilio-ruby/rest/memory/v1/conversation_summary.rb +5 -5
- data/lib/twilio-ruby/rest/memory/v1/import.rb +21 -7
- data/lib/twilio-ruby/rest/memory/v1/observation.rb +9 -9
- data/lib/twilio-ruby/rest/memory/v1/revision.rb +1 -1
- data/lib/twilio-ruby/rest/memory/v1/store.rb +7 -0
- data/lib/twilio-ruby/rest/messaging/v2/channels_sender.rb +30 -2
- data/lib/twilio-ruby/rest/numbers/v1/sms_verification.rb +30 -6
- data/lib/twilio-ruby/rest/numbers/v1/sms_verification_check.rb +30 -6
- data/lib/twilio-ruby/rest/numbers/v1/voice_verification.rb +45 -6
- data/lib/twilio-ruby/rest/numbers/v1.rb +0 -6
- data/lib/twilio-ruby/rest/preview/hosted_numbers/authorization_document.rb +40 -0
- data/lib/twilio-ruby/rest/routes/v3/iso_country_code.rb +402 -0
- data/lib/twilio-ruby/rest/routes/v3.rb +43 -0
- data/lib/twilio-ruby/rest/trusthub/v1/a2p_brand_registration.rb +5 -5
- data/lib/twilio-ruby/rest/trusthub/v1/a2p_campaign_registration.rb +4 -4
- data/lib/twilio-ruby/rest/trusthub/v1.rb +2 -2
- data/lib/twilio-ruby/rest/webhooks/v1/auth_profile.rb +825 -0
- data/lib/twilio-ruby/rest/webhooks/v1/edge_zone.rb +320 -0
- data/lib/twilio-ruby/rest/webhooks/v1/operation.rb +513 -0
- data/lib/twilio-ruby/rest/webhooks/v1/rule.rb +818 -0
- data/lib/twilio-ruby/rest/webhooks/v1/setting.rb +1028 -0
- data/lib/twilio-ruby/rest/webhooks/v1/shared_key.rb +722 -0
- data/lib/twilio-ruby/rest/webhooks/v1/test.rb +228 -0
- data/lib/twilio-ruby/rest/webhooks/v1.rb +126 -0
- data/lib/twilio-ruby/rest/webhooks.rb +6 -0
- data/lib/twilio-ruby/rest/webhooks_base.rb +38 -0
- data/lib/twilio-ruby/security/request_validator.rb +54 -8
- data/lib/twilio-ruby/twiml/voice_response.rb +20 -5
- data/lib/twilio-ruby/version.rb +1 -1
- metadata +15 -5
- data/lib/twilio-ruby/rest/numbers/v1/voice_verification_check.rb +0 -165
- data/lib/twilio-ruby/rest/oauth/v2/oauth_authorization_server.rb +0 -200
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
##
|
|
2
|
+
# This code was generated by
|
|
3
|
+
# ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __
|
|
4
|
+
# | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/
|
|
5
|
+
# | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \
|
|
6
|
+
#
|
|
7
|
+
# Twilio - Webhooks Configuration
|
|
8
|
+
# Centralized webhook configuration with OAuth2 support, immutable resources, and zero-downtime credential rotation.
|
|
9
|
+
#
|
|
10
|
+
# NOTE: This class is auto generated by OpenAPI Generator.
|
|
11
|
+
# https://openapi-generator.tech
|
|
12
|
+
# Do not edit the class manually.
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
module Twilio
|
|
17
|
+
module REST
|
|
18
|
+
class Webhooks < WebhooksBase
|
|
19
|
+
class V1 < Version
|
|
20
|
+
class TestList < ListResource
|
|
21
|
+
|
|
22
|
+
class TestDeliveryResultResponse
|
|
23
|
+
# @param [status_code]: [Integer]
|
|
24
|
+
# @param [headers]: [Hash<String, Array<String>>]
|
|
25
|
+
attr_accessor :status_code, :headers
|
|
26
|
+
def initialize(payload)
|
|
27
|
+
@status_code = payload["status_code"]
|
|
28
|
+
@headers = payload["headers"]
|
|
29
|
+
end
|
|
30
|
+
def to_json(options = {})
|
|
31
|
+
{
|
|
32
|
+
"statusCode": @status_code,
|
|
33
|
+
"headers": @headers,
|
|
34
|
+
}.to_json(options)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class TestDeliveryResultTiming
|
|
39
|
+
# @param [total_ms]: [Integer] Total time taken for the webhook delivery in milliseconds
|
|
40
|
+
attr_accessor :total_ms
|
|
41
|
+
def initialize(payload)
|
|
42
|
+
@total_ms = payload["total_ms"]
|
|
43
|
+
end
|
|
44
|
+
def to_json(options = {})
|
|
45
|
+
{
|
|
46
|
+
"totalMs": @total_ms,
|
|
47
|
+
}.to_json(options)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class TestRequest
|
|
52
|
+
# @param [url]: [String] Target webhook URL to test
|
|
53
|
+
# @param [setting_id]: [String] Setting to use for test configuration
|
|
54
|
+
# @param [method]: [String] HTTP method to use when sending the test webhook request
|
|
55
|
+
attr_accessor :url, :setting_id, :method
|
|
56
|
+
def initialize(payload)
|
|
57
|
+
@url = payload["url"]
|
|
58
|
+
@setting_id = payload["setting_id"]
|
|
59
|
+
@method = payload["method"]
|
|
60
|
+
end
|
|
61
|
+
def to_json(options = {})
|
|
62
|
+
{
|
|
63
|
+
"url": @url,
|
|
64
|
+
"settingId": @setting_id,
|
|
65
|
+
"method": @method,
|
|
66
|
+
}.to_json(options)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
##
|
|
72
|
+
# Initialize the TestList
|
|
73
|
+
# @param [Version] version Version that contains the resource
|
|
74
|
+
# @return [TestList] TestList
|
|
75
|
+
def initialize(version)
|
|
76
|
+
|
|
77
|
+
apiV1Version = ApiV1Version.new version.domain, version
|
|
78
|
+
super(apiV1Version)
|
|
79
|
+
# Path Solution
|
|
80
|
+
@solution = { }
|
|
81
|
+
@uri = "/Webhooks/Tests"
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
##
|
|
85
|
+
# Create the TestInstance
|
|
86
|
+
# @param [TestRequest] test_request
|
|
87
|
+
# @return [TestInstance] Created TestInstance
|
|
88
|
+
def create(test_request: nil
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
headers = Twilio::Values.of({'Content-Type' => 'application/x-www-form-urlencoded', })
|
|
92
|
+
headers['Content-Type'] = 'application/json'
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
headers['Accept'] = '*/*'
|
|
97
|
+
payload = @version.create('POST', @uri, headers: headers, data: test_request.to_json)
|
|
98
|
+
TestInstance.new(
|
|
99
|
+
@version,
|
|
100
|
+
payload,
|
|
101
|
+
)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
##
|
|
105
|
+
# Create the TestInstanceMetadata
|
|
106
|
+
# @param [TestRequest] test_request
|
|
107
|
+
# @return [TestInstance] Created TestInstance
|
|
108
|
+
def create_with_metadata(test_request: nil
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
headers = Twilio::Values.of({'Content-Type' => 'application/x-www-form-urlencoded', })
|
|
112
|
+
headers['Content-Type'] = 'application/json'
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
headers['Accept'] = '*/*'
|
|
117
|
+
response = @version.create_with_metadata('POST', @uri, headers: headers, data: test_request.to_json)
|
|
118
|
+
test_instance = TestInstance.new(
|
|
119
|
+
@version,
|
|
120
|
+
response.body,
|
|
121
|
+
)
|
|
122
|
+
TestInstanceMetadata.new(
|
|
123
|
+
@version,
|
|
124
|
+
test_instance,
|
|
125
|
+
response.headers,
|
|
126
|
+
response.status_code
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# Provide a user friendly representation
|
|
134
|
+
def to_s
|
|
135
|
+
'#<Twilio.Webhooks.V1.TestList>'
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
class TestPage < TokenPage
|
|
140
|
+
##
|
|
141
|
+
# Initialize the TestPage
|
|
142
|
+
# @param [Version] version Version that contains the resource
|
|
143
|
+
# @param [Response] response Response from the API
|
|
144
|
+
# @param [Hash] solution Path solution for the resource
|
|
145
|
+
# @return [TestPage] TestPage
|
|
146
|
+
def initialize(version, response, solution)
|
|
147
|
+
|
|
148
|
+
apiV1Version = ApiV1Version.new version.domain, version
|
|
149
|
+
super(apiV1Version, response)
|
|
150
|
+
|
|
151
|
+
# Path Solution
|
|
152
|
+
@solution = solution
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
##
|
|
156
|
+
# Build an instance of TestInstance
|
|
157
|
+
# @param [Hash] payload Payload response from the API
|
|
158
|
+
# @return [TestInstance] TestInstance
|
|
159
|
+
def get_instance(payload)
|
|
160
|
+
TestInstance.new(@version, payload)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
##
|
|
164
|
+
# Provide a user friendly representation
|
|
165
|
+
def to_s
|
|
166
|
+
'<Twilio.Webhooks.V1.TestPage>'
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
class TestInstance < InstanceResource
|
|
171
|
+
##
|
|
172
|
+
# Initialize the TestInstance
|
|
173
|
+
# @param [Version] version Version that contains the resource
|
|
174
|
+
# @param [Hash] payload payload that contains response from Twilio
|
|
175
|
+
# @param [String] account_sid The SID of the
|
|
176
|
+
# {Account}[https://www.twilio.com/docs/iam/api/account] that created this Test
|
|
177
|
+
# resource.
|
|
178
|
+
# @param [String] sid The SID of the Call resource to fetch.
|
|
179
|
+
# @return [TestInstance] TestInstance
|
|
180
|
+
def initialize(version, payload )
|
|
181
|
+
|
|
182
|
+
apiV1Version = ApiV1Version.new version.domain, version
|
|
183
|
+
super(apiV1Version)
|
|
184
|
+
|
|
185
|
+
# Marshaled Properties
|
|
186
|
+
@properties = {
|
|
187
|
+
'url' => payload['url'],
|
|
188
|
+
'result' => payload['result'],
|
|
189
|
+
'created_at' => Twilio.deserialize_iso8601_datetime(payload['created_at']),
|
|
190
|
+
}
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
##
|
|
195
|
+
# @return [String]
|
|
196
|
+
def url
|
|
197
|
+
@properties['url']
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
##
|
|
201
|
+
# @return [TestDeliveryResult]
|
|
202
|
+
def result
|
|
203
|
+
@properties['result']
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
##
|
|
207
|
+
# @return [Time]
|
|
208
|
+
def created_at
|
|
209
|
+
@properties['created_at']
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
##
|
|
213
|
+
# Provide a user friendly representation
|
|
214
|
+
def to_s
|
|
215
|
+
"<Twilio.Webhooks.V1.TestInstance>"
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
##
|
|
219
|
+
# Provide a detailed, user friendly representation
|
|
220
|
+
def inspect
|
|
221
|
+
"<Twilio.Webhooks.V1.TestInstance>"
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
##
|
|
2
|
+
# This code was generated by
|
|
3
|
+
# ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __
|
|
4
|
+
# | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/
|
|
5
|
+
# | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \
|
|
6
|
+
#
|
|
7
|
+
# Twilio - Webhooks Configuration
|
|
8
|
+
# Centralized webhook configuration with OAuth2 support, immutable resources, and zero-downtime credential rotation.
|
|
9
|
+
#
|
|
10
|
+
# NOTE: This class is auto generated by OpenAPI Generator.
|
|
11
|
+
# https://openapi-generator.tech
|
|
12
|
+
# Do not edit the class manually.
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
module Twilio
|
|
16
|
+
module REST
|
|
17
|
+
class Webhooks
|
|
18
|
+
class V1 < Version
|
|
19
|
+
##
|
|
20
|
+
# Initialize the V1 version of Webhooks
|
|
21
|
+
def initialize(domain)
|
|
22
|
+
super
|
|
23
|
+
@version = 'v1'
|
|
24
|
+
@auth_profiles = nil
|
|
25
|
+
@edge_zones = nil
|
|
26
|
+
@operations = nil
|
|
27
|
+
@rules = nil
|
|
28
|
+
@settings = nil
|
|
29
|
+
@shared_keys = nil
|
|
30
|
+
@tests = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# @param [String] auth_profile_id
|
|
35
|
+
# @return [Twilio::REST::Webhooks::V1::AuthProfileContext] if authProfileId was passed.
|
|
36
|
+
# @return [Twilio::REST::Webhooks::V1::AuthProfileList]
|
|
37
|
+
def auth_profiles(auth_profile_id=:unset)
|
|
38
|
+
if auth_profile_id.nil?
|
|
39
|
+
raise ArgumentError, 'auth_profile_id cannot be nil'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if auth_profile_id == :unset
|
|
43
|
+
@auth_profiles ||= AuthProfileList.new self
|
|
44
|
+
else
|
|
45
|
+
AuthProfileContext.new(self, auth_profile_id)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
##
|
|
49
|
+
# @return [Twilio::REST::Webhooks::V1::EdgeZoneList]
|
|
50
|
+
def edge_zones
|
|
51
|
+
@edge_zones ||= EdgeZoneList.new self
|
|
52
|
+
end
|
|
53
|
+
##
|
|
54
|
+
# @param [String] operation_id
|
|
55
|
+
# @return [Twilio::REST::Webhooks::V1::OperationContext] if operationId was passed.
|
|
56
|
+
# @return [Twilio::REST::Webhooks::V1::OperationList]
|
|
57
|
+
def operations(operation_id=:unset)
|
|
58
|
+
if operation_id.nil?
|
|
59
|
+
raise ArgumentError, 'operation_id cannot be nil'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
if operation_id == :unset
|
|
63
|
+
@operations ||= OperationList.new self
|
|
64
|
+
else
|
|
65
|
+
OperationContext.new(self, operation_id)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
##
|
|
69
|
+
# @param [String] rule_id
|
|
70
|
+
# @return [Twilio::REST::Webhooks::V1::RuleContext] if ruleId was passed.
|
|
71
|
+
# @return [Twilio::REST::Webhooks::V1::RuleList]
|
|
72
|
+
def rules(rule_id=:unset)
|
|
73
|
+
if rule_id.nil?
|
|
74
|
+
raise ArgumentError, 'rule_id cannot be nil'
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if rule_id == :unset
|
|
78
|
+
@rules ||= RuleList.new self
|
|
79
|
+
else
|
|
80
|
+
RuleContext.new(self, rule_id)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
##
|
|
84
|
+
# @param [String] setting_id
|
|
85
|
+
# @return [Twilio::REST::Webhooks::V1::SettingContext] if settingId was passed.
|
|
86
|
+
# @return [Twilio::REST::Webhooks::V1::SettingList]
|
|
87
|
+
def settings(setting_id=:unset)
|
|
88
|
+
if setting_id.nil?
|
|
89
|
+
raise ArgumentError, 'setting_id cannot be nil'
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
if setting_id == :unset
|
|
93
|
+
@settings ||= SettingList.new self
|
|
94
|
+
else
|
|
95
|
+
SettingContext.new(self, setting_id)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
##
|
|
99
|
+
# @param [String] shared_key_id
|
|
100
|
+
# @return [Twilio::REST::Webhooks::V1::SharedKeyContext] if sharedKeyId was passed.
|
|
101
|
+
# @return [Twilio::REST::Webhooks::V1::SharedKeyList]
|
|
102
|
+
def shared_keys(shared_key_id=:unset)
|
|
103
|
+
if shared_key_id.nil?
|
|
104
|
+
raise ArgumentError, 'shared_key_id cannot be nil'
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
if shared_key_id == :unset
|
|
108
|
+
@shared_keys ||= SharedKeyList.new self
|
|
109
|
+
else
|
|
110
|
+
SharedKeyContext.new(self, shared_key_id)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
##
|
|
114
|
+
# @return [Twilio::REST::Webhooks::V1::TestList]
|
|
115
|
+
def tests
|
|
116
|
+
@tests ||= TestList.new self
|
|
117
|
+
end
|
|
118
|
+
##
|
|
119
|
+
# Provide a user friendly representation
|
|
120
|
+
def to_s
|
|
121
|
+
'<Twilio::REST::Webhooks::V1>';
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
##
|
|
2
|
+
# This code was generated by
|
|
3
|
+
# ___ _ _ _ _ _ _ ____ ____ ____ _ ____ ____ _ _ ____ ____ ____ ___ __ __
|
|
4
|
+
# | | | | | | | | | __ | | |__| | __ | __ |___ |\ | |___ |__/ |__| | | | |__/
|
|
5
|
+
# | |_|_| | |___ | |__| |__| | | | |__] |___ | \| |___ | \ | | | |__| | \
|
|
6
|
+
#
|
|
7
|
+
# NOTE: This class is auto generated by OpenAPI Generator.
|
|
8
|
+
# https://openapi-generator.tech
|
|
9
|
+
# Do not edit the class manually.
|
|
10
|
+
# frozen_string_literal: true
|
|
11
|
+
module Twilio
|
|
12
|
+
module REST
|
|
13
|
+
class WebhooksBase < Domain
|
|
14
|
+
##
|
|
15
|
+
# Initialize webhooks domain
|
|
16
|
+
#
|
|
17
|
+
# @param twilio - The twilio client
|
|
18
|
+
#
|
|
19
|
+
def initialize(twilio)
|
|
20
|
+
super(twilio)
|
|
21
|
+
@base_url = "https://webhooks.twilio.com"
|
|
22
|
+
@host = "webhooks.twilio.com"
|
|
23
|
+
@port = 443
|
|
24
|
+
@v1 = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def v1
|
|
28
|
+
@v1 ||= Webhooks::V1.new self
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# Provide a user friendly representation
|
|
33
|
+
def to_s
|
|
34
|
+
'<Twilio::REST::Webhooks::V1>';
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -3,11 +3,35 @@
|
|
|
3
3
|
module Twilio
|
|
4
4
|
module Security
|
|
5
5
|
class RequestValidator
|
|
6
|
+
# Header that carries the webhook signature
|
|
7
|
+
SIGNATURE_HEADER = 'X-Twilio-Signature'
|
|
8
|
+
# Header Twilio adds to webhooks signed with a SharedKey: key=<sharedKeyId>,algorithm=<algorithm>
|
|
9
|
+
SIGNATURE_SETTINGS_HEADER = 'X-Twilio-Webhooks-Signature-Settings'
|
|
10
|
+
HMAC_SHA1 = 'HMAC_SHA1'
|
|
11
|
+
HMAC_SHA256 = 'HMAC_SHA256'
|
|
12
|
+
DIGESTS = { HMAC_SHA1 => 'sha1', HMAC_SHA256 => 'sha256' }.freeze
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Returns the signing algorithm named in the X-Twilio-Webhooks-Signature-Settings header. A missing or
|
|
16
|
+
# empty header (or one without +algorithm=+) means the legacy Auth Token signature, HMAC_SHA1.
|
|
17
|
+
#
|
|
18
|
+
# @param [String] signature_settings_header e.g. "key=webhooks_sharedkey_...,algorithm=HMAC_SHA256"
|
|
19
|
+
#
|
|
20
|
+
# @return [String] the algorithm name
|
|
21
|
+
def self.signature_algorithm(signature_settings_header)
|
|
22
|
+
signature_settings_header.to_s.split(',').each do |part|
|
|
23
|
+
name, value = part.split('=', 2).map(&:strip)
|
|
24
|
+
return value if name == 'algorithm' && !value.to_s.empty?
|
|
25
|
+
end
|
|
26
|
+
HMAC_SHA1
|
|
27
|
+
end
|
|
28
|
+
|
|
6
29
|
##
|
|
7
30
|
# Initialize a Request Validator. auth_token will either be grabbed from the global Twilio object or you can
|
|
8
31
|
# pass it in here.
|
|
9
32
|
#
|
|
10
|
-
# @param [String] auth_token
|
|
33
|
+
# @param [String] auth_token The secret the webhook was signed with: your account auth token, or the
|
|
34
|
+
# SharedKey secret when the request carries the X-Twilio-Webhooks-Signature-Settings header
|
|
11
35
|
def initialize(auth_token = nil)
|
|
12
36
|
@auth_token = auth_token || Twilio.auth_token
|
|
13
37
|
raise ArgumentError, 'Auth token is required' if @auth_token.nil?
|
|
@@ -22,9 +46,17 @@ module Twilio
|
|
|
22
46
|
# a bodySHA256 parameter in the query string, this parameter can instead be the POST body as a string to
|
|
23
47
|
# validate JSON or other text-based payloads that aren't x-www-form-urlencoded.
|
|
24
48
|
# @param [String] signature The expected signature, from the X-Twilio-Signature header of the request
|
|
49
|
+
# @param [String] algorithm HMAC_SHA1 (default) or HMAC_SHA256, see {.signature_algorithm}. An unsupported
|
|
50
|
+
# algorithm fails validation and logs a warning.
|
|
25
51
|
#
|
|
26
52
|
# @return [Boolean] whether or not the computed signature matches the signature parameter
|
|
27
|
-
def validate(url, params, signature)
|
|
53
|
+
def validate(url, params, signature, algorithm = nil)
|
|
54
|
+
unless digest_name_for(algorithm)
|
|
55
|
+
warn "[Twilio] Unsupported webhook signature algorithm '#{algorithm}'. " \
|
|
56
|
+
"Supported: #{HMAC_SHA1}, #{HMAC_SHA256}. Upgrade twilio-ruby if Twilio added a new one."
|
|
57
|
+
return false
|
|
58
|
+
end
|
|
59
|
+
|
|
28
60
|
parsed_url = URI(url)
|
|
29
61
|
url_with_port = add_port(parsed_url)
|
|
30
62
|
url_without_port = remove_port(parsed_url)
|
|
@@ -40,8 +72,10 @@ module Twilio
|
|
|
40
72
|
|
|
41
73
|
# Check signature of the url with and without port numbers
|
|
42
74
|
# since signature generation on the back end is inconsistent
|
|
43
|
-
valid_signature_with_port = secure_compare(build_signature_for(url_with_port, params_hash
|
|
44
|
-
|
|
75
|
+
valid_signature_with_port = secure_compare(build_signature_for(url_with_port, params_hash, algorithm),
|
|
76
|
+
signature)
|
|
77
|
+
valid_signature_without_port = secure_compare(build_signature_for(url_without_port, params_hash, algorithm),
|
|
78
|
+
signature)
|
|
45
79
|
|
|
46
80
|
valid_body && (valid_signature_with_port || valid_signature_without_port)
|
|
47
81
|
end
|
|
@@ -58,20 +92,32 @@ module Twilio
|
|
|
58
92
|
end
|
|
59
93
|
|
|
60
94
|
##
|
|
61
|
-
# Build
|
|
95
|
+
# Build an HMAC signature for a url and parameter hash
|
|
62
96
|
#
|
|
63
97
|
# @param [String] url The request url, including any query parameters
|
|
64
98
|
# @param [#join] params The POST parameters
|
|
99
|
+
# @param [String] algorithm HMAC_SHA1 (default) or HMAC_SHA256
|
|
65
100
|
#
|
|
66
|
-
# @return [String] A base64 encoded
|
|
67
|
-
|
|
101
|
+
# @return [String] A base64 encoded HMAC
|
|
102
|
+
# @raise [ArgumentError] if the algorithm is not supported
|
|
103
|
+
def build_signature_for(url, params, algorithm = nil)
|
|
104
|
+
digest_name = digest_name_for(algorithm)
|
|
105
|
+
raise ArgumentError, "Unsupported webhook signature algorithm: #{algorithm}" unless digest_name
|
|
106
|
+
|
|
68
107
|
data = url + params.sort.join
|
|
69
|
-
digest = OpenSSL::Digest.new(
|
|
108
|
+
digest = OpenSSL::Digest.new(digest_name)
|
|
70
109
|
Base64.strict_encode64(OpenSSL::HMAC.digest(digest, @auth_token, data))
|
|
71
110
|
end
|
|
72
111
|
|
|
73
112
|
private
|
|
74
113
|
|
|
114
|
+
# Maps an algorithm name to an OpenSSL digest name, or nil when unsupported. nil or empty means HMAC_SHA1.
|
|
115
|
+
def digest_name_for(algorithm)
|
|
116
|
+
return DIGESTS[HMAC_SHA1] if algorithm.nil? || algorithm.to_s.empty?
|
|
117
|
+
|
|
118
|
+
DIGESTS[algorithm.to_s.upcase]
|
|
119
|
+
end
|
|
120
|
+
|
|
75
121
|
# Compares two strings in constant time to avoid timing attacks.
|
|
76
122
|
# Borrowed from ActiveSupport::MessageVerifier.
|
|
77
123
|
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/message_verifier.rb
|
|
@@ -2118,9 +2118,20 @@ module Twilio
|
|
|
2118
2118
|
# deepgramSmartFormat:: This parameter enables Deepgram's smart formatting feature, which automatically applies punctuation, capitalization, and formatting (e.g. numbers, dates, currency) to transcripts
|
|
2119
2119
|
# ignoreBackchannel:: This parameter brief caller acknowledgments (e.g. "yeah", "uh-huh") are ignored and will not interrupt the agent while it is speaking.
|
|
2120
2120
|
# events:: This parameter allows you to enable event subscriptions
|
|
2121
|
-
#
|
|
2122
|
-
|
|
2123
|
-
|
|
2121
|
+
# partial_prompt_on_hangup:: Whether the in-progress partial transcript should be reported as a prompt when the call hangs up
|
|
2122
|
+
# eager_eot_threshold:: Eager end-of-turn confidence threshold, which fires before the end-of-turn threshold. The value can be between 0.3 and 0.9
|
|
2123
|
+
# asr_smart_format:: Whether smart formatting should be applied to transcripts. Takes precedence over deepgramSmartFormat
|
|
2124
|
+
# tts_text_normalization:: Text normalization for text-to-speech. The value can be auto, on, or off. Takes precedence over elevenlabsTextNormalization
|
|
2125
|
+
# tts_language_text_normalization:: Whether language-specific text normalization should be applied for text-to-speech
|
|
2126
|
+
# elevenlabs_language_text_normalization:: Legacy alias of ttsLanguageTextNormalization
|
|
2127
|
+
# include_in_prompt:: Extra fields to be included in prompt messages. The value can be turn-state or prompt-segments
|
|
2128
|
+
# customer_name:: Customer name sent to Conversations and Conversational Intelligence. Defaults to the caller ID
|
|
2129
|
+
# application_reconnect:: Whether the WebSocket connection to the application should be reconnected if it drops during the call. Defaults to false
|
|
2130
|
+
# echo_prompt_filtering:: Whether transcripts that echo the agent's own text-to-speech should be dropped. Defaults to false
|
|
2131
|
+
# turn_detection:: Whether turn detection should be used to decide when the caller has finished speaking. Defaults to false
|
|
2132
|
+
# keyword_args:: additional attributes
|
|
2133
|
+
def conversation_relay(url: nil, language: nil, tts_language: nil, transcription_language: nil, tts_provider: nil, voice: nil, transcription_provider: nil, speech_model: nil, profanity_filter: nil, dtmf_detection: nil, welcome_greeting: nil, partial_prompts: nil, welcome_greeting_interruptible: nil, interruptible: nil, preemptible: nil, hints: nil, intelligence_service: nil, report_input_during_agent_speech: nil, elevenlabs_text_normalization: nil, interrupt_sensitivity: nil, debug: nil, backgroundNoiseReduction: nil, speechTimeout: nil, deepgramSmartFormat: nil, ignoreBackchannel: nil, events: nil, partial_prompt_on_hangup: nil, eager_eot_threshold: nil, asr_smart_format: nil, tts_text_normalization: nil, tts_language_text_normalization: nil, elevenlabs_language_text_normalization: nil, include_in_prompt: nil, customer_name: nil, application_reconnect: nil, echo_prompt_filtering: nil, turn_detection: nil, **keyword_args)
|
|
2134
|
+
conversation_relay = ConversationRelay.new(url: url, language: language, tts_language: tts_language, transcription_language: transcription_language, tts_provider: tts_provider, voice: voice, transcription_provider: transcription_provider, speech_model: speech_model, profanity_filter: profanity_filter, dtmf_detection: dtmf_detection, welcome_greeting: welcome_greeting, partial_prompts: partial_prompts, welcome_greeting_interruptible: welcome_greeting_interruptible, interruptible: interruptible, preemptible: preemptible, hints: hints, intelligence_service: intelligence_service, report_input_during_agent_speech: report_input_during_agent_speech, elevenlabs_text_normalization: elevenlabs_text_normalization, interrupt_sensitivity: interrupt_sensitivity, debug: debug, backgroundNoiseReduction: backgroundNoiseReduction, speechTimeout: speechTimeout, deepgramSmartFormat: deepgramSmartFormat, ignoreBackchannel: ignoreBackchannel, events: events, partial_prompt_on_hangup: partial_prompt_on_hangup, eager_eot_threshold: eager_eot_threshold, asr_smart_format: asr_smart_format, tts_text_normalization: tts_text_normalization, tts_language_text_normalization: tts_language_text_normalization, elevenlabs_language_text_normalization: elevenlabs_language_text_normalization, include_in_prompt: include_in_prompt, customer_name: customer_name, application_reconnect: application_reconnect, echo_prompt_filtering: echo_prompt_filtering, turn_detection: turn_detection, **keyword_args)
|
|
2124
2135
|
|
|
2125
2136
|
yield(conversation_relay) if block_given?
|
|
2126
2137
|
append(conversation_relay)
|
|
@@ -2184,9 +2195,13 @@ module Twilio
|
|
|
2184
2195
|
# voice:: Voice to be used for text-to-speech of this language
|
|
2185
2196
|
# transcription_provider:: Provider to be used for transcription of this language
|
|
2186
2197
|
# speech_model:: Speech model to be used for transcription of this language
|
|
2198
|
+
# hints:: Phrases to help better accuracy in speech recognition of this language
|
|
2199
|
+
# asr_smart_format:: Whether smart formatting should be applied to transcripts of this language
|
|
2200
|
+
# tts_text_normalization:: Text normalization for text-to-speech of this language. The value can be auto, on, or off
|
|
2201
|
+
# tts_language_text_normalization:: Whether language-specific text normalization should be applied for text-to-speech of this language
|
|
2187
2202
|
# keyword_args:: additional attributes
|
|
2188
|
-
def language(code: nil, tts_provider: nil, voice: nil, transcription_provider: nil, speech_model: nil, **keyword_args)
|
|
2189
|
-
append(Language.new(code: code, tts_provider: tts_provider, voice: voice, transcription_provider: transcription_provider, speech_model: speech_model, **keyword_args))
|
|
2203
|
+
def language(code: nil, tts_provider: nil, voice: nil, transcription_provider: nil, speech_model: nil, hints: nil, asr_smart_format: nil, tts_text_normalization: nil, tts_language_text_normalization: nil, **keyword_args)
|
|
2204
|
+
append(Language.new(code: code, tts_provider: tts_provider, voice: voice, transcription_provider: transcription_provider, speech_model: speech_model, hints: hints, asr_smart_format: asr_smart_format, tts_text_normalization: tts_text_normalization, tts_language_text_normalization: tts_language_text_normalization, **keyword_args))
|
|
2190
2205
|
end
|
|
2191
2206
|
|
|
2192
2207
|
##
|
data/lib/twilio-ruby/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: twilio-ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 7.
|
|
4
|
+
version: 7.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Twilio API Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jwt
|
|
@@ -223,6 +223,7 @@ files:
|
|
|
223
223
|
- cluster/cluster_oauth_spec.rb
|
|
224
224
|
- cluster/cluster_orgs_spec.rb
|
|
225
225
|
- cluster/cluster_spec.rb
|
|
226
|
+
- examples/WebhookSignatureValidation.md
|
|
226
227
|
- examples/examples.rb
|
|
227
228
|
- examples/print_call_log.rb
|
|
228
229
|
- examples/public_oauth.rb
|
|
@@ -519,8 +520,8 @@ files:
|
|
|
519
520
|
- lib/twilio-ruby/rest/insights/v2/outbound_report.rb
|
|
520
521
|
- lib/twilio-ruby/rest/insights/v2/report.rb
|
|
521
522
|
- lib/twilio-ruby/rest/insights/v3.rb
|
|
522
|
-
- lib/twilio-ruby/rest/insights/v3/capacity.rb
|
|
523
523
|
- lib/twilio-ruby/rest/insights/v3/custom_field_mapping.rb
|
|
524
|
+
- lib/twilio-ruby/rest/insights/v3/custom_field_mappings_capacity.rb
|
|
524
525
|
- lib/twilio-ruby/rest/insights/v3/metadata.rb
|
|
525
526
|
- lib/twilio-ruby/rest/insights/v3/query.rb
|
|
526
527
|
- lib/twilio-ruby/rest/insights/v3/query_job.rb
|
|
@@ -684,7 +685,6 @@ files:
|
|
|
684
685
|
- lib/twilio-ruby/rest/numbers/v1/sms_verification.rb
|
|
685
686
|
- lib/twilio-ruby/rest/numbers/v1/sms_verification_check.rb
|
|
686
687
|
- lib/twilio-ruby/rest/numbers/v1/voice_verification.rb
|
|
687
|
-
- lib/twilio-ruby/rest/numbers/v1/voice_verification_check.rb
|
|
688
688
|
- lib/twilio-ruby/rest/numbers/v1/webhook.rb
|
|
689
689
|
- lib/twilio-ruby/rest/numbers/v2.rb
|
|
690
690
|
- lib/twilio-ruby/rest/numbers/v2/application.rb
|
|
@@ -713,7 +713,6 @@ files:
|
|
|
713
713
|
- lib/twilio-ruby/rest/oauth/v1/token.rb
|
|
714
714
|
- lib/twilio-ruby/rest/oauth/v2.rb
|
|
715
715
|
- lib/twilio-ruby/rest/oauth/v2/authorize.rb
|
|
716
|
-
- lib/twilio-ruby/rest/oauth/v2/oauth_authorization_server.rb
|
|
717
716
|
- lib/twilio-ruby/rest/oauth/v2/token.rb
|
|
718
717
|
- lib/twilio-ruby/rest/oauth_base.rb
|
|
719
718
|
- lib/twilio-ruby/rest/preview.rb
|
|
@@ -773,6 +772,7 @@ files:
|
|
|
773
772
|
- lib/twilio-ruby/rest/routes/v2/sip_domain.rb
|
|
774
773
|
- lib/twilio-ruby/rest/routes/v2/trunk.rb
|
|
775
774
|
- lib/twilio-ruby/rest/routes/v3.rb
|
|
775
|
+
- lib/twilio-ruby/rest/routes/v3/iso_country_code.rb
|
|
776
776
|
- lib/twilio-ruby/rest/routes/v3/phone_number.rb
|
|
777
777
|
- lib/twilio-ruby/rest/routes_base.rb
|
|
778
778
|
- lib/twilio-ruby/rest/serverless.rb
|
|
@@ -969,6 +969,16 @@ files:
|
|
|
969
969
|
- lib/twilio-ruby/rest/voice/v3.rb
|
|
970
970
|
- lib/twilio-ruby/rest/voice/v3/transcription.rb
|
|
971
971
|
- lib/twilio-ruby/rest/voice_base.rb
|
|
972
|
+
- lib/twilio-ruby/rest/webhooks.rb
|
|
973
|
+
- lib/twilio-ruby/rest/webhooks/v1.rb
|
|
974
|
+
- lib/twilio-ruby/rest/webhooks/v1/auth_profile.rb
|
|
975
|
+
- lib/twilio-ruby/rest/webhooks/v1/edge_zone.rb
|
|
976
|
+
- lib/twilio-ruby/rest/webhooks/v1/operation.rb
|
|
977
|
+
- lib/twilio-ruby/rest/webhooks/v1/rule.rb
|
|
978
|
+
- lib/twilio-ruby/rest/webhooks/v1/setting.rb
|
|
979
|
+
- lib/twilio-ruby/rest/webhooks/v1/shared_key.rb
|
|
980
|
+
- lib/twilio-ruby/rest/webhooks/v1/test.rb
|
|
981
|
+
- lib/twilio-ruby/rest/webhooks_base.rb
|
|
972
982
|
- lib/twilio-ruby/rest/wireless.rb
|
|
973
983
|
- lib/twilio-ruby/rest/wireless/v1.rb
|
|
974
984
|
- lib/twilio-ruby/rest/wireless/v1/command.rb
|