voiceml 0.9.2 → 0.9.3
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 +17 -13
- data/lib/voiceml/client.rb +53 -13
- data/lib/voiceml/errors.rb +15 -2
- data/lib/voiceml/models/accounts.rb +47 -3
- data/lib/voiceml/models/applications.rb +6 -0
- data/lib/voiceml/models/assistants_v1.rb +40 -0
- data/lib/voiceml/models/calls.rb +4 -0
- data/lib/voiceml/models/common.rb +35 -4
- data/lib/voiceml/models/conferences.rb +8 -0
- data/lib/voiceml/models/conversations_v1.rb +131 -29
- data/lib/voiceml/models/diagnostics.rb +33 -12
- data/lib/voiceml/models/exports.rb +58 -0
- data/lib/voiceml/models/incoming_phone_numbers.rb +4 -0
- data/lib/voiceml/models/media.rb +4 -0
- data/lib/voiceml/models/messages.rb +29 -0
- data/lib/voiceml/models/messaging_v1.rb +35 -2
- data/lib/voiceml/models/monitor_v1.rb +54 -0
- data/lib/voiceml/models/notifications.rb +2 -0
- data/lib/voiceml/models/outgoing_caller_ids.rb +6 -0
- data/lib/voiceml/models/payments.rb +2 -0
- data/lib/voiceml/models/pricing.rb +38 -4
- data/lib/voiceml/models/queues.rb +8 -0
- data/lib/voiceml/models/recordings.rb +5 -1
- data/lib/voiceml/models/routes_v2.rb +6 -2
- data/lib/voiceml/models/sip.rb +34 -6
- data/lib/voiceml/models/sip_registrations.rb +49 -0
- data/lib/voiceml/models/siprec.rb +5 -17
- data/lib/voiceml/models/streams.rb +4 -16
- data/lib/voiceml/models/transcriptions.rb +35 -4
- data/lib/voiceml/models/usage_records.rb +48 -0
- data/lib/voiceml/models/user_defined_messages.rb +2 -0
- data/lib/voiceml/models/voice_v1.rb +29 -19
- data/lib/voiceml/request_validator.rb +125 -0
- data/lib/voiceml/resources/accounts.rb +75 -0
- data/lib/voiceml/resources/applications.rb +3 -1
- data/lib/voiceml/resources/assistants_v1.rb +53 -77
- data/lib/voiceml/resources/base.rb +138 -16
- data/lib/voiceml/resources/calls.rb +34 -49
- data/lib/voiceml/resources/conferences.rb +26 -5
- data/lib/voiceml/resources/conversations_v1.rb +147 -172
- data/lib/voiceml/resources/diagnostics.rb +20 -35
- data/lib/voiceml/resources/exports.rb +36 -0
- data/lib/voiceml/resources/incoming_phone_numbers.rb +53 -32
- data/lib/voiceml/resources/messages.rb +79 -15
- data/lib/voiceml/resources/messaging_v1.rb +47 -13
- data/lib/voiceml/resources/monitor_v1.rb +68 -0
- data/lib/voiceml/resources/notifications.rb +1 -1
- data/lib/voiceml/resources/outgoing_caller_ids.rb +77 -0
- data/lib/voiceml/resources/pricing.rb +13 -5
- data/lib/voiceml/resources/queues.rb +6 -5
- data/lib/voiceml/resources/recordings.rb +39 -5
- data/lib/voiceml/resources/routes_v2.rb +10 -4
- data/lib/voiceml/resources/sip.rb +30 -11
- data/lib/voiceml/resources/transcriptions.rb +40 -0
- data/lib/voiceml/resources/usage_records.rb +51 -0
- data/lib/voiceml/resources/voice_v1.rb +31 -38
- data/lib/voiceml/transport.rb +277 -230
- data/lib/voiceml/version.rb +1 -1
- data/lib/voiceml.rb +14 -15
- metadata +18 -6
|
@@ -1,64 +1,49 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'json'
|
|
4
|
-
require 'net/http'
|
|
5
|
-
require 'uri'
|
|
6
|
-
|
|
7
3
|
require_relative '../models/diagnostics'
|
|
8
4
|
|
|
9
5
|
module VoiceML
|
|
10
6
|
# Diagnostic surfaces — `/health` and the OpenAPI doc endpoints.
|
|
11
7
|
#
|
|
12
8
|
# These don't sit under `/2010-04-01/Accounts/{AccountSid}/...`; they're mounted at the
|
|
13
|
-
# server root and
|
|
9
|
+
# server root and require no credentials (the spec marks them `security: []`), so every
|
|
10
|
+
# call routes through the normal transport — the configured HTTP client, timeout, proxy
|
|
11
|
+
# and User-Agent apply — with auth omitted.
|
|
14
12
|
class DiagnosticsResource
|
|
15
13
|
def initialize(transport)
|
|
16
14
|
@transport = transport
|
|
17
15
|
end
|
|
18
16
|
|
|
19
17
|
# Hit `GET /health`. 200 = all hard checks pass; 503 raises `VoiceML::ServerError`
|
|
20
|
-
# with the failure list on `error.body`.
|
|
18
|
+
# with the failure list on `error.body`. `warnings`/`failures` are optional in the
|
|
19
|
+
# response body.
|
|
21
20
|
#
|
|
22
21
|
# @return [VoiceML::HealthStatus]
|
|
23
22
|
def health
|
|
24
|
-
HealthStatus.from_hash(
|
|
23
|
+
HealthStatus.from_hash(@transport.request(:get, '/health', auth: false))
|
|
25
24
|
end
|
|
26
25
|
|
|
27
26
|
# Fetch the OpenAPI spec as parsed JSON.
|
|
28
27
|
#
|
|
29
28
|
# @return [Hash]
|
|
30
29
|
def openapi_json
|
|
31
|
-
|
|
30
|
+
@transport.request(:get, '/openapi.json', auth: false)
|
|
32
31
|
end
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
42
|
-
http.use_ssl = uri.scheme == 'https'
|
|
43
|
-
http.open_timeout = 10
|
|
44
|
-
http.read_timeout = 10
|
|
45
|
-
response = http.start { |h| h.request(req) }
|
|
46
|
-
|
|
47
|
-
status = response.code.to_i
|
|
48
|
-
unless status >= 200 && status < 300
|
|
49
|
-
body = begin
|
|
50
|
-
response.body && !response.body.empty? ? JSON.parse(response.body) : response.body
|
|
51
|
-
rescue JSON::ParserError
|
|
52
|
-
response.body
|
|
53
|
-
end
|
|
54
|
-
message = body.is_a?(Hash) && body['message'].is_a?(String) ? body['message'] : "HTTP #{status}"
|
|
55
|
-
more_info = body.is_a?(Hash) && body['more_info'].is_a?(String) ? body['more_info'] : nil
|
|
56
|
-
raise VoiceML.error_from_response(status, message, body: body, more_info: more_info)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
return nil if response.body.nil? || response.body.empty?
|
|
33
|
+
# Fetch the OpenAPI spec as raw YAML (`/openapi.yaml`).
|
|
34
|
+
#
|
|
35
|
+
# @return [String]
|
|
36
|
+
def openapi_yaml
|
|
37
|
+
_status, body, _headers = @transport.request_raw(:get, '/openapi.yaml', auth: false)
|
|
38
|
+
body
|
|
39
|
+
end
|
|
60
40
|
|
|
61
|
-
|
|
41
|
+
# Fetch the OpenAPI spec as raw YAML (`/openapi.yml`).
|
|
42
|
+
#
|
|
43
|
+
# @return [String]
|
|
44
|
+
def openapi_yml
|
|
45
|
+
_status, body, _headers = @transport.request_raw(:get, '/openapi.yml', auth: false)
|
|
46
|
+
body
|
|
62
47
|
end
|
|
63
48
|
end
|
|
64
49
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative '../models/exports'
|
|
5
|
+
|
|
6
|
+
module VoiceML
|
|
7
|
+
# Operations on `/Exports` — the account's daily usage archives.
|
|
8
|
+
class ExportsResource < BaseResource
|
|
9
|
+
# List the account's available archives, newest first.
|
|
10
|
+
#
|
|
11
|
+
# @return [VoiceML::ExportList]
|
|
12
|
+
def list
|
|
13
|
+
ExportList.from_hash(@transport.request(:get, path('Exports')))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Download one day's archive. Like recording audio, the server may answer
|
|
17
|
+
# with the bytes directly or with a 302 to a presigned object; redirects
|
|
18
|
+
# are followed with credentials stripped and https -> http downgrades
|
|
19
|
+
# refused.
|
|
20
|
+
#
|
|
21
|
+
# @param date [String] archive day, `YYYY-MM-DD`.
|
|
22
|
+
# @return [VoiceML::ExportDownload]
|
|
23
|
+
def download(date)
|
|
24
|
+
status, content, headers = @transport.fetch_bytes(path('Exports', date, suffix: ''))
|
|
25
|
+
content_type = headers['content-type']
|
|
26
|
+
content_type = content_type.first if content_type.is_a?(Array)
|
|
27
|
+
x_amz_id = headers['x-amz-id-2']
|
|
28
|
+
ExportDownload.new(
|
|
29
|
+
date: date,
|
|
30
|
+
content: content,
|
|
31
|
+
content_type: content_type || 'application/octet-stream',
|
|
32
|
+
via_redirect: status == 200 && !x_amz_id.nil?
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -11,10 +11,13 @@ module VoiceML
|
|
|
11
11
|
# returning a 0-or-1-row envelope, then `get(sid)`) is supported.
|
|
12
12
|
class IncomingPhoneNumbersResource < BaseResource
|
|
13
13
|
LIST_FIELDS = {
|
|
14
|
-
'PhoneNumber'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
14
|
+
'PhoneNumber' => :phone_number,
|
|
15
|
+
'FriendlyName' => :friendly_name,
|
|
16
|
+
'Beta' => :beta,
|
|
17
|
+
'Origin' => :origin,
|
|
18
|
+
'Page' => :page,
|
|
19
|
+
'PageSize' => :page_size,
|
|
20
|
+
'PageToken' => :page_token
|
|
18
21
|
}.freeze
|
|
19
22
|
|
|
20
23
|
CREATE_FIELDS = {
|
|
@@ -23,7 +26,11 @@ module VoiceML
|
|
|
23
26
|
'VoiceMethod' => :voice_method,
|
|
24
27
|
'VoiceFallbackUrl' => :voice_fallback_url,
|
|
25
28
|
'VoiceFallbackMethod' => :voice_fallback_method,
|
|
26
|
-
'
|
|
29
|
+
'SmsUrl' => :sms_url,
|
|
30
|
+
'SmsMethod' => :sms_method,
|
|
31
|
+
'SmsFallbackUrl' => :sms_fallback_url,
|
|
32
|
+
'SmsFallbackMethod' => :sms_fallback_method,
|
|
33
|
+
'SmsApplicationSid' => :sms_application_sid
|
|
27
34
|
}.freeze
|
|
28
35
|
|
|
29
36
|
UPDATE_FIELDS = {
|
|
@@ -31,28 +38,36 @@ module VoiceML
|
|
|
31
38
|
'VoiceMethod' => :voice_method,
|
|
32
39
|
'VoiceFallbackUrl' => :voice_fallback_url,
|
|
33
40
|
'VoiceFallbackMethod' => :voice_fallback_method,
|
|
34
|
-
'
|
|
41
|
+
'SmsUrl' => :sms_url,
|
|
42
|
+
'SmsMethod' => :sms_method,
|
|
43
|
+
'SmsFallbackUrl' => :sms_fallback_url,
|
|
44
|
+
'SmsFallbackMethod' => :sms_fallback_method,
|
|
45
|
+
'SmsApplicationSid' => :sms_application_sid
|
|
35
46
|
}.freeze
|
|
36
47
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'PageSize' => :page_size,
|
|
44
|
-
'PageToken' => :page_token
|
|
48
|
+
# The typed Local/Mobile/TollFree creates take a reduced body: only
|
|
49
|
+
# `PhoneNumber`, `VoiceUrl` and `VoiceMethod`.
|
|
50
|
+
CREATE_TYPED_FIELDS = {
|
|
51
|
+
'PhoneNumber' => :phone_number,
|
|
52
|
+
'VoiceUrl' => :voice_url,
|
|
53
|
+
'VoiceMethod' => :voice_method
|
|
45
54
|
}.freeze
|
|
46
55
|
|
|
47
56
|
# List DIDs assigned to the authenticated tenant.
|
|
48
57
|
#
|
|
49
58
|
# @param phone_number [String, nil] exact-match E.164 filter. Returns a 0-or-1-row
|
|
50
59
|
# envelope when set — the standard twilio-ruby lookup pattern.
|
|
51
|
-
# @param
|
|
52
|
-
# @param
|
|
60
|
+
# @param friendly_name [String, nil] filter by display label.
|
|
61
|
+
# @param beta [Boolean, nil] filter new (beta) numbers.
|
|
62
|
+
# @param origin [String, nil] filter by number origin (`twilio`, `hosted`).
|
|
63
|
+
# @param page [Integer, nil] 0-indexed page number.
|
|
64
|
+
# @param page_size [Integer, nil] page size (server-bounded).
|
|
65
|
+
# @param page_token [String, nil] continuation token from a previous page.
|
|
53
66
|
# @return [VoiceML::IncomingPhoneNumberList]
|
|
54
|
-
def list(phone_number: nil,
|
|
55
|
-
|
|
67
|
+
def list(phone_number: nil, friendly_name: nil, beta: nil, origin: nil,
|
|
68
|
+
page: nil, page_size: nil, page_token: nil)
|
|
69
|
+
kwargs = { phone_number: phone_number, friendly_name: friendly_name, beta: beta,
|
|
70
|
+
origin: origin, page: page, page_size: page_size, page_token: page_token }
|
|
56
71
|
IncomingPhoneNumberList.from_hash(
|
|
57
72
|
@transport.request(:get, path('IncomingPhoneNumbers'),
|
|
58
73
|
params: form_params(LIST_FIELDS, kwargs))
|
|
@@ -62,22 +77,29 @@ module VoiceML
|
|
|
62
77
|
# Assign a DID to the authenticated tenant. Idempotent re-POSTing the same
|
|
63
78
|
# `phone_number:` rebinds the voice routing (matches Twilio update semantics).
|
|
64
79
|
#
|
|
65
|
-
# @param phone_number
|
|
66
|
-
# @param voice_url
|
|
67
|
-
# @param voice_method
|
|
68
|
-
# @param voice_fallback_url
|
|
80
|
+
# @param phone_number [String] required. E.164 — leading `+`, 7-15 digits.
|
|
81
|
+
# @param voice_url [String, nil] inbound-voice handler URL.
|
|
82
|
+
# @param voice_method [String, nil] HTTP method for `voice_url` (`GET`/`POST`).
|
|
83
|
+
# @param voice_fallback_url [String, nil] fallback handler URL.
|
|
69
84
|
# @param voice_fallback_method [String, nil] HTTP method for `voice_fallback_url`.
|
|
70
|
-
# @param
|
|
85
|
+
# @param sms_url [String, nil] inbound-SMS webhook URL.
|
|
86
|
+
# @param sms_method [String, nil] HTTP method for `sms_url`.
|
|
87
|
+
# @param sms_fallback_url [String, nil] inbound-SMS fallback URL.
|
|
88
|
+
# @param sms_fallback_method [String, nil] HTTP method for `sms_fallback_url`.
|
|
89
|
+
# @param sms_application_sid [String, nil] `AP`-prefixed Application that
|
|
90
|
+
# handles inbound SMS (`^AP[0-9a-fA-F]{32}$`).
|
|
71
91
|
# @return [VoiceML::IncomingPhoneNumber]
|
|
72
92
|
def create(phone_number:, voice_url: nil, voice_method: nil,
|
|
73
93
|
voice_fallback_url: nil, voice_fallback_method: nil,
|
|
74
|
-
|
|
94
|
+
sms_url: nil, sms_method: nil, sms_fallback_url: nil,
|
|
95
|
+
sms_fallback_method: nil, sms_application_sid: nil)
|
|
75
96
|
kwargs = {
|
|
76
97
|
phone_number: phone_number,
|
|
77
98
|
voice_url: voice_url, voice_method: voice_method,
|
|
78
|
-
voice_fallback_url: voice_fallback_url,
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
voice_fallback_url: voice_fallback_url, voice_fallback_method: voice_fallback_method,
|
|
100
|
+
sms_url: sms_url, sms_method: sms_method,
|
|
101
|
+
sms_fallback_url: sms_fallback_url, sms_fallback_method: sms_fallback_method,
|
|
102
|
+
sms_application_sid: sms_application_sid
|
|
81
103
|
}
|
|
82
104
|
IncomingPhoneNumber.from_hash(
|
|
83
105
|
@transport.request(:post, path('IncomingPhoneNumbers'),
|
|
@@ -93,11 +115,10 @@ module VoiceML
|
|
|
93
115
|
)
|
|
94
116
|
end
|
|
95
117
|
|
|
96
|
-
# Update voice routing on an assigned DID. Only set fields are touched.
|
|
118
|
+
# Update voice/SMS routing on an assigned DID. Only set fields are touched.
|
|
97
119
|
#
|
|
98
120
|
# @param sid [String] the `PN`-prefixed identifier.
|
|
99
|
-
# @param opts [Hash] any of
|
|
100
|
-
# `voice_fallback_method:`, `friendly_name:`.
|
|
121
|
+
# @param opts [Hash] any of the `create` kwargs except `phone_number:`.
|
|
101
122
|
# @return [VoiceML::IncomingPhoneNumber]
|
|
102
123
|
def update(sid, **opts)
|
|
103
124
|
IncomingPhoneNumber.from_hash(
|
|
@@ -148,14 +169,14 @@ module VoiceML
|
|
|
148
169
|
def list_typed(kind, kwargs)
|
|
149
170
|
IncomingPhoneNumberList.from_hash(
|
|
150
171
|
@transport.request(:get, path('IncomingPhoneNumbers', kind),
|
|
151
|
-
params: form_params(
|
|
172
|
+
params: form_params(LIST_FIELDS, kwargs))
|
|
152
173
|
)
|
|
153
174
|
end
|
|
154
175
|
|
|
155
176
|
def create_typed(kind, kwargs)
|
|
156
177
|
IncomingPhoneNumber.from_hash(
|
|
157
178
|
@transport.request(:post, path('IncomingPhoneNumbers', kind),
|
|
158
|
-
form: form_params(
|
|
179
|
+
form: form_params(CREATE_TYPED_FIELDS, kwargs))
|
|
159
180
|
)
|
|
160
181
|
end
|
|
161
182
|
end
|
|
@@ -2,21 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'base'
|
|
4
4
|
require_relative '../models/messages'
|
|
5
|
+
require_relative '../models/media'
|
|
5
6
|
|
|
6
7
|
module VoiceML
|
|
7
|
-
# Operations on `/Messages` — VoiceTel's Twilio-compatible SMS surface,
|
|
8
|
-
# backed by the SDK 2.2 gateway.
|
|
9
|
-
# webhook delivery).
|
|
8
|
+
# Operations on `/Messages` — VoiceTel's Twilio-compatible SMS/MMS surface,
|
|
9
|
+
# backed by the SDK 2.2 gateway.
|
|
10
10
|
#
|
|
11
11
|
# All methods accept idiomatic snake_case keyword arguments — they're
|
|
12
|
-
# translated to the PascalCase wire names internally.
|
|
12
|
+
# translated to the PascalCase wire names internally. Unknown kwargs raise
|
|
13
|
+
# `ArgumentError`.
|
|
13
14
|
class MessagesResource < BaseResource
|
|
14
15
|
CREATE_FIELDS = {
|
|
15
16
|
'To' => :to,
|
|
16
17
|
'Body' => :body,
|
|
18
|
+
'MediaUrl' => :media_url,
|
|
17
19
|
'From' => :from,
|
|
18
20
|
'MessagingServiceSid' => :messaging_service_sid,
|
|
19
|
-
'
|
|
21
|
+
'ValidityPeriod' => :validity_period,
|
|
22
|
+
'StatusCallback' => :status_callback,
|
|
23
|
+
'StatusCallbackMethod' => :status_callback_method
|
|
20
24
|
}.freeze
|
|
21
25
|
|
|
22
26
|
LIST_FIELDS = {
|
|
@@ -35,12 +39,31 @@ module VoiceML
|
|
|
35
39
|
'Status' => :status
|
|
36
40
|
}.freeze
|
|
37
41
|
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
FEEDBACK_FIELDS = {
|
|
43
|
+
'Outcome' => :outcome
|
|
44
|
+
}.freeze
|
|
45
|
+
|
|
46
|
+
LIST_MEDIA_FIELDS = {
|
|
47
|
+
'DateCreated' => :date_created,
|
|
48
|
+
'DateCreated<' => :date_created_lt,
|
|
49
|
+
'DateCreated>' => :date_created_gt,
|
|
50
|
+
'Page' => :page,
|
|
51
|
+
'PageSize' => :page_size,
|
|
52
|
+
'PageToken' => :page_token
|
|
53
|
+
}.freeze
|
|
54
|
+
|
|
55
|
+
# Dispatch an outbound SMS or MMS. `to:` is required, plus at least one of
|
|
56
|
+
# `body:` (SMS text) or `media_url:` (MMS media; pass a string or an array —
|
|
57
|
+
# repeated keys are sent on the wire). `from:` falls back to the tenant's
|
|
58
|
+
# configured default sender when omitted.
|
|
40
59
|
#
|
|
41
60
|
# @return [VoiceML::Message]
|
|
42
61
|
def create(**kwargs)
|
|
43
|
-
|
|
62
|
+
form = form_params(CREATE_FIELDS, kwargs)
|
|
63
|
+
raise ArgumentError, 'to: is required' if form['To'].nil?
|
|
64
|
+
raise ArgumentError, 'either body: or media_url: is required' if form['Body'].nil? && form['MediaUrl'].nil?
|
|
65
|
+
|
|
66
|
+
data = @transport.request(:post, path('Messages'), form: form)
|
|
44
67
|
Message.from_hash(data)
|
|
45
68
|
end
|
|
46
69
|
|
|
@@ -61,21 +84,22 @@ module VoiceML
|
|
|
61
84
|
)
|
|
62
85
|
end
|
|
63
86
|
|
|
64
|
-
# Walk every page of /Messages and yield each Message
|
|
65
|
-
#
|
|
87
|
+
# Walk every page of /Messages and yield each Message, following the
|
|
88
|
+
# envelope's `next_page_uri` verbatim (the server requires its `PageToken`
|
|
89
|
+
# on page 2+). Returns an Enumerator when called without a block.
|
|
66
90
|
#
|
|
67
91
|
# @yield [VoiceML::Message]
|
|
68
92
|
# @return [Enumerator<VoiceML::Message>] when no block given
|
|
69
93
|
def each(**kwargs, &block)
|
|
70
94
|
return enum_for(:each, **kwargs) unless block
|
|
71
95
|
|
|
72
|
-
|
|
96
|
+
page = list(**kwargs)
|
|
73
97
|
loop do
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
break if
|
|
98
|
+
page.messages.each(&block)
|
|
99
|
+
nxt = next_page(page)
|
|
100
|
+
break if nxt.nil?
|
|
77
101
|
|
|
78
|
-
|
|
102
|
+
page = nxt
|
|
79
103
|
end
|
|
80
104
|
end
|
|
81
105
|
|
|
@@ -90,6 +114,19 @@ module VoiceML
|
|
|
90
114
|
Message.from_hash(data)
|
|
91
115
|
end
|
|
92
116
|
|
|
117
|
+
# Submit delivery feedback for a Message
|
|
118
|
+
# (`POST /Messages/{Sid}/Feedback.json`). `outcome:` is `"confirmed"` or
|
|
119
|
+
# `"unconfirmed"`; any other value echoes back as `unconfirmed` and the
|
|
120
|
+
# feedback is not persisted.
|
|
121
|
+
#
|
|
122
|
+
# @return [VoiceML::MessageFeedback]
|
|
123
|
+
def create_feedback(sid, outcome:)
|
|
124
|
+
MessageFeedback.from_hash(
|
|
125
|
+
@transport.request(:post, path('Messages', sid, 'Feedback'),
|
|
126
|
+
form: form_params(FEEDBACK_FIELDS, { outcome: outcome }))
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
|
|
93
130
|
# Remove a Message resource from the account's store.
|
|
94
131
|
#
|
|
95
132
|
# @return [nil]
|
|
@@ -97,5 +134,32 @@ module VoiceML
|
|
|
97
134
|
@transport.request(:delete, path('Messages', sid))
|
|
98
135
|
nil
|
|
99
136
|
end
|
|
137
|
+
|
|
138
|
+
# --- Media (`/Messages/{MessageSid}/Media`) ---
|
|
139
|
+
|
|
140
|
+
# List the media items of a message.
|
|
141
|
+
#
|
|
142
|
+
# @return [VoiceML::MediaList]
|
|
143
|
+
def list_media(message_sid, **kwargs)
|
|
144
|
+
MediaList.from_hash(
|
|
145
|
+
@transport.request(:get, path('Messages', message_sid, 'Media'),
|
|
146
|
+
params: form_params(LIST_MEDIA_FIELDS, kwargs))
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Fetch one media item's metadata.
|
|
151
|
+
#
|
|
152
|
+
# @return [VoiceML::Media]
|
|
153
|
+
def get_media(message_sid, sid)
|
|
154
|
+
Media.from_hash(@transport.request(:get, path('Messages', message_sid, 'Media', sid)))
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Delete a media item.
|
|
158
|
+
#
|
|
159
|
+
# @return [nil]
|
|
160
|
+
def delete_media(message_sid, sid)
|
|
161
|
+
@transport.request(:delete, path('Messages', message_sid, 'Media', sid))
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
100
164
|
end
|
|
101
165
|
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'base'
|
|
3
4
|
require_relative '../models/messaging_v1'
|
|
4
5
|
|
|
5
6
|
module VoiceML
|
|
@@ -20,6 +21,9 @@ module VoiceML
|
|
|
20
21
|
# `create` / `list` / `fetch` / `delete` reuse the shared path; `update`
|
|
21
22
|
# (`POST /v1/Services/{sid}`) is unique to Messaging Service.
|
|
22
23
|
class MessagingV1ServicesResource
|
|
24
|
+
include V1Pages
|
|
25
|
+
include FieldMapping
|
|
26
|
+
|
|
23
27
|
SERVICE_FIELDS = {
|
|
24
28
|
'FriendlyName' => :friendly_name,
|
|
25
29
|
'InboundRequestUrl' => :inbound_request_url,
|
|
@@ -46,7 +50,7 @@ module VoiceML
|
|
|
46
50
|
def create(friendly_name:, **kwargs)
|
|
47
51
|
kwargs[:friendly_name] = friendly_name
|
|
48
52
|
MessagingService.from_hash(
|
|
49
|
-
@transport.request(:post, '/v1/Services', form:
|
|
53
|
+
@transport.request(:post, '/v1/Services', form: form_params(SERVICE_FIELDS, kwargs))
|
|
50
54
|
)
|
|
51
55
|
end
|
|
52
56
|
|
|
@@ -57,31 +61,61 @@ module VoiceML
|
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
def fetch(sid)
|
|
60
|
-
MessagingService.from_hash(@transport.request(:get, "/v1/Services/#{sid}"))
|
|
64
|
+
MessagingService.from_hash(@transport.request(:get, "/v1/Services/#{encode_path_segment(sid)}"))
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
def update(sid, **kwargs)
|
|
64
68
|
MessagingService.from_hash(
|
|
65
|
-
@transport.request(:post, "/v1/Services/#{sid}",
|
|
69
|
+
@transport.request(:post, "/v1/Services/#{encode_path_segment(sid)}",
|
|
70
|
+
form: form_params(SERVICE_FIELDS, kwargs))
|
|
66
71
|
)
|
|
67
72
|
end
|
|
68
73
|
|
|
69
74
|
def delete(sid)
|
|
70
|
-
@transport.request(:delete, "/v1/Services/#{sid}")
|
|
75
|
+
@transport.request(:delete, "/v1/Services/#{encode_path_segment(sid)}")
|
|
71
76
|
nil
|
|
72
77
|
end
|
|
73
78
|
|
|
74
|
-
|
|
79
|
+
# --- Sender-pool phone numbers (`/v1/Services/{ChatServiceSid}/PhoneNumbers`) ---
|
|
80
|
+
|
|
81
|
+
# List the phone numbers in a Messaging Service's sender pool. Note the
|
|
82
|
+
# spec declares only `Page`/`PageSize` here — no `PageToken`.
|
|
83
|
+
#
|
|
84
|
+
# @return [VoiceML::MessagingServicePhoneNumberList]
|
|
85
|
+
def list_phone_numbers(chat_service_sid, page: nil, page_size: nil)
|
|
86
|
+
params = {}
|
|
87
|
+
params['Page'] = page unless page.nil?
|
|
88
|
+
params['PageSize'] = page_size unless page_size.nil?
|
|
89
|
+
MessagingServicePhoneNumberList.new(
|
|
90
|
+
@transport.request(:get, "/v1/Services/#{encode_path_segment(chat_service_sid)}/PhoneNumbers",
|
|
91
|
+
params: params)
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Add a phone number (by its `PN` sid) to a Messaging Service's sender
|
|
96
|
+
# pool. HTTP 409 means the number is already in the pool.
|
|
97
|
+
#
|
|
98
|
+
# @return [VoiceML::MessagingServicePhoneNumber]
|
|
99
|
+
def create_phone_number(chat_service_sid, phone_number_sid:)
|
|
100
|
+
MessagingServicePhoneNumber.from_hash(
|
|
101
|
+
@transport.request(:post, "/v1/Services/#{encode_path_segment(chat_service_sid)}/PhoneNumbers",
|
|
102
|
+
form: { 'PhoneNumberSid' => phone_number_sid })
|
|
103
|
+
)
|
|
104
|
+
end
|
|
75
105
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
106
|
+
# @return [VoiceML::MessagingServicePhoneNumber]
|
|
107
|
+
def fetch_phone_number(chat_service_sid, sid)
|
|
108
|
+
MessagingServicePhoneNumber.from_hash(
|
|
109
|
+
@transport.request(:get, "/v1/Services/#{encode_path_segment(chat_service_sid)}/PhoneNumbers/#{encode_path_segment(sid)}")
|
|
110
|
+
)
|
|
111
|
+
end
|
|
81
112
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
113
|
+
# Remove a phone number from a Messaging Service's sender pool.
|
|
114
|
+
#
|
|
115
|
+
# @return [nil]
|
|
116
|
+
def delete_phone_number(chat_service_sid, sid)
|
|
117
|
+
@transport.request(:delete, "/v1/Services/#{encode_path_segment(chat_service_sid)}/PhoneNumbers/#{encode_path_segment(sid)}")
|
|
118
|
+
nil
|
|
85
119
|
end
|
|
86
120
|
end
|
|
87
121
|
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
|
|
5
|
+
require_relative '../models/monitor_v1'
|
|
6
|
+
|
|
7
|
+
module VoiceML
|
|
8
|
+
# `client.monitor_alerts` — Monitor v1 Alerts (`/v1/Alerts`), on the
|
|
9
|
+
# default host: failure events for calls and messaging delivery.
|
|
10
|
+
#
|
|
11
|
+
# The server serves a single page (`meta.next_page_url` is always `null`);
|
|
12
|
+
# `next_page` follows the envelope verbatim regardless and returns `nil`
|
|
13
|
+
# when there is no continuation.
|
|
14
|
+
class MonitorAlertsResource
|
|
15
|
+
include V1Pages
|
|
16
|
+
include FieldMapping
|
|
17
|
+
|
|
18
|
+
LIST_FIELDS = {
|
|
19
|
+
'LogLevel' => :log_level,
|
|
20
|
+
'StartDate' => :start_date,
|
|
21
|
+
'EndDate' => :end_date,
|
|
22
|
+
'PageSize' => :page_size
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
def initialize(transport)
|
|
26
|
+
@transport = transport
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Return one page of alerts, narrowed by severity (`log_level:` `"error"`
|
|
30
|
+
# or `"warning"`) and generation window (`start_date:` / `end_date:`,
|
|
31
|
+
# `YYYY-MM-DD` or a full ISO-8601 timestamp). `page_size:` limits this
|
|
32
|
+
# collection (default 50, capped at 1000 server-side).
|
|
33
|
+
#
|
|
34
|
+
# @return [VoiceML::MonitorAlertList]
|
|
35
|
+
def list(**kwargs)
|
|
36
|
+
MonitorAlertList.new(
|
|
37
|
+
@transport.request(:get, '/v1/Alerts', params: form_params(LIST_FIELDS, kwargs))
|
|
38
|
+
)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Walk every page of `/v1/Alerts` and yield each alert, following the
|
|
42
|
+
# envelope's `meta.next_page_url` verbatim. Returns an Enumerator when
|
|
43
|
+
# called without a block; stopping early makes no further requests.
|
|
44
|
+
#
|
|
45
|
+
# @yield [VoiceML::MonitorAlert]
|
|
46
|
+
# @return [Enumerator<VoiceML::MonitorAlert>] when no block given
|
|
47
|
+
def each(**kwargs, &block)
|
|
48
|
+
return enum_for(:each, **kwargs) unless block
|
|
49
|
+
|
|
50
|
+
page = list(**kwargs)
|
|
51
|
+
loop do
|
|
52
|
+
page.alerts.each(&block)
|
|
53
|
+
nxt = next_page(page)
|
|
54
|
+
break if nxt.nil?
|
|
55
|
+
|
|
56
|
+
page = nxt
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Fetch one alert by sid, with the stored request/response detail when
|
|
61
|
+
# the underlying event carries any.
|
|
62
|
+
#
|
|
63
|
+
# @return [VoiceML::MonitorAlert]
|
|
64
|
+
def fetch(sid)
|
|
65
|
+
MonitorAlert.from_hash(@transport.request(:get, "/v1/Alerts/#{encode_path_segment(sid)}"))
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'base'
|
|
4
|
+
require_relative '../models/outgoing_caller_ids'
|
|
5
|
+
|
|
6
|
+
module VoiceML
|
|
7
|
+
# Operations on `/OutgoingCallerIds` — verified caller-id numbers.
|
|
8
|
+
class OutgoingCallerIdsResource < BaseResource
|
|
9
|
+
LIST_FIELDS = {
|
|
10
|
+
'PhoneNumber' => :phone_number,
|
|
11
|
+
'FriendlyName' => :friendly_name,
|
|
12
|
+
'Page' => :page,
|
|
13
|
+
'PageSize' => :page_size,
|
|
14
|
+
'PageToken' => :page_token
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
CREATE_FIELDS = {
|
|
18
|
+
'PhoneNumber' => :phone_number,
|
|
19
|
+
'FriendlyName' => :friendly_name
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
UPDATE_FIELDS = {
|
|
23
|
+
'FriendlyName' => :friendly_name
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
# List verified outgoing caller ids.
|
|
27
|
+
#
|
|
28
|
+
# @return [VoiceML::OutgoingCallerIdList]
|
|
29
|
+
def list(**kwargs)
|
|
30
|
+
OutgoingCallerIdList.from_hash(
|
|
31
|
+
@transport.request(:get, path('OutgoingCallerIds'),
|
|
32
|
+
params: form_params(LIST_FIELDS, kwargs))
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Start verification of a new outgoing caller id. The server places a
|
|
37
|
+
# validation call; the returned `VoiceML::ValidationRequest` carries the
|
|
38
|
+
# `validation_code` the user must enter on that call.
|
|
39
|
+
#
|
|
40
|
+
# @param phone_number [String] required. E.164.
|
|
41
|
+
# @param friendly_name [String, nil] display label.
|
|
42
|
+
# @return [VoiceML::ValidationRequest]
|
|
43
|
+
def create(phone_number:, friendly_name: nil)
|
|
44
|
+
ValidationRequest.from_hash(
|
|
45
|
+
@transport.request(:post, path('OutgoingCallerIds'),
|
|
46
|
+
form: form_params(CREATE_FIELDS,
|
|
47
|
+
{ phone_number: phone_number,
|
|
48
|
+
friendly_name: friendly_name }))
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Fetch a verified caller id by its `PN`-prefixed sid.
|
|
53
|
+
#
|
|
54
|
+
# @return [VoiceML::OutgoingCallerId]
|
|
55
|
+
def fetch(sid)
|
|
56
|
+
OutgoingCallerId.from_hash(@transport.request(:get, path('OutgoingCallerIds', sid)))
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Update a caller id's friendly name.
|
|
60
|
+
#
|
|
61
|
+
# @return [VoiceML::OutgoingCallerId]
|
|
62
|
+
def update(sid, friendly_name: nil)
|
|
63
|
+
OutgoingCallerId.from_hash(
|
|
64
|
+
@transport.request(:post, path('OutgoingCallerIds', sid),
|
|
65
|
+
form: form_params(UPDATE_FIELDS, { friendly_name: friendly_name }))
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Remove a caller id.
|
|
70
|
+
#
|
|
71
|
+
# @return [nil]
|
|
72
|
+
def delete(sid)
|
|
73
|
+
@transport.request(:delete, path('OutgoingCallerIds', sid))
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|