voiceml 0.8.1 → 0.9.2
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/README.md +13 -6
- data/lib/voiceml/client.rb +29 -2
- data/lib/voiceml/hosts.rb +62 -0
- data/lib/voiceml/models/accounts.rb +54 -0
- data/lib/voiceml/models/assistants_v1.rb +239 -0
- data/lib/voiceml/models/conversations_v1.rb +610 -0
- data/lib/voiceml/models/media.rb +48 -0
- data/lib/voiceml/models/messaging_v1.rb +40 -0
- data/lib/voiceml/models/notifications.rb +39 -0
- data/lib/voiceml/models/outgoing_caller_ids.rb +70 -0
- data/lib/voiceml/models/pricing.rb +231 -0
- data/lib/voiceml/models/routes_v2.rb +14 -0
- data/lib/voiceml/models/sip.rb +22 -0
- data/lib/voiceml/models/user_defined_messages.rb +31 -0
- data/lib/voiceml/models/voice_v1.rb +152 -0
- data/lib/voiceml/resources/assistants_v1.rb +508 -0
- data/lib/voiceml/resources/conversations_v1.rb +1175 -0
- data/lib/voiceml/resources/messaging_v1.rb +87 -0
- data/lib/voiceml/resources/pricing.rb +140 -0
- data/lib/voiceml/resources/routes_v2.rb +22 -2
- data/lib/voiceml/resources/voice_v1.rb +250 -0
- data/lib/voiceml/transport.rb +44 -4
- data/lib/voiceml/version.rb +1 -1
- data/lib/voiceml.rb +11 -0
- metadata +18 -2
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
|
|
5
|
+
module VoiceML
|
|
6
|
+
# Twilio-compatible Notification resource — a log row for a specific webhook /
|
|
7
|
+
# TwiML invocation failure (HTTP non-2xx, parse error, etc.).
|
|
8
|
+
#
|
|
9
|
+
# VoiceML treats `/Notifications` and `/Calls/{Sid}/Notifications` as compat
|
|
10
|
+
# stubs (always-empty list, fetch-by-sid returns 404), so this model decodes
|
|
11
|
+
# Twilio's documented shape for migration tooling and conformance checks
|
|
12
|
+
# without implying voiceml emits notification rows.
|
|
13
|
+
#
|
|
14
|
+
# The same resource shape backs both account-scoped and call-scoped fetches.
|
|
15
|
+
class Notification
|
|
16
|
+
ATTRIBUTES = %w[
|
|
17
|
+
sid account_sid call_sid api_version
|
|
18
|
+
error_code more_info message_date message_text log
|
|
19
|
+
request_method request_url request_variables
|
|
20
|
+
response_body response_headers
|
|
21
|
+
date_created date_updated uri
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
25
|
+
|
|
26
|
+
def initialize(attrs = {})
|
|
27
|
+
ATTRIBUTES.each do |field|
|
|
28
|
+
value = attrs.key?(field) ? attrs[field] : attrs[field.to_sym]
|
|
29
|
+
instance_variable_set("@#{field}", value)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.from_hash(hash)
|
|
34
|
+
return nil if hash.nil?
|
|
35
|
+
|
|
36
|
+
new(hash)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
|
|
5
|
+
module VoiceML
|
|
6
|
+
# Twilio-compatible OutgoingCallerId resource. The `sid` is `PN`-prefixed
|
|
7
|
+
# to mirror Twilio (caller-id resources share the IncomingPhoneNumber SID
|
|
8
|
+
# space upstream); `phone_number` carries the verified E.164 number.
|
|
9
|
+
class OutgoingCallerId
|
|
10
|
+
ATTRIBUTES = %w[
|
|
11
|
+
sid account_sid friendly_name phone_number
|
|
12
|
+
date_created date_updated uri
|
|
13
|
+
].freeze
|
|
14
|
+
|
|
15
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
16
|
+
|
|
17
|
+
def initialize(attrs = {})
|
|
18
|
+
ATTRIBUTES.each do |field|
|
|
19
|
+
value = attrs.key?(field) ? attrs[field] : attrs[field.to_sym]
|
|
20
|
+
instance_variable_set("@#{field}", value)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.from_hash(hash)
|
|
25
|
+
return nil if hash.nil?
|
|
26
|
+
|
|
27
|
+
new(hash)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Paginated `GET /OutgoingCallerIds` response.
|
|
32
|
+
class OutgoingCallerIdList
|
|
33
|
+
include Pageable
|
|
34
|
+
|
|
35
|
+
attr_reader :outgoing_caller_ids
|
|
36
|
+
|
|
37
|
+
def initialize(hash = {})
|
|
38
|
+
assign_page_fields(hash)
|
|
39
|
+
@outgoing_caller_ids =
|
|
40
|
+
(hash['outgoing_caller_ids'] || []).map { |o| OutgoingCallerId.from_hash(o) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.from_hash(hash)
|
|
44
|
+
new(hash || {})
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# `POST /OutgoingCallerIds` response — the verify-by-callback flow used to
|
|
49
|
+
# provision a new outgoing caller id. No `sid` on the wire (the resource
|
|
50
|
+
# doesn't exist until the validation_code is dialled back); the
|
|
51
|
+
# `validation_code` is what the user is prompted to enter on the live call.
|
|
52
|
+
class ValidationRequest
|
|
53
|
+
ATTRIBUTES = %w[account_sid call_sid friendly_name phone_number validation_code].freeze
|
|
54
|
+
|
|
55
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
56
|
+
|
|
57
|
+
def initialize(attrs = {})
|
|
58
|
+
ATTRIBUTES.each do |field|
|
|
59
|
+
value = attrs.key?(field) ? attrs[field] : attrs[field.to_sym]
|
|
60
|
+
instance_variable_set("@#{field}", value)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.from_hash(hash)
|
|
65
|
+
return nil if hash.nil?
|
|
66
|
+
|
|
67
|
+
new(hash)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
require_relative 'voice_v1' # V1Pageable lives here; PricingCountriesList reuses the `meta` envelope
|
|
5
|
+
|
|
6
|
+
module VoiceML
|
|
7
|
+
# Twilio Pricing (pricing.twilio.com) v1/v2 response models.
|
|
8
|
+
#
|
|
9
|
+
# VoiceML has no dedicated pricing subdomain, so these ride the default host
|
|
10
|
+
# (`voiceml.voicetel.com`) under `/v1` and `/v2`. All operations are read-only GETs.
|
|
11
|
+
# Every field is permissive/nullable — VoiceML is NANP-only, so a `Countries` list
|
|
12
|
+
# carries exactly one entry and a `Numbers` fetch 404s for non-NANP destinations.
|
|
13
|
+
|
|
14
|
+
# --- Price leaves ---------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
class PricingInboundCallPrice
|
|
17
|
+
ATTRIBUTES = %w[base_price current_price number_type].freeze
|
|
18
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
19
|
+
def initialize(attrs = {})
|
|
20
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
21
|
+
end
|
|
22
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class PricingOutboundCallPrice
|
|
26
|
+
ATTRIBUTES = %w[base_price current_price].freeze
|
|
27
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
28
|
+
def initialize(attrs = {})
|
|
29
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
30
|
+
end
|
|
31
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class PricingOutboundCallPriceWithOrigin
|
|
35
|
+
attr_reader :origination_prefixes, :base_price, :current_price
|
|
36
|
+
def initialize(attrs = {})
|
|
37
|
+
@origination_prefixes = attrs['origination_prefixes'] || attrs[:origination_prefixes] || []
|
|
38
|
+
@base_price = attrs['base_price'] || attrs[:base_price]
|
|
39
|
+
@current_price = attrs['current_price'] || attrs[:current_price]
|
|
40
|
+
end
|
|
41
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class PricingOutboundPrefixPrice
|
|
45
|
+
attr_reader :prefixes, :base_price, :current_price, :friendly_name
|
|
46
|
+
def initialize(attrs = {})
|
|
47
|
+
@prefixes = attrs['prefixes'] || attrs[:prefixes] || []
|
|
48
|
+
@base_price = attrs['base_price'] || attrs[:base_price]
|
|
49
|
+
@current_price = attrs['current_price'] || attrs[:current_price]
|
|
50
|
+
@friendly_name = attrs['friendly_name'] || attrs[:friendly_name]
|
|
51
|
+
end
|
|
52
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class PricingOutboundPrefixPriceWithOrigin
|
|
56
|
+
attr_reader :origination_prefixes, :destination_prefixes, :base_price, :current_price, :friendly_name
|
|
57
|
+
def initialize(attrs = {})
|
|
58
|
+
@origination_prefixes = attrs['origination_prefixes'] || attrs[:origination_prefixes] || []
|
|
59
|
+
@destination_prefixes = attrs['destination_prefixes'] || attrs[:destination_prefixes] || []
|
|
60
|
+
@base_price = attrs['base_price'] || attrs[:base_price]
|
|
61
|
+
@current_price = attrs['current_price'] || attrs[:current_price]
|
|
62
|
+
@friendly_name = attrs['friendly_name'] || attrs[:friendly_name]
|
|
63
|
+
end
|
|
64
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class PricingOutboundSMSPrice
|
|
68
|
+
attr_reader :carrier, :mcc, :mnc, :prices
|
|
69
|
+
def initialize(attrs = {})
|
|
70
|
+
@carrier = attrs['carrier'] || attrs[:carrier]
|
|
71
|
+
@mcc = attrs['mcc'] || attrs[:mcc]
|
|
72
|
+
@mnc = attrs['mnc'] || attrs[:mnc]
|
|
73
|
+
@prices = (attrs['prices'] || attrs[:prices] || []).map { |h| PricingInboundCallPrice.from_hash(h) }
|
|
74
|
+
end
|
|
75
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class PricingPhoneNumberPrice
|
|
79
|
+
ATTRIBUTES = %w[number_type base_price current_price].freeze
|
|
80
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
81
|
+
def initialize(attrs = {})
|
|
82
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
83
|
+
end
|
|
84
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# --- Countries list envelope ----------------------------------------------
|
|
88
|
+
|
|
89
|
+
class PricingCountryRef
|
|
90
|
+
ATTRIBUTES = %w[country iso_country url].freeze
|
|
91
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
92
|
+
def initialize(attrs = {})
|
|
93
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
94
|
+
end
|
|
95
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class PricingCountriesList
|
|
99
|
+
include V1Pageable
|
|
100
|
+
attr_reader :countries
|
|
101
|
+
def initialize(hash = {})
|
|
102
|
+
assign_meta_fields(hash)
|
|
103
|
+
@countries = (hash['countries'] || []).map { |h| PricingCountryRef.from_hash(h) }
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# --- Pricing v1 country / number bodies -----------------------------------
|
|
108
|
+
|
|
109
|
+
class PricingVoiceCountry
|
|
110
|
+
attr_reader :country, :iso_country, :outbound_prefix_prices, :inbound_call_prices, :price_unit, :url
|
|
111
|
+
def initialize(attrs = {})
|
|
112
|
+
@country = attrs['country'] || attrs[:country]
|
|
113
|
+
@iso_country = attrs['iso_country'] || attrs[:iso_country]
|
|
114
|
+
@outbound_prefix_prices =
|
|
115
|
+
(attrs['outbound_prefix_prices'] || []).map { |h| PricingOutboundPrefixPrice.from_hash(h) }
|
|
116
|
+
@inbound_call_prices =
|
|
117
|
+
(attrs['inbound_call_prices'] || []).map { |h| PricingInboundCallPrice.from_hash(h) }
|
|
118
|
+
@price_unit = attrs['price_unit'] || attrs[:price_unit]
|
|
119
|
+
@url = attrs['url'] || attrs[:url]
|
|
120
|
+
end
|
|
121
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class PricingVoiceNumber
|
|
125
|
+
attr_reader :number, :country, :iso_country, :outbound_call_price, :inbound_call_price, :price_unit, :url
|
|
126
|
+
def initialize(attrs = {})
|
|
127
|
+
@number = attrs['number'] || attrs[:number]
|
|
128
|
+
@country = attrs['country'] || attrs[:country]
|
|
129
|
+
@iso_country = attrs['iso_country'] || attrs[:iso_country]
|
|
130
|
+
@outbound_call_price = PricingOutboundCallPrice.from_hash(attrs['outbound_call_price'])
|
|
131
|
+
@inbound_call_price = PricingInboundCallPrice.from_hash(attrs['inbound_call_price'])
|
|
132
|
+
@price_unit = attrs['price_unit'] || attrs[:price_unit]
|
|
133
|
+
@url = attrs['url'] || attrs[:url]
|
|
134
|
+
end
|
|
135
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
class PricingMessagingCountry
|
|
139
|
+
attr_reader :country, :iso_country, :outbound_sms_prices, :inbound_sms_prices, :price_unit, :url
|
|
140
|
+
def initialize(attrs = {})
|
|
141
|
+
@country = attrs['country'] || attrs[:country]
|
|
142
|
+
@iso_country = attrs['iso_country'] || attrs[:iso_country]
|
|
143
|
+
@outbound_sms_prices =
|
|
144
|
+
(attrs['outbound_sms_prices'] || []).map { |h| PricingOutboundSMSPrice.from_hash(h) }
|
|
145
|
+
@inbound_sms_prices =
|
|
146
|
+
(attrs['inbound_sms_prices'] || []).map { |h| PricingInboundCallPrice.from_hash(h) }
|
|
147
|
+
@price_unit = attrs['price_unit'] || attrs[:price_unit]
|
|
148
|
+
@url = attrs['url'] || attrs[:url]
|
|
149
|
+
end
|
|
150
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class PricingPhoneNumberCountry
|
|
154
|
+
attr_reader :country, :iso_country, :phone_number_prices, :price_unit, :url
|
|
155
|
+
def initialize(attrs = {})
|
|
156
|
+
@country = attrs['country'] || attrs[:country]
|
|
157
|
+
@iso_country = attrs['iso_country'] || attrs[:iso_country]
|
|
158
|
+
@phone_number_prices =
|
|
159
|
+
(attrs['phone_number_prices'] || []).map { |h| PricingPhoneNumberPrice.from_hash(h) }
|
|
160
|
+
@price_unit = attrs['price_unit'] || attrs[:price_unit]
|
|
161
|
+
@url = attrs['url'] || attrs[:url]
|
|
162
|
+
end
|
|
163
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# --- Pricing v2 country / number bodies -----------------------------------
|
|
167
|
+
|
|
168
|
+
class PricingVoiceCountryV2
|
|
169
|
+
attr_reader :country, :iso_country, :outbound_prefix_prices, :inbound_call_prices, :price_unit, :url
|
|
170
|
+
def initialize(attrs = {})
|
|
171
|
+
@country = attrs['country'] || attrs[:country]
|
|
172
|
+
@iso_country = attrs['iso_country'] || attrs[:iso_country]
|
|
173
|
+
@outbound_prefix_prices =
|
|
174
|
+
(attrs['outbound_prefix_prices'] || []).map { |h| PricingOutboundPrefixPriceWithOrigin.from_hash(h) }
|
|
175
|
+
@inbound_call_prices =
|
|
176
|
+
(attrs['inbound_call_prices'] || []).map { |h| PricingInboundCallPrice.from_hash(h) }
|
|
177
|
+
@price_unit = attrs['price_unit'] || attrs[:price_unit]
|
|
178
|
+
@url = attrs['url'] || attrs[:url]
|
|
179
|
+
end
|
|
180
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
class PricingVoiceNumberV2
|
|
184
|
+
attr_reader :destination_number, :origination_number, :country, :iso_country,
|
|
185
|
+
:outbound_call_prices, :inbound_call_price, :price_unit, :url
|
|
186
|
+
def initialize(attrs = {})
|
|
187
|
+
@destination_number = attrs['destination_number'] || attrs[:destination_number]
|
|
188
|
+
@origination_number = attrs['origination_number'] || attrs[:origination_number]
|
|
189
|
+
@country = attrs['country'] || attrs[:country]
|
|
190
|
+
@iso_country = attrs['iso_country'] || attrs[:iso_country]
|
|
191
|
+
@outbound_call_prices =
|
|
192
|
+
(attrs['outbound_call_prices'] || []).map { |h| PricingOutboundCallPriceWithOrigin.from_hash(h) }
|
|
193
|
+
@inbound_call_price = PricingInboundCallPrice.from_hash(attrs['inbound_call_price'])
|
|
194
|
+
@price_unit = attrs['price_unit'] || attrs[:price_unit]
|
|
195
|
+
@url = attrs['url'] || attrs[:url]
|
|
196
|
+
end
|
|
197
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
class PricingTrunkingCountry
|
|
201
|
+
attr_reader :country, :iso_country, :terminating_prefix_prices, :originating_call_prices, :price_unit, :url
|
|
202
|
+
def initialize(attrs = {})
|
|
203
|
+
@country = attrs['country'] || attrs[:country]
|
|
204
|
+
@iso_country = attrs['iso_country'] || attrs[:iso_country]
|
|
205
|
+
@terminating_prefix_prices =
|
|
206
|
+
(attrs['terminating_prefix_prices'] || []).map { |h| PricingOutboundPrefixPriceWithOrigin.from_hash(h) }
|
|
207
|
+
@originating_call_prices =
|
|
208
|
+
(attrs['originating_call_prices'] || []).map { |h| PricingInboundCallPrice.from_hash(h) }
|
|
209
|
+
@price_unit = attrs['price_unit'] || attrs[:price_unit]
|
|
210
|
+
@url = attrs['url'] || attrs[:url]
|
|
211
|
+
end
|
|
212
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
class PricingTrunkingNumber
|
|
216
|
+
attr_reader :destination_number, :origination_number, :country, :iso_country,
|
|
217
|
+
:terminating_prefix_prices, :originating_call_price, :price_unit, :url
|
|
218
|
+
def initialize(attrs = {})
|
|
219
|
+
@destination_number = attrs['destination_number'] || attrs[:destination_number]
|
|
220
|
+
@origination_number = attrs['origination_number'] || attrs[:origination_number]
|
|
221
|
+
@country = attrs['country'] || attrs[:country]
|
|
222
|
+
@iso_country = attrs['iso_country'] || attrs[:iso_country]
|
|
223
|
+
@terminating_prefix_prices =
|
|
224
|
+
(attrs['terminating_prefix_prices'] || []).map { |h| PricingOutboundPrefixPriceWithOrigin.from_hash(h) }
|
|
225
|
+
@originating_call_price = PricingInboundCallPrice.from_hash(attrs['originating_call_price'])
|
|
226
|
+
@price_unit = attrs['price_unit'] || attrs[:price_unit]
|
|
227
|
+
@url = attrs['url'] || attrs[:url]
|
|
228
|
+
end
|
|
229
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
@@ -14,4 +14,18 @@ module VoiceML
|
|
|
14
14
|
end
|
|
15
15
|
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
# Twilio routes/v2 Inbound Processing Region binding for a claimed phone number.
|
|
19
|
+
# Keyed by E.164 phone number or its PN sid; account resolved from HTTP Basic auth.
|
|
20
|
+
class RoutesV2PhoneNumber
|
|
21
|
+
ATTRIBUTES = %w[
|
|
22
|
+
sid phone_number account_sid friendly_name voice_region url
|
|
23
|
+
date_created date_updated
|
|
24
|
+
].freeze
|
|
25
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
26
|
+
def initialize(attrs = {})
|
|
27
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
28
|
+
end
|
|
29
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
30
|
+
end
|
|
17
31
|
end
|
data/lib/voiceml/models/sip.rb
CHANGED
|
@@ -37,6 +37,7 @@ module VoiceML
|
|
|
37
37
|
assign_page_fields(hash)
|
|
38
38
|
@domains = (hash['domains'] || []).map { |h| SipDomain.from_hash(h) }
|
|
39
39
|
end
|
|
40
|
+
def self.from_hash(hash); new(hash || {}); end
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
# SipCredentialList — `CL...`.
|
|
@@ -58,6 +59,7 @@ module VoiceML
|
|
|
58
59
|
assign_page_fields(hash)
|
|
59
60
|
@credential_lists = (hash['credential_lists'] || []).map { |h| SipCredentialList.from_hash(h) }
|
|
60
61
|
end
|
|
62
|
+
def self.from_hash(hash); new(hash || {}); end
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
# SipCredential — `CR...`. Password is write-only (never returned).
|
|
@@ -82,6 +84,7 @@ module VoiceML
|
|
|
82
84
|
assign_page_fields(hash)
|
|
83
85
|
@credentials = (hash['credentials'] || []).map { |h| SipCredential.from_hash(h) }
|
|
84
86
|
end
|
|
87
|
+
def self.from_hash(hash); new(hash || {}); end
|
|
85
88
|
end
|
|
86
89
|
|
|
87
90
|
# SipIpAccessControlList — `AL...`.
|
|
@@ -103,6 +106,7 @@ module VoiceML
|
|
|
103
106
|
assign_page_fields(hash)
|
|
104
107
|
@ip_access_control_lists = (hash['ip_access_control_lists'] || []).map { |h| SipIpAccessControlList.from_hash(h) }
|
|
105
108
|
end
|
|
109
|
+
def self.from_hash(hash); new(hash || {}); end
|
|
106
110
|
end
|
|
107
111
|
|
|
108
112
|
# SipIpAddress — `IP...`.
|
|
@@ -126,6 +130,7 @@ module VoiceML
|
|
|
126
130
|
assign_page_fields(hash)
|
|
127
131
|
@ip_addresses = (hash['ip_addresses'] || []).map { |h| SipIpAddress.from_hash(h) }
|
|
128
132
|
end
|
|
133
|
+
def self.from_hash(hash); new(hash || {}); end
|
|
129
134
|
end
|
|
130
135
|
|
|
131
136
|
# SipDomainMapping — round-trip shape for every domain mapping sub-resource.
|
|
@@ -149,6 +154,7 @@ module VoiceML
|
|
|
149
154
|
assign_page_fields(hash)
|
|
150
155
|
@credential_list_mappings = (hash['credential_list_mappings'] || []).map { |h| SipDomainMapping.from_hash(h) }
|
|
151
156
|
end
|
|
157
|
+
def self.from_hash(hash); new(hash || {}); end
|
|
152
158
|
end
|
|
153
159
|
|
|
154
160
|
class SipIpAccessControlListMappingList
|
|
@@ -158,5 +164,21 @@ module VoiceML
|
|
|
158
164
|
assign_page_fields(hash)
|
|
159
165
|
@ip_access_control_list_mappings = (hash['ip_access_control_list_mappings'] || []).map { |h| SipDomainMapping.from_hash(h) }
|
|
160
166
|
end
|
|
167
|
+
def self.from_hash(hash); new(hash || {}); end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# SipAuthMappingList — page envelope used by all four `/Auth/*` mapping list
|
|
171
|
+
# endpoints. Twilio's spec uses a generic `contents` envelope key here (not
|
|
172
|
+
# `credential_list_mappings` / `ip_access_control_list_mappings`) because
|
|
173
|
+
# the same list shape is reused across CredentialList and IpAccessControlList
|
|
174
|
+
# auth mappings; the inner items decode as SipDomainMapping either way.
|
|
175
|
+
class SipAuthMappingList
|
|
176
|
+
include Pageable
|
|
177
|
+
attr_reader :contents
|
|
178
|
+
def initialize(hash = {})
|
|
179
|
+
assign_page_fields(hash)
|
|
180
|
+
@contents = (hash['contents'] || []).map { |h| SipDomainMapping.from_hash(h) }
|
|
181
|
+
end
|
|
182
|
+
def self.from_hash(hash); new(hash || {}); end
|
|
161
183
|
end
|
|
162
184
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
|
|
5
|
+
module VoiceML
|
|
6
|
+
# Twilio-compatible UserDefinedMessage resource — out-of-band messages a
|
|
7
|
+
# caller can attach to an in-progress call, delivered via the SDK 2.x
|
|
8
|
+
# WebSocket. VoiceML doesn't surface this as a first-class resource yet;
|
|
9
|
+
# the model exists to decode the response shape (KX-prefixed sid +
|
|
10
|
+
# account/call sids) for conformance and round-trip tooling.
|
|
11
|
+
class UserDefinedMessage
|
|
12
|
+
ATTRIBUTES = %w[
|
|
13
|
+
sid account_sid call_sid date_created
|
|
14
|
+
].freeze
|
|
15
|
+
|
|
16
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
17
|
+
|
|
18
|
+
def initialize(attrs = {})
|
|
19
|
+
ATTRIBUTES.each do |field|
|
|
20
|
+
value = attrs.key?(field) ? attrs[field] : attrs[field.to_sym]
|
|
21
|
+
instance_variable_set("@#{field}", value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.from_hash(hash)
|
|
26
|
+
return nil if hash.nil?
|
|
27
|
+
|
|
28
|
+
new(hash)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'common'
|
|
4
|
+
|
|
5
|
+
module VoiceML
|
|
6
|
+
# Twilio Voice v1 (voice.twilio.com/v1) resources.
|
|
7
|
+
#
|
|
8
|
+
# Distinct from the 2010-04-01 surface in two ways:
|
|
9
|
+
# - No `/Accounts/{AccountSid}` segment — account resolves from HTTP Basic auth.
|
|
10
|
+
# - List pages carry a `meta` envelope (URLs + page/page_size + key) instead of
|
|
11
|
+
# the flat Twilio2010 `next_page_uri` shape.
|
|
12
|
+
#
|
|
13
|
+
# Six resources implemented for v0.9.0:
|
|
14
|
+
# - ByocTrunk (BY...) — bring-your-own-carrier trunk
|
|
15
|
+
# - ConnectionPolicy (NY...) — origination policy
|
|
16
|
+
# - ConnectionPolicyTarget (NE...)
|
|
17
|
+
# - Settings — DialingPermissions inheritance
|
|
18
|
+
# - SourceIpMapping (IB...) — bind IpRecord -> SipDomain
|
|
19
|
+
# - IpRecord (IL...) — standalone allowed source IP
|
|
20
|
+
|
|
21
|
+
# Shared `meta` envelope for `/v1/*` list responses.
|
|
22
|
+
module V1Pageable
|
|
23
|
+
META_FIELDS = %w[first_page_url next_page_url previous_page_url url page page_size key].freeze
|
|
24
|
+
|
|
25
|
+
META_FIELDS.each { |f| attr_reader f.to_sym }
|
|
26
|
+
|
|
27
|
+
def assign_meta_fields(hash)
|
|
28
|
+
meta = hash['meta'] || {}
|
|
29
|
+
META_FIELDS.each { |f| instance_variable_set("@#{f}", meta[f]) }
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# VoiceV1IpRecord — `IL...`.
|
|
34
|
+
class VoiceV1IpRecord
|
|
35
|
+
ATTRIBUTES = %w[
|
|
36
|
+
account_sid sid friendly_name ip_address cidr_prefix_length
|
|
37
|
+
date_created date_updated url
|
|
38
|
+
].freeze
|
|
39
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
40
|
+
def initialize(attrs = {})
|
|
41
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
42
|
+
end
|
|
43
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class VoiceV1IpRecordList
|
|
47
|
+
include V1Pageable
|
|
48
|
+
attr_reader :ip_records
|
|
49
|
+
def initialize(hash = {})
|
|
50
|
+
assign_meta_fields(hash)
|
|
51
|
+
@ip_records = (hash['ip_records'] || []).map { |h| VoiceV1IpRecord.from_hash(h) }
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# VoiceV1SourceIpMapping — `IB...`. Binds an IpRecord to a SipDomain.
|
|
56
|
+
class VoiceV1SourceIpMapping
|
|
57
|
+
ATTRIBUTES = %w[
|
|
58
|
+
sid ip_record_sid sip_domain_sid date_created date_updated url
|
|
59
|
+
].freeze
|
|
60
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
61
|
+
def initialize(attrs = {})
|
|
62
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
63
|
+
end
|
|
64
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class VoiceV1SourceIpMappingList
|
|
68
|
+
include V1Pageable
|
|
69
|
+
attr_reader :source_ip_mappings
|
|
70
|
+
def initialize(hash = {})
|
|
71
|
+
assign_meta_fields(hash)
|
|
72
|
+
@source_ip_mappings = (hash['source_ip_mappings'] || []).map { |h| VoiceV1SourceIpMapping.from_hash(h) }
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# VoiceV1ByocTrunk — `BY...`. Bring-your-own-carrier trunk.
|
|
77
|
+
class VoiceV1ByocTrunk
|
|
78
|
+
ATTRIBUTES = %w[
|
|
79
|
+
account_sid sid friendly_name voice_url voice_method voice_fallback_url
|
|
80
|
+
voice_fallback_method status_callback_url status_callback_method
|
|
81
|
+
cnam_lookup_enabled connection_policy_sid from_domain_sid
|
|
82
|
+
date_created date_updated url
|
|
83
|
+
].freeze
|
|
84
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
85
|
+
def initialize(attrs = {})
|
|
86
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
87
|
+
end
|
|
88
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class VoiceV1ByocTrunkList
|
|
92
|
+
include V1Pageable
|
|
93
|
+
attr_reader :byoc_trunks
|
|
94
|
+
def initialize(hash = {})
|
|
95
|
+
assign_meta_fields(hash)
|
|
96
|
+
@byoc_trunks = (hash['byoc_trunks'] || []).map { |h| VoiceV1ByocTrunk.from_hash(h) }
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# VoiceV1ConnectionPolicy — `NY...`.
|
|
101
|
+
class VoiceV1ConnectionPolicy
|
|
102
|
+
ATTRIBUTES = %w[
|
|
103
|
+
account_sid sid friendly_name date_created date_updated url links
|
|
104
|
+
].freeze
|
|
105
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
106
|
+
def initialize(attrs = {})
|
|
107
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
108
|
+
end
|
|
109
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class VoiceV1ConnectionPolicyList
|
|
113
|
+
include V1Pageable
|
|
114
|
+
attr_reader :connection_policies
|
|
115
|
+
def initialize(hash = {})
|
|
116
|
+
assign_meta_fields(hash)
|
|
117
|
+
@connection_policies = (hash['connection_policies'] || []).map { |h| VoiceV1ConnectionPolicy.from_hash(h) }
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# VoiceV1ConnectionPolicyTarget — `NE...`. Scoped to a parent ConnectionPolicy.
|
|
122
|
+
class VoiceV1ConnectionPolicyTarget
|
|
123
|
+
ATTRIBUTES = %w[
|
|
124
|
+
account_sid connection_policy_sid sid friendly_name target priority weight
|
|
125
|
+
enabled date_created date_updated url
|
|
126
|
+
].freeze
|
|
127
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
128
|
+
def initialize(attrs = {})
|
|
129
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
130
|
+
end
|
|
131
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class VoiceV1ConnectionPolicyTargetList
|
|
135
|
+
include V1Pageable
|
|
136
|
+
attr_reader :targets
|
|
137
|
+
def initialize(hash = {})
|
|
138
|
+
assign_meta_fields(hash)
|
|
139
|
+
@targets = (hash['targets'] || []).map { |h| VoiceV1ConnectionPolicyTarget.from_hash(h) }
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# VoiceV1DialingPermissionsSettings — `/v1/Settings`.
|
|
144
|
+
class VoiceV1DialingPermissionsSettings
|
|
145
|
+
ATTRIBUTES = %w[dialing_permissions_inheritance url].freeze
|
|
146
|
+
attr_reader(*ATTRIBUTES.map(&:to_sym))
|
|
147
|
+
def initialize(attrs = {})
|
|
148
|
+
ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs[f] || attrs[f.to_sym]) }
|
|
149
|
+
end
|
|
150
|
+
def self.from_hash(h); h.nil? ? nil : new(h); end
|
|
151
|
+
end
|
|
152
|
+
end
|