sentdm 0.34.0 → 0.35.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/CHANGELOG.md +8 -0
- data/README.md +11 -9
- data/lib/sentdm/client.rb +25 -0
- data/lib/sentdm/internal/calls_page.rb +115 -0
- data/lib/sentdm/models/api_response_of_call.rb +43 -0
- data/lib/sentdm/models/api_response_of_call_recordings.rb +43 -0
- data/lib/sentdm/models/api_response_of_calls_list.rb +42 -0
- data/lib/sentdm/models/call.rb +133 -0
- data/lib/sentdm/models/call_event.rb +72 -0
- data/lib/sentdm/models/call_event_payload.rb +102 -0
- data/lib/sentdm/models/call_hangup_params.rb +32 -0
- data/lib/sentdm/models/call_list_params.rb +83 -0
- data/lib/sentdm/models/call_list_recordings_params.rb +26 -0
- data/lib/sentdm/models/call_party.rb +31 -0
- data/lib/sentdm/models/call_record_params.rb +55 -0
- data/lib/sentdm/models/call_recording.rb +38 -0
- data/lib/sentdm/models/call_recordings.rb +24 -0
- data/lib/sentdm/models/call_retrieve_params.rb +26 -0
- data/lib/sentdm/models/call_timeline_entry.rb +26 -0
- data/lib/sentdm/models/calls/api_response_of_list_of_call_participant.rb +45 -0
- data/lib/sentdm/models/calls/call_participant.rb +60 -0
- data/lib/sentdm/models/calls/call_participant_target.rb +30 -0
- data/lib/sentdm/models/calls/participant_add_params.rb +66 -0
- data/lib/sentdm/models/calls/participant_list_params.rb +28 -0
- data/lib/sentdm/models/calls/participant_remove_all_params.rb +28 -0
- data/lib/sentdm/models/calls/participant_remove_params.rb +34 -0
- data/lib/sentdm/models/calls/participant_update_params.rb +64 -0
- data/lib/sentdm/models/calls_list.rb +26 -0
- data/lib/sentdm/models/channels/api_response_of_list_of_voice_number.rb +45 -0
- data/lib/sentdm/models/channels/api_response_of_voice_callback_test.rb +45 -0
- data/lib/sentdm/models/channels/api_response_of_voice_number.rb +45 -0
- data/lib/sentdm/models/channels/api_response_of_voice_number_created.rb +45 -0
- data/lib/sentdm/models/channels/api_response_of_voice_secret.rb +45 -0
- data/lib/sentdm/models/channels/api_response_of_voice_token.rb +45 -0
- data/lib/sentdm/models/channels/voice_callback_test.rb +65 -0
- data/lib/sentdm/models/channels/voice_callback_test_error_info.rb +41 -0
- data/lib/sentdm/models/channels/voice_callback_test_request_info.rb +40 -0
- data/lib/sentdm/models/channels/voice_callback_test_response_info.rb +28 -0
- data/lib/sentdm/models/channels/voice_create_params.rb +86 -0
- data/lib/sentdm/models/channels/voice_create_token_params.rb +69 -0
- data/lib/sentdm/models/channels/voice_list_params.rb +22 -0
- data/lib/sentdm/models/channels/voice_number.rb +68 -0
- data/lib/sentdm/models/channels/voice_number_created.rb +26 -0
- data/lib/sentdm/models/channels/voice_retrieve_params.rb +28 -0
- data/lib/sentdm/models/channels/voice_rotate_secret_params.rb +34 -0
- data/lib/sentdm/models/channels/voice_secret.rb +25 -0
- data/lib/sentdm/models/channels/voice_test_params.rb +34 -0
- data/lib/sentdm/models/channels/voice_token.rb +44 -0
- data/lib/sentdm/models/channels/voice_update_params.rb +95 -0
- data/lib/sentdm/models/webhook_list_events_response.rb +4 -173
- data/lib/sentdm/models.rb +36 -0
- data/lib/sentdm/resources/calls/participants.rb +231 -0
- data/lib/sentdm/resources/calls.rb +235 -0
- data/lib/sentdm/resources/channels/voice.rb +381 -0
- data/lib/sentdm/resources/channels.rb +45 -0
- data/lib/sentdm/version.rb +1 -1
- data/lib/sentdm.rb +50 -0
- data/rbi/sentdm/client.rbi +23 -0
- data/rbi/sentdm/internal/calls_page.rbi +71 -0
- data/rbi/sentdm/models/api_response_of_call.rbi +74 -0
- data/rbi/sentdm/models/api_response_of_call_recordings.rbi +74 -0
- data/rbi/sentdm/models/api_response_of_calls_list.rbi +74 -0
- data/rbi/sentdm/models/call.rbi +177 -0
- data/rbi/sentdm/models/call_event.rbi +114 -0
- data/rbi/sentdm/models/call_event_payload.rbi +135 -0
- data/rbi/sentdm/models/call_hangup_params.rbi +59 -0
- data/rbi/sentdm/models/call_list_params.rbi +112 -0
- data/rbi/sentdm/models/call_list_recordings_params.rbi +46 -0
- data/rbi/sentdm/models/call_party.rbi +41 -0
- data/rbi/sentdm/models/call_record_params.rbi +83 -0
- data/rbi/sentdm/models/call_recording.rbi +59 -0
- data/rbi/sentdm/models/call_recordings.rbi +39 -0
- data/rbi/sentdm/models/call_retrieve_params.rbi +46 -0
- data/rbi/sentdm/models/call_timeline_entry.rbi +40 -0
- data/rbi/sentdm/models/calls/api_response_of_list_of_call_participant.rbi +76 -0
- data/rbi/sentdm/models/calls/call_participant.rbi +92 -0
- data/rbi/sentdm/models/calls/call_participant_target.rbi +47 -0
- data/rbi/sentdm/models/calls/participant_add_params.rbi +98 -0
- data/rbi/sentdm/models/calls/participant_list_params.rbi +51 -0
- data/rbi/sentdm/models/calls/participant_remove_all_params.rbi +51 -0
- data/rbi/sentdm/models/calls/participant_remove_params.rbi +61 -0
- data/rbi/sentdm/models/calls/participant_update_params.rbi +94 -0
- data/rbi/sentdm/models/calls_list.rbi +47 -0
- data/rbi/sentdm/models/channels/api_response_of_list_of_voice_number.rbi +76 -0
- data/rbi/sentdm/models/channels/api_response_of_voice_callback_test.rbi +83 -0
- data/rbi/sentdm/models/channels/api_response_of_voice_number.rbi +81 -0
- data/rbi/sentdm/models/channels/api_response_of_voice_number_created.rbi +83 -0
- data/rbi/sentdm/models/channels/api_response_of_voice_secret.rbi +81 -0
- data/rbi/sentdm/models/channels/api_response_of_voice_token.rbi +81 -0
- data/rbi/sentdm/models/channels/voice_callback_test.rbi +126 -0
- data/rbi/sentdm/models/channels/voice_callback_test_error_info.rbi +65 -0
- data/rbi/sentdm/models/channels/voice_callback_test_request_info.rbi +66 -0
- data/rbi/sentdm/models/channels/voice_callback_test_response_info.rbi +48 -0
- data/rbi/sentdm/models/channels/voice_create_params.rbi +127 -0
- data/rbi/sentdm/models/channels/voice_create_token_params.rbi +104 -0
- data/rbi/sentdm/models/channels/voice_list_params.rbi +40 -0
- data/rbi/sentdm/models/channels/voice_number.rbi +105 -0
- data/rbi/sentdm/models/channels/voice_number_created.rbi +42 -0
- data/rbi/sentdm/models/channels/voice_retrieve_params.rbi +51 -0
- data/rbi/sentdm/models/channels/voice_rotate_secret_params.rbi +64 -0
- data/rbi/sentdm/models/channels/voice_secret.rbi +35 -0
- data/rbi/sentdm/models/channels/voice_test_params.rbi +61 -0
- data/rbi/sentdm/models/channels/voice_token.rbi +76 -0
- data/rbi/sentdm/models/channels/voice_update_params.rbi +158 -0
- data/rbi/sentdm/models/webhook_list_events_response.rbi +3 -269
- data/rbi/sentdm/models.rbi +36 -0
- data/rbi/sentdm/resources/calls/participants.rbi +199 -0
- data/rbi/sentdm/resources/calls.rbi +206 -0
- data/rbi/sentdm/resources/channels/voice.rbi +371 -0
- data/rbi/sentdm/resources/channels.rbi +42 -0
- data/sig/sentdm/client.rbs +4 -0
- data/sig/sentdm/internal/calls_page.rbs +41 -0
- data/sig/sentdm/models/api_response_of_call.rbs +39 -0
- data/sig/sentdm/models/api_response_of_call_recordings.rbs +39 -0
- data/sig/sentdm/models/api_response_of_calls_list.rbs +39 -0
- data/sig/sentdm/models/call.rbs +101 -0
- data/sig/sentdm/models/call_event.rbs +44 -0
- data/sig/sentdm/models/call_event_payload.rbs +68 -0
- data/sig/sentdm/models/call_hangup_params.rbs +38 -0
- data/sig/sentdm/models/call_list_params.rbs +67 -0
- data/sig/sentdm/models/call_list_recordings_params.rbs +30 -0
- data/sig/sentdm/models/call_party.rbs +17 -0
- data/sig/sentdm/models/call_record_params.rbs +54 -0
- data/sig/sentdm/models/call_recording.rbs +32 -0
- data/sig/sentdm/models/call_recordings.rbs +17 -0
- data/sig/sentdm/models/call_retrieve_params.rbs +30 -0
- data/sig/sentdm/models/call_timeline_entry.rbs +19 -0
- data/sig/sentdm/models/calls/api_response_of_list_of_call_participant.rbs +41 -0
- data/sig/sentdm/models/calls/call_participant.rbs +52 -0
- data/sig/sentdm/models/calls/call_participant_target.rbs +23 -0
- data/sig/sentdm/models/calls/participant_add_params.rbs +63 -0
- data/sig/sentdm/models/calls/participant_list_params.rbs +32 -0
- data/sig/sentdm/models/calls/participant_remove_all_params.rbs +34 -0
- data/sig/sentdm/models/calls/participant_remove_params.rbs +40 -0
- data/sig/sentdm/models/calls/participant_update_params.rbs +61 -0
- data/sig/sentdm/models/calls_list.rbs +26 -0
- data/sig/sentdm/models/channels/api_response_of_list_of_voice_number.rbs +41 -0
- data/sig/sentdm/models/channels/api_response_of_voice_callback_test.rbs +41 -0
- data/sig/sentdm/models/channels/api_response_of_voice_number.rbs +41 -0
- data/sig/sentdm/models/channels/api_response_of_voice_number_created.rbs +41 -0
- data/sig/sentdm/models/channels/api_response_of_voice_secret.rbs +41 -0
- data/sig/sentdm/models/channels/api_response_of_voice_token.rbs +41 -0
- data/sig/sentdm/models/channels/voice_callback_test.rbs +51 -0
- data/sig/sentdm/models/channels/voice_callback_test_error_info.rbs +28 -0
- data/sig/sentdm/models/channels/voice_callback_test_request_info.rbs +34 -0
- data/sig/sentdm/models/channels/voice_callback_test_response_info.rbs +20 -0
- data/sig/sentdm/models/channels/voice_create_params.rbs +64 -0
- data/sig/sentdm/models/channels/voice_create_token_params.rbs +61 -0
- data/sig/sentdm/models/channels/voice_list_params.rbs +27 -0
- data/sig/sentdm/models/channels/voice_number.rbs +57 -0
- data/sig/sentdm/models/channels/voice_number_created.rbs +17 -0
- data/sig/sentdm/models/channels/voice_retrieve_params.rbs +32 -0
- data/sig/sentdm/models/channels/voice_rotate_secret_params.rbs +40 -0
- data/sig/sentdm/models/channels/voice_secret.rbs +17 -0
- data/sig/sentdm/models/channels/voice_test_params.rbs +40 -0
- data/sig/sentdm/models/channels/voice_token.rbs +40 -0
- data/sig/sentdm/models/channels/voice_update_params.rbs +78 -0
- data/sig/sentdm/models/webhook_list_events_response.rbs +1 -107
- data/sig/sentdm/models.rbs +36 -0
- data/sig/sentdm/resources/calls/participants.rbs +50 -0
- data/sig/sentdm/resources/calls.rbs +50 -0
- data/sig/sentdm/resources/channels/voice.rbs +68 -0
- data/sig/sentdm/resources/channels.rbs +9 -0
- metadata +152 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cb2a3192ac350b8b789c39c41c5c98b4dcd1df0b714d0d3f0540e88205a0b57d
|
|
4
|
+
data.tar.gz: 5d97dab3605f998f583ea137599413f498de456598e57d257591df8afb173e14
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 65df0bbbe671b490469b171d4b241b1d4fc5d088b271390c3a727227f046fddb138c61be3bee853f2a8b26a8e910aae0a41c0f0b46ef01ffedee44e5fc1002b6
|
|
7
|
+
data.tar.gz: 0fc42b03c73246deaf5cb6711e0431a4469b6eb172c6d1a3cf2a8d7f63621aa4cda7708d7054d61afa042d571d07377b9e1b94437cc2e65aed7099a16eadd77c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.35.0](https://github.com/sentdm/sent-dm-ruby/compare/v0.34.0...v0.35.0) (2026-10-01)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **api:** add voice and calls endpoints to the SDKs ([53f68a7](https://github.com/sentdm/sent-dm-ruby/commit/53f68a7293095980d2674fd20bbcc6045883554d))
|
|
9
|
+
* **api:** sync generated SDKs from the committed spec ([b6fbc5a](https://github.com/sentdm/sent-dm-ruby/commit/b6fbc5a4e562a8f0354c7e2a9c9a357d1207cb6b))
|
|
10
|
+
|
|
3
11
|
## [0.34.0](https://github.com/sentdm/sent-dm-ruby/compare/v0.33.0...v0.34.0) (2026-09-30)
|
|
4
12
|
|
|
5
13
|
|
data/README.md
CHANGED
|
@@ -17,7 +17,7 @@ To use this gem, install via Bundler by adding the following to your application
|
|
|
17
17
|
<!-- x-release-please-start-version -->
|
|
18
18
|
|
|
19
19
|
```ruby
|
|
20
|
-
gem "sentdm", "~> 0.
|
|
20
|
+
gem "sentdm", "~> 0.35.0"
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
<!-- x-release-please-end -->
|
|
@@ -289,23 +289,25 @@ sent.messages.send_(**params)
|
|
|
289
289
|
Since this library does not depend on `sorbet-runtime`, it cannot provide [`T::Enum`](https://sorbet.org/docs/tenum) instances. Instead, we provide "tagged symbols" instead, which is always a primitive at runtime:
|
|
290
290
|
|
|
291
291
|
```ruby
|
|
292
|
-
# :
|
|
293
|
-
puts(Sentdm::
|
|
292
|
+
# :ACTIVE
|
|
293
|
+
puts(Sentdm::Channels::VoiceUpdateParams::Status::ACTIVE)
|
|
294
294
|
|
|
295
|
-
# Revealed type: `T.all(Sentdm::
|
|
296
|
-
T.reveal_type(Sentdm::
|
|
295
|
+
# Revealed type: `T.all(Sentdm::Channels::VoiceUpdateParams::Status, Symbol)`
|
|
296
|
+
T.reveal_type(Sentdm::Channels::VoiceUpdateParams::Status::ACTIVE)
|
|
297
297
|
```
|
|
298
298
|
|
|
299
299
|
Enum parameters have a "relaxed" type, so you can either pass in enum constants or their literal value:
|
|
300
300
|
|
|
301
301
|
```ruby
|
|
302
|
-
|
|
303
|
-
|
|
302
|
+
# Using the enum constants preserves the tagged type information:
|
|
303
|
+
sent.channels.voice.update(
|
|
304
|
+
status: Sentdm::Channels::VoiceUpdateParams::Status::ACTIVE,
|
|
304
305
|
# …
|
|
305
306
|
)
|
|
306
307
|
|
|
307
|
-
|
|
308
|
-
|
|
308
|
+
# Literal values are also permissible:
|
|
309
|
+
sent.channels.voice.update(
|
|
310
|
+
status: :ACTIVE,
|
|
309
311
|
# …
|
|
310
312
|
)
|
|
311
313
|
```
|
data/lib/sentdm/client.rb
CHANGED
|
@@ -115,6 +115,29 @@ module Sentdm
|
|
|
115
115
|
# @return [Sentdm::Resources::Conversations]
|
|
116
116
|
attr_reader :conversations
|
|
117
117
|
|
|
118
|
+
# Phone calls from the numbers you hold, driven by your own callback URL.
|
|
119
|
+
#
|
|
120
|
+
# `POST /v3/channels/voice` enables a number for calls, with the callback URL Sent
|
|
121
|
+
# asks what to do with each call on it, and `POST /v3/channels/voice/tokens` mints
|
|
122
|
+
# a short-lived token that lets a user of your app place and receive calls as that
|
|
123
|
+
# number. When a call arrives or a caller presses a key, a signed question is
|
|
124
|
+
# POSTed to the callback URL and the answer decides the call;
|
|
125
|
+
# `POST /v3/channels/voice/{number}/test` checks the URL answers the way we need
|
|
126
|
+
# before a real call reaches it, and
|
|
127
|
+
# `POST /v3/channels/voice/{number}/rotate-secret` replaces the signing secret.
|
|
128
|
+
# The call events themselves (`call.completed` and the rest) arrive through your
|
|
129
|
+
# webhooks.
|
|
130
|
+
#
|
|
131
|
+
# Every call is a record under `/v3/calls`: read it, list its recordings once one
|
|
132
|
+
# is ready, hang it up, start or stop recording, and add, mute or remove
|
|
133
|
+
# conference participants while it is live. A leg to a phone number runs for at
|
|
134
|
+
# most what your balance affords at the destination's rate.
|
|
135
|
+
# @return [Sentdm::Resources::Calls]
|
|
136
|
+
attr_reader :calls
|
|
137
|
+
|
|
138
|
+
# @return [Sentdm::Resources::Channels]
|
|
139
|
+
attr_reader :channels
|
|
140
|
+
|
|
118
141
|
# Who the current key is.
|
|
119
142
|
#
|
|
120
143
|
# `GET /v3/me` answers with the account the key authenticates as, which is the
|
|
@@ -193,6 +216,8 @@ module Sentdm
|
|
|
193
216
|
@messages = Sentdm::Resources::Messages.new(client: self)
|
|
194
217
|
@contacts = Sentdm::Resources::Contacts.new(client: self)
|
|
195
218
|
@conversations = Sentdm::Resources::Conversations.new(client: self)
|
|
219
|
+
@calls = Sentdm::Resources::Calls.new(client: self)
|
|
220
|
+
@channels = Sentdm::Resources::Channels.new(client: self)
|
|
196
221
|
@me = Sentdm::Resources::Me.new(client: self)
|
|
197
222
|
end
|
|
198
223
|
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentdm
|
|
4
|
+
module Internal
|
|
5
|
+
# @generic Elem
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# if calls_page.has_next?
|
|
9
|
+
# calls_page = calls_page.next_page
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# calls_page.auto_paging_each do |call|
|
|
14
|
+
# puts(call)
|
|
15
|
+
# end
|
|
16
|
+
class CallsPage
|
|
17
|
+
include Sentdm::Internal::Type::BasePage
|
|
18
|
+
|
|
19
|
+
# @return [Data]
|
|
20
|
+
attr_accessor :data
|
|
21
|
+
|
|
22
|
+
# @return [Boolean]
|
|
23
|
+
def next_page?
|
|
24
|
+
data&.pagination&.has_more
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @raise [Sentdm::HTTP::Error]
|
|
28
|
+
# @return [self]
|
|
29
|
+
def next_page
|
|
30
|
+
unless next_page?
|
|
31
|
+
message = "No more pages available. Please check #next_page? before calling ##{__method__}"
|
|
32
|
+
raise RuntimeError.new(message)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
req = Sentdm::Internal::Util.deep_merge(
|
|
36
|
+
@req,
|
|
37
|
+
{query: {page: @req.fetch(:query).fetch(:page, 1).to_i.succ}}
|
|
38
|
+
)
|
|
39
|
+
@client.request(req)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @param blk [Proc]
|
|
43
|
+
#
|
|
44
|
+
# @yieldparam [generic<Elem>]
|
|
45
|
+
def auto_paging_each(&blk)
|
|
46
|
+
unless block_given?
|
|
47
|
+
raise ArgumentError.new("A block must be given to ##{__method__}")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
page = self
|
|
51
|
+
loop do
|
|
52
|
+
page.data&.calls&.each(&blk)
|
|
53
|
+
|
|
54
|
+
break unless page.next_page?
|
|
55
|
+
page = page.next_page
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @api private
|
|
60
|
+
#
|
|
61
|
+
# @param client [Sentdm::Internal::Transport::BaseClient]
|
|
62
|
+
# @param req [Hash{Symbol=>Object}]
|
|
63
|
+
# @param headers [Hash{String=>String}]
|
|
64
|
+
# @param page_data [Hash{Symbol=>Object}]
|
|
65
|
+
def initialize(client:, req:, headers:, page_data:)
|
|
66
|
+
super
|
|
67
|
+
|
|
68
|
+
case page_data
|
|
69
|
+
in {data: Hash | nil => data}
|
|
70
|
+
if (calls = data[:calls]).is_a?(Array)
|
|
71
|
+
data = {**data, calls: calls.map { Sentdm::Internal::Type::Converter.coerce(@model, _1) }}
|
|
72
|
+
end
|
|
73
|
+
@data = Sentdm::Internal::Type::Converter.coerce(Sentdm::Internal::CallsPage::Data, data)
|
|
74
|
+
else
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @api private
|
|
79
|
+
#
|
|
80
|
+
# @return [String]
|
|
81
|
+
def inspect
|
|
82
|
+
model = Sentdm::Internal::Type::Converter.inspect(@model, depth: 1)
|
|
83
|
+
|
|
84
|
+
"#<#{self.class}[#{model}]:0x#{object_id.to_s(16)}>"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class Data < Sentdm::Internal::Type::BaseModel
|
|
88
|
+
# @!attribute calls
|
|
89
|
+
#
|
|
90
|
+
# @return [Array<Object>, nil]
|
|
91
|
+
optional :calls, Sentdm::Internal::Type::ArrayOf[Sentdm::Internal::Type::Unknown]
|
|
92
|
+
|
|
93
|
+
# @!attribute pagination
|
|
94
|
+
#
|
|
95
|
+
# @return [Data::Pagination, nil]
|
|
96
|
+
optional :pagination, -> { Data::Pagination }
|
|
97
|
+
|
|
98
|
+
# @!method initialize(calls: nil, pagination: nil)
|
|
99
|
+
# @param calls [Array<Object>]
|
|
100
|
+
# @param pagination [Data::Pagination]
|
|
101
|
+
|
|
102
|
+
# @see Data#pagination
|
|
103
|
+
class Pagination < Sentdm::Internal::Type::BaseModel
|
|
104
|
+
# @!attribute has_more
|
|
105
|
+
#
|
|
106
|
+
# @return [Boolean, nil]
|
|
107
|
+
optional :has_more, Sentdm::Internal::Type::Boolean
|
|
108
|
+
|
|
109
|
+
# @!method initialize(has_more: nil)
|
|
110
|
+
# @param has_more [Boolean]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentdm
|
|
4
|
+
module Models
|
|
5
|
+
# @see Sentdm::Resources::Calls#retrieve
|
|
6
|
+
class APIResponseOfCall < Sentdm::Internal::Type::BaseModel
|
|
7
|
+
# @!attribute data
|
|
8
|
+
# A call record
|
|
9
|
+
#
|
|
10
|
+
# @return [Sentdm::Models::Call, nil]
|
|
11
|
+
optional :data, -> { Sentdm::Call }, nil?: true
|
|
12
|
+
|
|
13
|
+
# @!attribute error
|
|
14
|
+
# Error information
|
|
15
|
+
#
|
|
16
|
+
# @return [Sentdm::Models::ErrorDetail, nil]
|
|
17
|
+
optional :error, -> { Sentdm::ErrorDetail }, nil?: true
|
|
18
|
+
|
|
19
|
+
# @!attribute meta
|
|
20
|
+
# Request and response metadata
|
|
21
|
+
#
|
|
22
|
+
# @return [Sentdm::Models::APIMeta, nil]
|
|
23
|
+
optional :meta, -> { Sentdm::APIMeta }
|
|
24
|
+
|
|
25
|
+
# @!attribute success
|
|
26
|
+
# Indicates whether the request was successful
|
|
27
|
+
#
|
|
28
|
+
# @return [Boolean, nil]
|
|
29
|
+
optional :success, Sentdm::Internal::Type::Boolean
|
|
30
|
+
|
|
31
|
+
# @!method initialize(data: nil, error: nil, meta: nil, success: nil)
|
|
32
|
+
# Standard API response envelope for all v3 endpoints
|
|
33
|
+
#
|
|
34
|
+
# @param data [Sentdm::Models::Call, nil] A call record
|
|
35
|
+
#
|
|
36
|
+
# @param error [Sentdm::Models::ErrorDetail, nil] Error information
|
|
37
|
+
#
|
|
38
|
+
# @param meta [Sentdm::Models::APIMeta] Request and response metadata
|
|
39
|
+
#
|
|
40
|
+
# @param success [Boolean] Indicates whether the request was successful
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentdm
|
|
4
|
+
module Models
|
|
5
|
+
# @see Sentdm::Resources::Calls#list_recordings
|
|
6
|
+
class APIResponseOfCallRecordings < Sentdm::Internal::Type::BaseModel
|
|
7
|
+
# @!attribute data
|
|
8
|
+
# The recordings of a call, each as a short-lived download link
|
|
9
|
+
#
|
|
10
|
+
# @return [Sentdm::Models::CallRecordings, nil]
|
|
11
|
+
optional :data, -> { Sentdm::CallRecordings }, nil?: true
|
|
12
|
+
|
|
13
|
+
# @!attribute error
|
|
14
|
+
# Error information
|
|
15
|
+
#
|
|
16
|
+
# @return [Sentdm::Models::ErrorDetail, nil]
|
|
17
|
+
optional :error, -> { Sentdm::ErrorDetail }, nil?: true
|
|
18
|
+
|
|
19
|
+
# @!attribute meta
|
|
20
|
+
# Request and response metadata
|
|
21
|
+
#
|
|
22
|
+
# @return [Sentdm::Models::APIMeta, nil]
|
|
23
|
+
optional :meta, -> { Sentdm::APIMeta }
|
|
24
|
+
|
|
25
|
+
# @!attribute success
|
|
26
|
+
# Indicates whether the request was successful
|
|
27
|
+
#
|
|
28
|
+
# @return [Boolean, nil]
|
|
29
|
+
optional :success, Sentdm::Internal::Type::Boolean
|
|
30
|
+
|
|
31
|
+
# @!method initialize(data: nil, error: nil, meta: nil, success: nil)
|
|
32
|
+
# Standard API response envelope for all v3 endpoints
|
|
33
|
+
#
|
|
34
|
+
# @param data [Sentdm::Models::CallRecordings, nil] The recordings of a call, each as a short-lived download link
|
|
35
|
+
#
|
|
36
|
+
# @param error [Sentdm::Models::ErrorDetail, nil] Error information
|
|
37
|
+
#
|
|
38
|
+
# @param meta [Sentdm::Models::APIMeta] Request and response metadata
|
|
39
|
+
#
|
|
40
|
+
# @param success [Boolean] Indicates whether the request was successful
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentdm
|
|
4
|
+
module Models
|
|
5
|
+
class APIResponseOfCallsList < Sentdm::Internal::Type::BaseModel
|
|
6
|
+
# @!attribute data
|
|
7
|
+
# Paginated list of calls
|
|
8
|
+
#
|
|
9
|
+
# @return [Sentdm::Models::CallsList, nil]
|
|
10
|
+
optional :data, -> { Sentdm::CallsList }, nil?: true
|
|
11
|
+
|
|
12
|
+
# @!attribute error
|
|
13
|
+
# Error information
|
|
14
|
+
#
|
|
15
|
+
# @return [Sentdm::Models::ErrorDetail, nil]
|
|
16
|
+
optional :error, -> { Sentdm::ErrorDetail }, nil?: true
|
|
17
|
+
|
|
18
|
+
# @!attribute meta
|
|
19
|
+
# Request and response metadata
|
|
20
|
+
#
|
|
21
|
+
# @return [Sentdm::Models::APIMeta, nil]
|
|
22
|
+
optional :meta, -> { Sentdm::APIMeta }
|
|
23
|
+
|
|
24
|
+
# @!attribute success
|
|
25
|
+
# Indicates whether the request was successful
|
|
26
|
+
#
|
|
27
|
+
# @return [Boolean, nil]
|
|
28
|
+
optional :success, Sentdm::Internal::Type::Boolean
|
|
29
|
+
|
|
30
|
+
# @!method initialize(data: nil, error: nil, meta: nil, success: nil)
|
|
31
|
+
# Standard API response envelope for all v3 endpoints
|
|
32
|
+
#
|
|
33
|
+
# @param data [Sentdm::Models::CallsList, nil] Paginated list of calls
|
|
34
|
+
#
|
|
35
|
+
# @param error [Sentdm::Models::ErrorDetail, nil] Error information
|
|
36
|
+
#
|
|
37
|
+
# @param meta [Sentdm::Models::APIMeta] Request and response metadata
|
|
38
|
+
#
|
|
39
|
+
# @param success [Boolean] Indicates whether the request was successful
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentdm
|
|
4
|
+
module Models
|
|
5
|
+
# @see Sentdm::Resources::Calls#list
|
|
6
|
+
class Call < Sentdm::Internal::Type::BaseModel
|
|
7
|
+
# @!attribute id
|
|
8
|
+
# The call id, the same one carried by the call.request question and every call
|
|
9
|
+
# webhook
|
|
10
|
+
#
|
|
11
|
+
# @return [String, nil]
|
|
12
|
+
optional :id, String
|
|
13
|
+
|
|
14
|
+
# @!attribute answered_at
|
|
15
|
+
# When the call was answered (UTC). Null until then, and always null for a call
|
|
16
|
+
# between two of your app users
|
|
17
|
+
#
|
|
18
|
+
# @return [Time, nil]
|
|
19
|
+
optional :answered_at, Time, nil?: true
|
|
20
|
+
|
|
21
|
+
# @!attribute direction
|
|
22
|
+
# outbound for a call placed from your app, inbound for a call to one of your
|
|
23
|
+
# numbers
|
|
24
|
+
#
|
|
25
|
+
# @return [String, nil]
|
|
26
|
+
optional :direction, String
|
|
27
|
+
|
|
28
|
+
# @!attribute duration_seconds
|
|
29
|
+
# Billable duration in seconds. Null while the call is live
|
|
30
|
+
#
|
|
31
|
+
# @return [Integer, nil]
|
|
32
|
+
optional :duration_seconds, Integer, nil?: true
|
|
33
|
+
|
|
34
|
+
# @!attribute ended_at
|
|
35
|
+
# When the call ended (UTC). Null while the call is live
|
|
36
|
+
#
|
|
37
|
+
# @return [Time, nil]
|
|
38
|
+
optional :ended_at, Time, nil?: true
|
|
39
|
+
|
|
40
|
+
# @!attribute failure_reason
|
|
41
|
+
# Why the call did not complete: callback_timeout, invalid_answer,
|
|
42
|
+
# insufficient_balance, destination_blocked, rejected or no_answer. Null while the
|
|
43
|
+
# call is live, when it completed, and when it failed without a recorded reason
|
|
44
|
+
#
|
|
45
|
+
# @return [String, nil]
|
|
46
|
+
optional :failure_reason, String, nil?: true
|
|
47
|
+
|
|
48
|
+
# @!attribute from
|
|
49
|
+
# One end of a call
|
|
50
|
+
#
|
|
51
|
+
# @return [Sentdm::Models::CallParty, nil]
|
|
52
|
+
optional :from, -> { Sentdm::CallParty }
|
|
53
|
+
|
|
54
|
+
# @!attribute number
|
|
55
|
+
# Your number that owns the call, in E.164 format: the dialed number for an
|
|
56
|
+
# inbound call, the caller's bound number for a call placed from your app
|
|
57
|
+
#
|
|
58
|
+
# @return [String, nil]
|
|
59
|
+
optional :number, String
|
|
60
|
+
|
|
61
|
+
# @!attribute price
|
|
62
|
+
# What the call cost. Null until it has been priced
|
|
63
|
+
#
|
|
64
|
+
# @return [Float, nil]
|
|
65
|
+
optional :price, Float, nil?: true
|
|
66
|
+
|
|
67
|
+
# @!attribute recording_available
|
|
68
|
+
# True once a recording of the call is available
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean, nil]
|
|
71
|
+
optional :recording_available, Sentdm::Internal::Type::Boolean
|
|
72
|
+
|
|
73
|
+
# @!attribute started_at
|
|
74
|
+
# When the call was placed (UTC)
|
|
75
|
+
#
|
|
76
|
+
# @return [Time, nil]
|
|
77
|
+
optional :started_at, Time
|
|
78
|
+
|
|
79
|
+
# @!attribute status
|
|
80
|
+
# initiated, ringing, answered, completed, failed, no_answer or rejected
|
|
81
|
+
#
|
|
82
|
+
# @return [String, nil]
|
|
83
|
+
optional :status, String
|
|
84
|
+
|
|
85
|
+
# @!attribute timeline
|
|
86
|
+
# When the call entered each status, oldest first. Only returned when reading one
|
|
87
|
+
# call
|
|
88
|
+
#
|
|
89
|
+
# @return [Array<Sentdm::Models::CallTimelineEntry>, nil]
|
|
90
|
+
optional :timeline, -> { Sentdm::Internal::Type::ArrayOf[Sentdm::CallTimelineEntry] }, nil?: true
|
|
91
|
+
|
|
92
|
+
# @!attribute to
|
|
93
|
+
# One end of a call
|
|
94
|
+
#
|
|
95
|
+
# @return [Sentdm::Models::CallParty, nil]
|
|
96
|
+
optional :to, -> { Sentdm::CallParty }
|
|
97
|
+
|
|
98
|
+
# @!method initialize(id: nil, answered_at: nil, direction: nil, duration_seconds: nil, ended_at: nil, failure_reason: nil, from: nil, number: nil, price: nil, recording_available: nil, started_at: nil, status: nil, timeline: nil, to: nil)
|
|
99
|
+
# Some parameter documentations has been truncated, see {Sentdm::Models::Call} for
|
|
100
|
+
# more details.
|
|
101
|
+
#
|
|
102
|
+
# A call record
|
|
103
|
+
#
|
|
104
|
+
# @param id [String] The call id, the same one carried by the call.request question and every call we
|
|
105
|
+
#
|
|
106
|
+
# @param answered_at [Time, nil] When the call was answered (UTC). Null until then, and always null for a call be
|
|
107
|
+
#
|
|
108
|
+
# @param direction [String] outbound for a call placed from your app, inbound for a call to one of your numb
|
|
109
|
+
#
|
|
110
|
+
# @param duration_seconds [Integer, nil] Billable duration in seconds. Null while the call is live
|
|
111
|
+
#
|
|
112
|
+
# @param ended_at [Time, nil] When the call ended (UTC). Null while the call is live
|
|
113
|
+
#
|
|
114
|
+
# @param failure_reason [String, nil] Why the call did not complete: callback_timeout, invalid_answer, insufficient_ba
|
|
115
|
+
#
|
|
116
|
+
# @param from [Sentdm::Models::CallParty] One end of a call
|
|
117
|
+
#
|
|
118
|
+
# @param number [String] Your number that owns the call, in E.164 format: the dialed number for an inboun
|
|
119
|
+
#
|
|
120
|
+
# @param price [Float, nil] What the call cost. Null until it has been priced
|
|
121
|
+
#
|
|
122
|
+
# @param recording_available [Boolean] True once a recording of the call is available
|
|
123
|
+
#
|
|
124
|
+
# @param started_at [Time] When the call was placed (UTC)
|
|
125
|
+
#
|
|
126
|
+
# @param status [String] initiated, ringing, answered, completed, failed, no_answer or rejected
|
|
127
|
+
#
|
|
128
|
+
# @param timeline [Array<Sentdm::Models::CallTimelineEntry>, nil] When the call entered each status, oldest first. Only returned when reading one
|
|
129
|
+
#
|
|
130
|
+
# @param to [Sentdm::Models::CallParty] One end of a call
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentdm
|
|
4
|
+
module Models
|
|
5
|
+
class CallEvent < Sentdm::Internal::Type::BaseModel
|
|
6
|
+
# @!attribute event
|
|
7
|
+
# The specific event within the family, for example message.delivered,
|
|
8
|
+
# message.received or contact.opt_out. Absent on events that have no subtype, so
|
|
9
|
+
# treat it as optional.
|
|
10
|
+
#
|
|
11
|
+
# @return [String, nil]
|
|
12
|
+
optional :event, String, nil?: true
|
|
13
|
+
|
|
14
|
+
# @!attribute field
|
|
15
|
+
# The event family, for example message, templates or contact. Route on this
|
|
16
|
+
# first, then on event for the specific change.
|
|
17
|
+
#
|
|
18
|
+
# @return [String, nil]
|
|
19
|
+
optional :field, String
|
|
20
|
+
|
|
21
|
+
# @!attribute payload
|
|
22
|
+
# Body of a call.initiated, call.answered, call.completed, call.failed or
|
|
23
|
+
# call.recording_ready event. Which of them occurred is the envelope's event.
|
|
24
|
+
#
|
|
25
|
+
# Shaped like the message, inbound, template and channel payloads: account_id
|
|
26
|
+
# names the account the event is about, channel names the channel, and updated_at
|
|
27
|
+
# is when the change happened on the call, in the same yyyy-MM-ddTHH:mm:ssZ form.
|
|
28
|
+
# duration_seconds and price are added on call.completed, reason on call.failed
|
|
29
|
+
# and recording_id on call.recording_ready; each is omitted rather than sent as
|
|
30
|
+
# null when it does not apply.
|
|
31
|
+
#
|
|
32
|
+
# Casing is snake_case because these ride the same webhook stream customers
|
|
33
|
+
# already parse message_id from; the question/answer contract is a separate
|
|
34
|
+
# surface and stays camelCase. Nothing here is provider-shaped: no provider call
|
|
35
|
+
# id, no namespaced identity.
|
|
36
|
+
#
|
|
37
|
+
# @return [Sentdm::Models::CallEventPayload, nil]
|
|
38
|
+
optional :payload, -> { Sentdm::CallEventPayload }, nil?: true
|
|
39
|
+
|
|
40
|
+
# @!attribute request_id
|
|
41
|
+
# The event-specific body.
|
|
42
|
+
#
|
|
43
|
+
# @return [String, nil]
|
|
44
|
+
optional :request_id, String, nil?: true
|
|
45
|
+
|
|
46
|
+
# @!attribute timestamp
|
|
47
|
+
# When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
|
|
48
|
+
# time, not the time the underlying change happened. Use the timestamp inside the
|
|
49
|
+
# payload for the latter.
|
|
50
|
+
#
|
|
51
|
+
# @return [String, nil]
|
|
52
|
+
optional :timestamp, String
|
|
53
|
+
|
|
54
|
+
# @!method initialize(event: nil, field: nil, payload: nil, request_id: nil, timestamp: nil)
|
|
55
|
+
# Some parameter documentations has been truncated, see
|
|
56
|
+
# {Sentdm::Models::CallEvent} for more details.
|
|
57
|
+
#
|
|
58
|
+
# The envelope Sent POSTs to a subscribed webhook endpoint. Every event shares
|
|
59
|
+
# this shape and varies only in Payload.
|
|
60
|
+
#
|
|
61
|
+
# @param event [String, nil] The specific event within the family, for example message.delivered,
|
|
62
|
+
#
|
|
63
|
+
# @param field [String] The event family, for example message, templates or contact. Route on
|
|
64
|
+
#
|
|
65
|
+
# @param payload [Sentdm::Models::CallEventPayload, nil] Body of a call.initiated, call.answered, call.completed, call.failed
|
|
66
|
+
#
|
|
67
|
+
# @param request_id [String, nil] The event-specific body.
|
|
68
|
+
#
|
|
69
|
+
# @param timestamp [String] When Sent emitted the event, in UTC (yyyy-MM-ddTHH:mm:ssZ). This is the emission
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sentdm
|
|
4
|
+
module Models
|
|
5
|
+
class CallEventPayload < Sentdm::Internal::Type::BaseModel
|
|
6
|
+
# @!attribute call_id
|
|
7
|
+
# Sent's call id, the same one the customer saw on the first question.
|
|
8
|
+
#
|
|
9
|
+
# @return [String]
|
|
10
|
+
required :call_id, String
|
|
11
|
+
|
|
12
|
+
# @!attribute account_id
|
|
13
|
+
# The account the call belongs to: the key's own customer, or the sender profile
|
|
14
|
+
# it acted as.
|
|
15
|
+
#
|
|
16
|
+
# @return [String, nil]
|
|
17
|
+
optional :account_id, String
|
|
18
|
+
|
|
19
|
+
# @!attribute channel
|
|
20
|
+
# Always voice.
|
|
21
|
+
#
|
|
22
|
+
# @return [String, nil]
|
|
23
|
+
optional :channel, String
|
|
24
|
+
|
|
25
|
+
# @!attribute duration_seconds
|
|
26
|
+
# How long the call lasted. Only on call.completed.
|
|
27
|
+
#
|
|
28
|
+
# @return [Integer, nil]
|
|
29
|
+
optional :duration_seconds, Integer, nil?: true
|
|
30
|
+
|
|
31
|
+
# @!attribute number
|
|
32
|
+
# The customer number that owns the call, in E.164 format.
|
|
33
|
+
#
|
|
34
|
+
# @return [String, nil]
|
|
35
|
+
optional :number, String
|
|
36
|
+
|
|
37
|
+
# @!attribute price
|
|
38
|
+
# What the call was charged. Only on call.completed, and omitted there until
|
|
39
|
+
# billing has recorded the charge.
|
|
40
|
+
#
|
|
41
|
+
# @return [Float, nil]
|
|
42
|
+
optional :price, Float, nil?: true
|
|
43
|
+
|
|
44
|
+
# @!attribute reason
|
|
45
|
+
# The machine-readable reason the call did not complete. Only on call.failed, and
|
|
46
|
+
# omitted when no reason was recorded.
|
|
47
|
+
#
|
|
48
|
+
# @return [String, nil]
|
|
49
|
+
optional :reason, String, nil?: true
|
|
50
|
+
|
|
51
|
+
# @!attribute recording_id
|
|
52
|
+
# The recording that became available, the same id GET /v3/calls/{id}/recordings
|
|
53
|
+
# lists it under. Only on call.recording_ready, which is sent once per recording.
|
|
54
|
+
#
|
|
55
|
+
# @return [String, nil]
|
|
56
|
+
optional :recording_id, String, nil?: true
|
|
57
|
+
|
|
58
|
+
# @!attribute updated_at
|
|
59
|
+
# When the change happened on the call, as opposed to when the event was emitted.
|
|
60
|
+
#
|
|
61
|
+
# @return [String, nil]
|
|
62
|
+
optional :updated_at, String
|
|
63
|
+
|
|
64
|
+
# @!method initialize(call_id:, account_id: nil, channel: nil, duration_seconds: nil, number: nil, price: nil, reason: nil, recording_id: nil, updated_at: nil)
|
|
65
|
+
# Some parameter documentations has been truncated, see
|
|
66
|
+
# {Sentdm::Models::CallEventPayload} for more details.
|
|
67
|
+
#
|
|
68
|
+
# Body of a call.initiated, call.answered, call.completed, call.failed or
|
|
69
|
+
# call.recording_ready event. Which of them occurred is the envelope's event.
|
|
70
|
+
#
|
|
71
|
+
# Shaped like the message, inbound, template and channel payloads: account_id
|
|
72
|
+
# names the account the event is about, channel names the channel, and updated_at
|
|
73
|
+
# is when the change happened on the call, in the same yyyy-MM-ddTHH:mm:ssZ form.
|
|
74
|
+
# duration_seconds and price are added on call.completed, reason on call.failed
|
|
75
|
+
# and recording_id on call.recording_ready; each is omitted rather than sent as
|
|
76
|
+
# null when it does not apply.
|
|
77
|
+
#
|
|
78
|
+
# Casing is snake_case because these ride the same webhook stream customers
|
|
79
|
+
# already parse message_id from; the question/answer contract is a separate
|
|
80
|
+
# surface and stays camelCase. Nothing here is provider-shaped: no provider call
|
|
81
|
+
# id, no namespaced identity.
|
|
82
|
+
#
|
|
83
|
+
# @param call_id [String] Sent's call id, the same one the customer saw on the first question.
|
|
84
|
+
#
|
|
85
|
+
# @param account_id [String] The account the call belongs to: the key's own customer, or the sender profile i
|
|
86
|
+
#
|
|
87
|
+
# @param channel [String] Always voice.
|
|
88
|
+
#
|
|
89
|
+
# @param duration_seconds [Integer, nil] How long the call lasted. Only on call.completed.
|
|
90
|
+
#
|
|
91
|
+
# @param number [String] The customer number that owns the call, in E.164 format.
|
|
92
|
+
#
|
|
93
|
+
# @param price [Float, nil] What the call was charged. Only on call.completed, and omitted there until billi
|
|
94
|
+
#
|
|
95
|
+
# @param reason [String, nil] The machine-readable reason the call did not complete. Only on call.failed, and
|
|
96
|
+
#
|
|
97
|
+
# @param recording_id [String, nil] The recording that became available, the same id GET /v3/calls/{id}/recordings l
|
|
98
|
+
#
|
|
99
|
+
# @param updated_at [String] When the change happened on the call, as opposed to when the event was emitted.
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|