voiceml 0.7.1.1

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.
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative '../models/incoming_phone_numbers'
5
+
6
+ module VoiceML
7
+ # Operations on `/IncomingPhoneNumbers` — tenant-scoped DID lookup and routing.
8
+ #
9
+ # Twilio-compatible: `sid` is the canonical `PN`-prefixed identifier, `phone_number` is the
10
+ # E.164 form. The standard lookup-by-number pattern (`list(phone_number: '+1...')`
11
+ # returning a 0-or-1-row envelope, then `get(sid)`) is supported.
12
+ class IncomingPhoneNumbersResource < BaseResource
13
+ LIST_FIELDS = {
14
+ 'PhoneNumber' => :phone_number,
15
+ 'Page' => :page,
16
+ 'PageSize' => :page_size,
17
+ 'PageToken' => :page_token
18
+ }.freeze
19
+
20
+ CREATE_FIELDS = {
21
+ 'PhoneNumber' => :phone_number,
22
+ 'VoiceUrl' => :voice_url,
23
+ 'VoiceMethod' => :voice_method,
24
+ 'VoiceFallbackUrl' => :voice_fallback_url,
25
+ 'VoiceFallbackMethod' => :voice_fallback_method,
26
+ 'FriendlyName' => :friendly_name
27
+ }.freeze
28
+
29
+ UPDATE_FIELDS = {
30
+ 'VoiceUrl' => :voice_url,
31
+ 'VoiceMethod' => :voice_method,
32
+ 'VoiceFallbackUrl' => :voice_fallback_url,
33
+ 'VoiceFallbackMethod' => :voice_fallback_method,
34
+ 'FriendlyName' => :friendly_name
35
+ }.freeze
36
+
37
+ LIST_TYPED_FIELDS = {
38
+ 'PhoneNumber' => :phone_number,
39
+ 'FriendlyName' => :friendly_name,
40
+ 'Beta' => :beta,
41
+ 'Origin' => :origin,
42
+ 'Page' => :page,
43
+ 'PageSize' => :page_size,
44
+ 'PageToken' => :page_token
45
+ }.freeze
46
+
47
+ # List DIDs assigned to the authenticated tenant.
48
+ #
49
+ # @param phone_number [String, nil] exact-match E.164 filter. Returns a 0-or-1-row
50
+ # envelope when set — the standard twilio-ruby lookup pattern.
51
+ # @param page [Integer, nil] 0-indexed page number.
52
+ # @param page_size [Integer, nil] page size (server-bounded).
53
+ # @return [VoiceML::IncomingPhoneNumberList]
54
+ def list(phone_number: nil, page: nil, page_size: nil)
55
+ kwargs = { phone_number: phone_number, page: page, page_size: page_size }
56
+ IncomingPhoneNumberList.from_hash(
57
+ @transport.request(:get, path('IncomingPhoneNumbers'),
58
+ params: form_params(LIST_FIELDS, kwargs))
59
+ )
60
+ end
61
+
62
+ # Assign a DID to the authenticated tenant. Idempotent re-POSTing the same
63
+ # `phone_number:` rebinds the voice routing (matches Twilio update semantics).
64
+ #
65
+ # @param phone_number [String] required. E.164 — leading `+`, 7-15 digits.
66
+ # @param voice_url [String, nil] inbound-voice handler URL.
67
+ # @param voice_method [String, nil] HTTP method for `voice_url` (`GET`/`POST`).
68
+ # @param voice_fallback_url [String, nil] fallback handler URL.
69
+ # @param voice_fallback_method [String, nil] HTTP method for `voice_fallback_url`.
70
+ # @param friendly_name [String, nil] display label (server may ignore in v0.5.x).
71
+ # @return [VoiceML::IncomingPhoneNumber]
72
+ def create(phone_number:, voice_url: nil, voice_method: nil,
73
+ voice_fallback_url: nil, voice_fallback_method: nil,
74
+ friendly_name: nil)
75
+ kwargs = {
76
+ phone_number: phone_number,
77
+ voice_url: voice_url, voice_method: voice_method,
78
+ voice_fallback_url: voice_fallback_url,
79
+ voice_fallback_method: voice_fallback_method,
80
+ friendly_name: friendly_name
81
+ }
82
+ IncomingPhoneNumber.from_hash(
83
+ @transport.request(:post, path('IncomingPhoneNumbers'),
84
+ form: form_params(CREATE_FIELDS, kwargs))
85
+ )
86
+ end
87
+
88
+ # Fetch a single DID by its `PN`-prefixed sid.
89
+ # @return [VoiceML::IncomingPhoneNumber]
90
+ def get(sid)
91
+ IncomingPhoneNumber.from_hash(
92
+ @transport.request(:get, path('IncomingPhoneNumbers', sid))
93
+ )
94
+ end
95
+
96
+ # Update voice routing on an assigned DID. Only set fields are touched.
97
+ #
98
+ # @param sid [String] the `PN`-prefixed identifier.
99
+ # @param opts [Hash] any of `voice_url:`, `voice_method:`, `voice_fallback_url:`,
100
+ # `voice_fallback_method:`, `friendly_name:`.
101
+ # @return [VoiceML::IncomingPhoneNumber]
102
+ def update(sid, **opts)
103
+ IncomingPhoneNumber.from_hash(
104
+ @transport.request(:post, path('IncomingPhoneNumbers', sid),
105
+ form: form_params(UPDATE_FIELDS, opts))
106
+ )
107
+ end
108
+
109
+ # Release a DID from the authenticated tenant. Idempotent — 204 even if already gone.
110
+ # @return [nil]
111
+ def delete(sid)
112
+ @transport.request(:delete, path('IncomingPhoneNumbers', sid))
113
+ nil
114
+ end
115
+
116
+ # @return [VoiceML::IncomingPhoneNumberList]
117
+ def list_local(**kwargs)
118
+ list_typed('Local', kwargs)
119
+ end
120
+
121
+ # @return [VoiceML::IncomingPhoneNumber]
122
+ def create_local(**kwargs)
123
+ create_typed('Local', kwargs)
124
+ end
125
+
126
+ # @return [VoiceML::IncomingPhoneNumberList]
127
+ def list_mobile(**kwargs)
128
+ list_typed('Mobile', kwargs)
129
+ end
130
+
131
+ # @return [VoiceML::IncomingPhoneNumber]
132
+ def create_mobile(**kwargs)
133
+ create_typed('Mobile', kwargs)
134
+ end
135
+
136
+ # @return [VoiceML::IncomingPhoneNumberList]
137
+ def list_toll_free(**kwargs)
138
+ list_typed('TollFree', kwargs)
139
+ end
140
+
141
+ # @return [VoiceML::IncomingPhoneNumber]
142
+ def create_toll_free(**kwargs)
143
+ create_typed('TollFree', kwargs)
144
+ end
145
+
146
+ private
147
+
148
+ def list_typed(kind, kwargs)
149
+ IncomingPhoneNumberList.from_hash(
150
+ @transport.request(:get, path('IncomingPhoneNumbers', kind),
151
+ params: form_params(LIST_TYPED_FIELDS, kwargs))
152
+ )
153
+ end
154
+
155
+ def create_typed(kind, kwargs)
156
+ IncomingPhoneNumber.from_hash(
157
+ @transport.request(:post, path('IncomingPhoneNumbers', kind),
158
+ form: form_params(CREATE_FIELDS, kwargs))
159
+ )
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative '../models/messages'
5
+
6
+ module VoiceML
7
+ # Operations on `/Messages` — VoiceTel's Twilio-compatible SMS surface,
8
+ # backed by the SDK 2.2 gateway. Outbound-only today (no MMS, no inbound
9
+ # webhook delivery).
10
+ #
11
+ # All methods accept idiomatic snake_case keyword arguments — they're
12
+ # translated to the PascalCase wire names internally.
13
+ class MessagesResource < BaseResource
14
+ CREATE_FIELDS = {
15
+ 'To' => :to,
16
+ 'Body' => :body,
17
+ 'From' => :from,
18
+ 'MessagingServiceSid' => :messaging_service_sid,
19
+ 'StatusCallback' => :status_callback
20
+ }.freeze
21
+
22
+ LIST_FIELDS = {
23
+ 'To' => :to,
24
+ 'From' => :from,
25
+ 'DateSent' => :date_sent,
26
+ 'DateSent<' => :date_sent_lt,
27
+ 'DateSent>' => :date_sent_gt,
28
+ 'Page' => :page,
29
+ 'PageSize' => :page_size,
30
+ 'PageToken' => :page_token
31
+ }.freeze
32
+
33
+ UPDATE_FIELDS = {
34
+ 'Body' => :body,
35
+ 'Status' => :status
36
+ }.freeze
37
+
38
+ # Dispatch an outbound SMS. `to:` and `body:` are required; `from:` falls
39
+ # back to the tenant's configured default sender when omitted.
40
+ #
41
+ # @return [VoiceML::Message]
42
+ def create(**kwargs)
43
+ data = @transport.request(:post, path('Messages'), form: form_params(CREATE_FIELDS, kwargs))
44
+ Message.from_hash(data)
45
+ end
46
+
47
+ # Retrieve a previously-sent Message by sid.
48
+ #
49
+ # @return [VoiceML::Message]
50
+ def fetch(sid)
51
+ Message.from_hash(@transport.request(:get, path('Messages', sid)))
52
+ end
53
+
54
+ # Return a single page of Messages, narrowed by the Twilio-documented filter
55
+ # set (To, From, DateSent eq/gt/lt) plus pagination.
56
+ #
57
+ # @return [VoiceML::MessageList]
58
+ def list(**kwargs)
59
+ MessageList.from_hash(
60
+ @transport.request(:get, path('Messages'), params: form_params(LIST_FIELDS, kwargs))
61
+ )
62
+ end
63
+
64
+ # Walk every page of /Messages and yield each Message. Returns an Enumerator
65
+ # when called without a block.
66
+ #
67
+ # @yield [VoiceML::Message]
68
+ # @return [Enumerator<VoiceML::Message>] when no block given
69
+ def each(**kwargs, &block)
70
+ return enum_for(:each, **kwargs) unless block
71
+
72
+ page_num = kwargs.delete(:page) || 0
73
+ loop do
74
+ chunk = list(**kwargs, page: page_num)
75
+ chunk.messages.each(&block)
76
+ break if chunk.next_page_uri.nil? || chunk.next_page_uri.empty? || chunk.messages.empty?
77
+
78
+ page_num += 1
79
+ end
80
+ end
81
+
82
+ # Mutate an existing Message — redact `body:` to empty string, or attempt
83
+ # `status: "canceled"`. Cancellation returns 21610 today because the gateway
84
+ # is fire-and-forget.
85
+ #
86
+ # @return [VoiceML::Message]
87
+ def update(sid, **kwargs)
88
+ data = @transport.request(:post, path('Messages', sid),
89
+ form: form_params(UPDATE_FIELDS, kwargs))
90
+ Message.from_hash(data)
91
+ end
92
+
93
+ # Remove a Message resource from the account's store.
94
+ #
95
+ # @return [nil]
96
+ def delete(sid)
97
+ @transport.request(:delete, path('Messages', sid))
98
+ nil
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative '../models/diagnostics'
5
+
6
+ module VoiceML
7
+ # Account-scoped `/Notifications` compat stubs (always empty list; fetch returns 404).
8
+ class NotificationsResource < BaseResource
9
+ LIST_FIELDS = {
10
+ 'Page' => :page,
11
+ 'PageSize' => :page_size,
12
+ 'PageToken' => :page_token,
13
+ 'Log' => :log,
14
+ 'MessageDate' => :message_date,
15
+ 'MessageDate<' => :message_date_lt,
16
+ 'MessageDate>' => :message_date_gt
17
+ }.freeze
18
+
19
+ # @return [VoiceML::NotificationsList]
20
+ def list(**kwargs)
21
+ NotificationsList.from_hash(
22
+ @transport.request(:get, path('Notifications'),
23
+ params: form_params(LIST_FIELDS, kwargs))
24
+ )
25
+ end
26
+
27
+ # @return [Hash]
28
+ def get(notification_sid)
29
+ @transport.request(:get, path('Notifications', notification_sid))
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative '../models/queues'
5
+
6
+ module VoiceML
7
+ # Operations on `/Queues` and their members.
8
+ class QueuesResource < BaseResource
9
+ QUEUE_FIELDS = {
10
+ 'FriendlyName' => :friendly_name,
11
+ 'MaxSize' => :max_size
12
+ }.freeze
13
+
14
+ DEQUEUE_FIELDS = {
15
+ 'Url' => :url,
16
+ 'Method' => :method
17
+ }.freeze
18
+
19
+ LIST_PAGE_FIELDS = {
20
+ 'Page' => :page,
21
+ 'PageSize' => :page_size,
22
+ 'PageToken' => :page_token
23
+ }.freeze
24
+
25
+ LIST_MEMBERS_FIELDS = {
26
+ 'Page' => :page,
27
+ 'PageSize' => :page_size,
28
+ 'PageToken' => :page_token
29
+ }.freeze
30
+
31
+ # @return [VoiceML::Queue]
32
+ def create(**kwargs)
33
+ Queue.from_hash(
34
+ @transport.request(:post, path('Queues'), form: form_params(QUEUE_FIELDS, kwargs))
35
+ )
36
+ end
37
+
38
+ # @return [VoiceML::QueueList]
39
+ def list(**kwargs)
40
+ QueueList.from_hash(
41
+ @transport.request(:get, path('Queues'), params: form_params(LIST_PAGE_FIELDS, kwargs))
42
+ )
43
+ end
44
+
45
+ # @yield [VoiceML::Queue]
46
+ # @return [Enumerator<VoiceML::Queue>] when no block given
47
+ def each(**kwargs, &block)
48
+ return enum_for(:each, **kwargs) unless block
49
+
50
+ page_num = kwargs.delete(:page) || 0
51
+ loop do
52
+ chunk = list(**kwargs, page: page_num)
53
+ chunk.queues.each(&block)
54
+ break if chunk.next_page_uri.nil? || chunk.next_page_uri.empty? || chunk.queues.empty?
55
+ page_num += 1
56
+ end
57
+ end
58
+
59
+ # @return [VoiceML::Queue]
60
+ def get(queue_sid)
61
+ Queue.from_hash(@transport.request(:get, path('Queues', queue_sid)))
62
+ end
63
+
64
+ # @return [VoiceML::Queue]
65
+ def update(queue_sid, **kwargs)
66
+ Queue.from_hash(
67
+ @transport.request(:post, path('Queues', queue_sid),
68
+ form: form_params(QUEUE_FIELDS, kwargs))
69
+ )
70
+ end
71
+
72
+ # @return [nil]
73
+ def delete(queue_sid)
74
+ @transport.request(:delete, path('Queues', queue_sid))
75
+ nil
76
+ end
77
+
78
+ # --- Members ---
79
+
80
+ # @return [VoiceML::QueueMemberList]
81
+ def list_members(queue_sid, **kwargs)
82
+ QueueMemberList.from_hash(
83
+ @transport.request(:get, path('Queues', queue_sid, 'Members'),
84
+ params: form_params(LIST_MEMBERS_FIELDS, kwargs))
85
+ )
86
+ end
87
+
88
+ # @return [VoiceML::QueueMember]
89
+ def peek_front(queue_sid)
90
+ QueueMember.from_hash(
91
+ @transport.request(:get, path('Queues', queue_sid, 'Members', 'Front'))
92
+ )
93
+ end
94
+
95
+ # Dequeue the front-of-queue member to a TwiML URL.
96
+ # @return [VoiceML::QueueMember]
97
+ def dequeue_front(queue_sid, **kwargs)
98
+ QueueMember.from_hash(
99
+ @transport.request(:post, path('Queues', queue_sid, 'Members', 'Front'),
100
+ form: form_params(DEQUEUE_FIELDS, kwargs))
101
+ )
102
+ end
103
+
104
+ # @return [VoiceML::QueueMember]
105
+ def get_member(queue_sid, call_sid)
106
+ QueueMember.from_hash(
107
+ @transport.request(:get, path('Queues', queue_sid, 'Members', call_sid))
108
+ )
109
+ end
110
+
111
+ # Dequeue a specific member by CallSid.
112
+ # @return [VoiceML::QueueMember]
113
+ def dequeue_member(queue_sid, call_sid, **kwargs)
114
+ QueueMember.from_hash(
115
+ @transport.request(:post, path('Queues', queue_sid, 'Members', call_sid),
116
+ form: form_params(DEQUEUE_FIELDS, kwargs))
117
+ )
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+ require_relative '../models/recordings'
5
+
6
+ module VoiceML
7
+ # Account-scoped `/Recordings` operations.
8
+ #
9
+ # Per-call recording start/stop/list lives on {VoiceML::CallsResource} — this resource
10
+ # handles the account-wide list, single-recording fetch (both metadata and audio),
11
+ # and delete.
12
+ class RecordingsResource < BaseResource
13
+ LIST_FIELDS = {
14
+ 'DateCreated' => :date_created,
15
+ 'DateCreated<' => :date_created_lt,
16
+ 'DateCreated>' => :date_created_gt,
17
+ 'CallSid' => :call_sid,
18
+ 'ConferenceSid' => :conference_sid,
19
+ 'IncludeSoftDeleted' => :include_soft_deleted,
20
+ 'Page' => :page,
21
+ 'PageSize' => :page_size,
22
+ 'PageToken' => :page_token
23
+ }.freeze
24
+
25
+ GET_FIELDS = {
26
+ 'IncludeSoftDeleted' => :include_soft_deleted
27
+ }.freeze
28
+
29
+ # @return [VoiceML::RecordingList]
30
+ def list(**kwargs)
31
+ RecordingList.from_hash(
32
+ @transport.request(:get, path('Recordings'), params: form_params(LIST_FIELDS, kwargs))
33
+ )
34
+ end
35
+
36
+ # @yield [VoiceML::Recording]
37
+ # @return [Enumerator<VoiceML::Recording>] when no block given
38
+ def each(**kwargs, &block)
39
+ return enum_for(:each, **kwargs) unless block
40
+
41
+ page_num = kwargs.delete(:page) || 0
42
+ loop do
43
+ chunk = list(**kwargs, page: page_num)
44
+ chunk.recordings.each(&block)
45
+ break if chunk.next_page_uri.nil? || chunk.next_page_uri.empty? || chunk.recordings.empty?
46
+ page_num += 1
47
+ end
48
+ end
49
+
50
+ # Fetch the metadata JSON for a recording.
51
+ # @return [VoiceML::Recording]
52
+ def get(recording_sid, **kwargs)
53
+ params = form_params(GET_FIELDS, kwargs)
54
+ Recording.from_hash(
55
+ @transport.request(:get, path('Recordings', recording_sid), params: params.empty? ? nil : params)
56
+ )
57
+ end
58
+
59
+ # Fetch the WAV audio for a recording.
60
+ #
61
+ # Three server delivery shapes are flattened into one result by following any 302
62
+ # redirect to S3:
63
+ # - `200 OK` — local file present.
64
+ # - `302 Found` — archived to S3; the SDK follows the presigned URL.
65
+ # - `410 Gone` — local file gone AND no S3 key. Raises `VoiceML::GoneError`.
66
+ #
67
+ # @return [VoiceML::RecordingAudio]
68
+ def get_audio(recording_sid)
69
+ status, content, headers = @transport.fetch_bytes(
70
+ "#{path('Recordings', recording_sid, suffix: '')}.wav"
71
+ )
72
+ content_type = headers['content-type']
73
+ content_type = content_type.first if content_type.is_a?(Array)
74
+ x_amz_id = headers['x-amz-id-2']
75
+ RecordingAudio.new(
76
+ sid: recording_sid,
77
+ content: content,
78
+ content_type: content_type || 'application/octet-stream',
79
+ via_redirect: status == 200 && !x_amz_id.nil?
80
+ )
81
+ end
82
+
83
+ # @return [nil]
84
+ def delete(recording_sid)
85
+ @transport.request(:delete, path('Recordings', recording_sid))
86
+ nil
87
+ end
88
+ end
89
+ end