vonage 7.3.0 → 7.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71e2807806583d50e0ce062ffaaafbcb69f1629e3ba010a6cedbcc22cfcc0741
4
- data.tar.gz: da726714cefba39cd4e78f7356dbc566e4c7a61718b968281e1625c4787f5d84
3
+ metadata.gz: a62ffd2c9e2a145badeb3e1e232b7959ca2b31c0d7355c1cf804db7e955cb96a
4
+ data.tar.gz: 6fa0cae4317a331b4218e3d479bff3b7031145c36386c7d19e94d59dfb9cc927
5
5
  SHA512:
6
- metadata.gz: 13e4ce32d69d4bdc899e7143137e926891423e4d838d3ede9d606481e4da5840c47d6c4c4368ee428e1df6264ab36980cecfd485fbf4aa5b183cc340f4b05d72
7
- data.tar.gz: 711085663db7d880b0b5fe7d1acee074c08052a13fef61982df704681c6b2482178126ff9b0d3d868514e5b18ef61a7a39259be7095db362699f0778aa0a9918
6
+ metadata.gz: fc3c593e76119e4c74094db945f74da19e0289766752c18f89f5de8491753b983f0d130e02af4be4c6bbb70c0ee0e73d0b626af96c676b86c15b0fdbd62803c2
7
+ data.tar.gz: 702152725c3a7a707965e971ba0b4599bac7e62dd842037ad9c4e784d68f0d645c90e203093fff806016cb6efc2f7e5269103f14ba2fe7a1fd255b265bd12ed9
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Vonage Server SDK for Ruby
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/vonage.svg)](https://badge.fury.io/rb/vonage) ![Coverage Status](https://github.com/Vonage/vonage-ruby-sdk/workflows/CI/badge.svg) [![codecov](https://codecov.io/gh/Vonage/vonage-ruby-sdk/branch/master/graph/badge.svg)](https://codecov.io/gh/vonage/vonage-ruby-sdk)
3
+ [![Gem Version](https://badge.fury.io/rb/vonage.svg)](https://badge.fury.io/rb/vonage) ![Coverage Status](https://github.com/Vonage/vonage-ruby-sdk/workflows/CI/badge.svg) [![codecov](https://codecov.io/gh/Vonage/vonage-ruby-sdk/branch/7.x/graph/badge.svg?token=FKW6KL532P)](https://codecov.io/gh/Vonage/vonage-ruby-sdk)
4
4
 
5
5
 
6
6
  <img src="https://developer.nexmo.com/assets/images/Vonage_Nexmo.svg" height="48px" alt="Nexmo is now known as Vonage" />
@@ -16,6 +16,7 @@ need a Vonage account. Sign up [for free at vonage.com][signup].
16
16
  * [JWT authentication](#jwt-authentication)
17
17
  * [Webhook signatures](#webhook-signatures)
18
18
  * [Pagination](#pagination)
19
+ * [NCCO Builder](#ncco-builder)
19
20
  * [Documentation](#documentation)
20
21
  * [Frequently Asked Questions](#frequently-asked-questions)
21
22
  * [Supported APIs](#supported-apis)
@@ -167,6 +168,32 @@ To modify the `auto_advance` behavior you can specify it in your method:
167
168
  client.applications.list(auto_advance: false)
168
169
  ```
169
170
 
171
+ ## NCCO Builder
172
+
173
+ The Vonage Voice API accepts instructions via JSON objects called NCCOs. Each NCCO can be made up multiple actions that are executed in the order they are written. The Vonage API Developer Portal contains an [NCCO Reference](https://developer.vonage.com/voice/voice-api/ncco-reference) with instructions and information on all the parameters possible.
174
+
175
+ The SDK includes an NCCO builder that you can use to build NCCOs for your Voice API methods.
176
+
177
+ For example, to build `talk` and `input` NCCO actions and then combine them into a single NCCO you would do the following:
178
+
179
+ ```ruby
180
+ talk = Vonage::Voice::Ncco.talk(text: 'Hello World!')
181
+ input = Vonage::Voice::Ncco.input(type: ['dtmf'], dtmf: { bargeIn: true })
182
+ ncco = Vonage::Voice::Ncco.build(talk, input)
183
+
184
+ # => [{:action=>"talk", :text=>"Hello World!"}, {:action=>"input", :type=>["dtmf"], :dtmf=>{:bargeIn=>true}}]
185
+ ```
186
+
187
+ Once you have the constructed NCCO you can then use it in a Voice API request:
188
+
189
+ ```ruby
190
+ response = client.voice.create({
191
+ to: [{type: 'phone', number: '14843331234'}],
192
+ from: {type: 'phone', number: '14843335555'},
193
+ ncco: ncco
194
+ })
195
+ ```
196
+
170
197
  ## Documentation
171
198
 
172
199
  Vonage Ruby documentation: https://www.rubydoc.info/github/Vonage/vonage-ruby-sdk
@@ -1,5 +1,5 @@
1
1
  # typed: strong
2
2
 
3
3
  module Vonage
4
- VERSION = '7.3.0'
4
+ VERSION = '7.4.0'
5
5
  end
@@ -0,0 +1,199 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+ require 'phonelib'
4
+
5
+ module Vonage
6
+ class Voice::Actions::Connect
7
+ attr_accessor :endpoint, :from, :eventType, :timeout, :limit, :machineDetection, :eventUrl, :eventMethod, :ringbackTone
8
+
9
+ def initialize(attributes = {})
10
+ @endpoint = attributes.fetch(:endpoint)
11
+ @from = attributes.fetch(:from, nil)
12
+ @eventType = attributes.fetch(:eventType, nil)
13
+ @timeout = attributes.fetch(:timeout, nil)
14
+ @limit = attributes.fetch(:limit, nil)
15
+ @machineDetection = attributes.fetch(:machineDetection, nil)
16
+ @eventUrl = attributes.fetch(:eventUrl, nil)
17
+ @eventMethod = attributes.fetch(:eventMethod, nil)
18
+ @ringbackTone = attributes.fetch(:ringbackTone, nil)
19
+
20
+ after_initialize!
21
+ end
22
+
23
+ def after_initialize!
24
+ verify_endpoint
25
+
26
+ if self.from
27
+ verify_from
28
+ end
29
+
30
+ if self.eventType
31
+ verify_event_type
32
+ end
33
+
34
+ if self.limit
35
+ verify_limit
36
+ end
37
+
38
+ if self.machineDetection
39
+ verify_machine_detection
40
+ end
41
+
42
+ if self.eventUrl
43
+ verify_event_url
44
+ end
45
+
46
+ if self.eventMethod
47
+ verify_event_method
48
+ end
49
+
50
+ if self.ringbackTone
51
+ verify_ringback_tone
52
+ end
53
+ end
54
+
55
+ def verify_endpoint
56
+ case self.endpoint[:type]
57
+ when 'phone'
58
+ raise ClientError.new("Expected 'number' value to be in E.164 format") unless Phonelib.parse(endpoint[:number].to_i).valid?
59
+ when 'app'
60
+ raise ClientError.new("'user' must be defined") unless endpoint[:user]
61
+ when 'websocket'
62
+ raise ClientError.new("Expected 'uri' value to be a valid URI") unless URI.parse(endpoint[:uri]).kind_of?(URI::Generic)
63
+ raise ClientError.new("Expected 'content-type' parameter to be either 'audio/116;rate=16000' or 'audio/116;rate=8000") unless endpoint[:'content-type'] == 'audio/116;rate=16000' || endpoint[:'content-type'] == 'audio/116;rate=8000'
64
+ when 'sip'
65
+ raise ClientError.new("Expected 'uri' value to be a valid URI") unless URI.parse(endpoint[:uri]).kind_of?(URI::Generic)
66
+ end
67
+ end
68
+
69
+ def verify_from
70
+ raise ClientError.new("Invalid 'from' value, must be in E.164 format") unless Phonelib.parse(self.from.to_i).valid?
71
+ end
72
+
73
+ def verify_event_type
74
+ raise ClientError.new("Invalid 'eventType' value, must be 'synchronous' if defined") unless self.eventType == 'synchronous'
75
+ end
76
+
77
+ def verify_limit
78
+ raise ClientError.new("Invalid 'limit' value, must be between 0 and 7200 seconds") unless self.limit.to_i >= 0 && self.limit.to_i <= 7200
79
+ end
80
+
81
+ def verify_machine_detection
82
+ raise ClientError.new("Invalid 'machineDetection' value, must be either: 'continue' or 'hangup' if defined") unless self.machineDetection == 'continue' || self.machineDetection == 'hangup'
83
+ end
84
+
85
+ def verify_event_url
86
+ uri = URI.parse(self.eventUrl)
87
+
88
+ raise ClientError.new("Invalid 'eventUrl' value, must be a valid URL") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
89
+
90
+ self.eventUrl
91
+ end
92
+
93
+ def verify_event_method
94
+ valid_methods = ['GET', 'POST']
95
+
96
+ raise ClientError.new("Invalid 'eventMethod' value. must be either: 'GET' or 'POST'") unless valid_methods.include?(self.eventMethod.upcase)
97
+ end
98
+
99
+ def verify_ringback_tone
100
+ uri = URI.parse(self.ringbackTone)
101
+
102
+ raise ClientError.new("Invalid 'ringbackTone' value, must be a valid URL") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
103
+
104
+ self.ringbackTone
105
+ end
106
+
107
+ def action
108
+ create_connect!(self)
109
+ end
110
+
111
+ def create_connect!(builder)
112
+ ncco = [
113
+ {
114
+ action: 'connect',
115
+ endpoint: [
116
+ create_endpoint(builder)
117
+ ]
118
+ }
119
+ ]
120
+
121
+ ncco[0].merge!(from: builder.from) if builder.from
122
+ ncco[0].merge!(eventType: builder.eventType) if builder.eventType
123
+ ncco[0].merge!(timeout: builder.timeout) if builder.timeout
124
+ ncco[0].merge!(limit: builder.limit) if builder.limit
125
+ ncco[0].merge!(machineDetection: builder.machineDetection) if builder.machineDetection
126
+ ncco[0].merge!(eventUrl: builder.eventUrl) if builder.eventUrl
127
+ ncco[0].merge!(eventMethod: builder.eventMethod) if builder.eventMethod
128
+ ncco[0].merge!(ringbackTone: builder.ringbackTone) if builder.ringbackTone
129
+
130
+ ncco
131
+ end
132
+
133
+ def create_endpoint(builder)
134
+ case builder.endpoint[:type]
135
+ when 'phone'
136
+ phone_endpoint(builder.endpoint)
137
+ when 'app'
138
+ app_endpoint(builder.endpoint)
139
+ when 'websocket'
140
+ websocket_endpoint(builder.endpoint)
141
+ when 'sip'
142
+ sip_endpoint(builder.endpoint)
143
+ when 'vbc'
144
+ vbc_endpoint(builder.endpoint)
145
+ else
146
+ raise ClientError.new("Invalid value for 'endpoint', please refer to the Vonage API Developer Portal https://developer.nexmo.com/voice/voice-api/ncco-reference#endpoint-types-and-values for a list of possible values")
147
+ end
148
+ end
149
+
150
+ def phone_endpoint(endpoint_attrs)
151
+ hash = {
152
+ type: 'phone',
153
+ number: endpoint_attrs[:number]
154
+ }
155
+
156
+ hash.merge!(dtmfAnswer: endpoint_attrs[:dtmfAnswer]) if endpoint_attrs[:dtmfAnswer]
157
+ hash.merge!(onAnswer: endpoint_attrs[:onAnswer]) if endpoint_attrs[:onAnswer]
158
+
159
+ hash
160
+ end
161
+
162
+ def app_endpoint(endpoint_attrs)
163
+ {
164
+ type: 'app',
165
+ user: endpoint_attrs[:user]
166
+ }
167
+ end
168
+
169
+ def websocket_endpoint(endpoint_attrs)
170
+ hash = {
171
+ type: 'websocket',
172
+ uri: endpoint_attrs[:uri],
173
+ :'content-type' => endpoint_attrs[:'content-type']
174
+ }
175
+
176
+ hash.merge!(headers: endpoint_attrs[:headers]) if endpoint_attrs[:headers]
177
+
178
+ hash
179
+ end
180
+
181
+ def sip_endpoint(endpoint_attrs)
182
+ hash = {
183
+ type: 'sip',
184
+ uri: endpoint_attrs[:uri]
185
+ }
186
+
187
+ hash.merge!(headers: endpoint_attrs[:headers]) if endpoint_attrs[:headers]
188
+
189
+ hash
190
+ end
191
+
192
+ def vbc_endpoint(endpoint_attrs)
193
+ {
194
+ type: 'vbc',
195
+ extension: endpoint_attrs[:extension]
196
+ }
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,107 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Voice::Actions::Conversation
6
+ attr_accessor :name, :musicOnHoldUrl, :startOnEnter, :endOnExit, :record, :canSpeak, :canHear, :mute
7
+
8
+ def initialize(attributes = {})
9
+ @name = attributes.fetch(:name)
10
+ @musicOnHoldUrl = attributes.fetch(:musicOnHoldUrl, nil)
11
+ @startOnEnter = attributes.fetch(:startOnEnter, nil)
12
+ @endOnExit = attributes.fetch(:endOnExit, nil)
13
+ @record = attributes.fetch(:record, nil)
14
+ @canSpeak = attributes.fetch(:canSpeak, nil)
15
+ @canHear = attributes.fetch(:canHear, nil)
16
+ @mute = attributes.fetch(:mute, nil)
17
+
18
+ after_initialize!
19
+ end
20
+
21
+ def after_initialize!
22
+ if self.musicOnHoldUrl
23
+ verify_music_on_hold_url
24
+ end
25
+
26
+ if self.startOnEnter
27
+ verify_start_on_enter
28
+ end
29
+
30
+ if self.endOnExit
31
+ verify_end_on_exit
32
+ end
33
+
34
+ if self.record
35
+ verify_record
36
+ end
37
+
38
+ if self.canSpeak
39
+ verify_can_speak
40
+ end
41
+
42
+ if self.canHear
43
+ verify_can_hear
44
+ end
45
+
46
+ if self.mute
47
+ verify_mute
48
+ end
49
+ end
50
+
51
+ def verify_music_on_hold_url
52
+ uri = URI.parse(self.musicOnHoldUrl)
53
+
54
+ raise ClientError.new("Invalid 'musicOnHoldUrl' value, must be a valid URL") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
55
+
56
+ self.musicOnHoldUrl
57
+ end
58
+
59
+ def verify_start_on_enter
60
+ raise ClientError.new("Expected 'startOnEnter' value to be a Boolean") unless self.startOnEnter == true || self.startOnEnter == false
61
+ end
62
+
63
+ def verify_end_on_exit
64
+ raise ClientError.new("Expected 'endOnExit' value to be a Boolean") unless self.endOnExit == true || self.endOnExit == false
65
+ end
66
+
67
+ def verify_record
68
+ raise ClientError.new("Expected 'record' value to be a Boolean") unless self.record == true || self.record == false
69
+ end
70
+
71
+ def verify_can_speak
72
+ raise ClientError.new("Expected 'canSpeak' value to be an Array of leg UUIDs") unless self.canSpeak.is_a?(Array)
73
+ end
74
+
75
+ def verify_can_hear
76
+ raise ClientError.new("Expected 'canHear' value to be an Array of leg UUIDs") unless self.canHear.is_a?(Array)
77
+ end
78
+
79
+ def verify_mute
80
+ raise ClientError.new("Expected 'mute' value to be a Boolean") unless self.mute == true || self.mute == false
81
+ raise ClientError.new("The 'mute' value is not supported if the 'canSpeak' option is defined") if self.canSpeak
82
+ end
83
+
84
+ def action
85
+ create_conversation!(self)
86
+ end
87
+
88
+ def create_conversation!(builder)
89
+ ncco = [
90
+ {
91
+ action: 'conversation',
92
+ name: builder.name
93
+ }
94
+ ]
95
+
96
+ ncco[0].merge!(musicOnHoldUrl: builder.musicOnHoldUrl) if (builder.musicOnHoldUrl || builder.musicOnHoldUrl == false)
97
+ ncco[0].merge!(startOnEnter: builder.startOnEnter) if (builder.startOnEnter || builder.startOnEnter == false)
98
+ ncco[0].merge!(endOnExit: builder.endOnExit) if (builder.endOnExit || builder.endOnExit == false)
99
+ ncco[0].merge!(record: builder.record) if builder.record
100
+ ncco[0].merge!(canSpeak: builder.canSpeak) if builder.canSpeak
101
+ ncco[0].merge!(canHear: builder.canHear) if builder.canHear
102
+ ncco[0].merge!(mute: builder.mute) if builder.mute
103
+
104
+ ncco
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,119 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Voice::Actions::Input
6
+ attr_accessor :type, :dtmf, :speech, :eventUrl, :eventMethod
7
+
8
+ def initialize(attributes = {})
9
+ @type = attributes.fetch(:type)
10
+ @dtmf = attributes.fetch(:dtmf, nil)
11
+ @speech = attributes.fetch(:speech, nil)
12
+ @eventUrl = attributes.fetch(:eventUrl, nil)
13
+ @eventMethod = attributes.fetch(:eventMethod, nil)
14
+
15
+ after_initialize!
16
+ end
17
+
18
+ def after_initialize!
19
+ validate_type
20
+
21
+ if self.dtmf
22
+ validate_dtmf
23
+ end
24
+
25
+ if self.speech
26
+ validate_speech
27
+ end
28
+
29
+ if self.eventUrl
30
+ validate_event_url
31
+ end
32
+
33
+ if self.eventMethod
34
+ validate_event_method
35
+ end
36
+ end
37
+
38
+ def validate_type
39
+ valid_types = ['dtmf', 'speech']
40
+
41
+ raise ClientError.new("Invalid 'type', must be an Array of at least one String") unless self.type.is_a?(Array)
42
+ raise ClientError.new("Invalid 'type' value, must be 'dtmf', 'speech' or both 'dtmf' and 'speech'") if (valid_types & self.type).empty?
43
+ end
44
+
45
+ def validate_dtmf
46
+ raise ClientError.new("Expected 'dtmf' to be included in 'type' parameter if 'dtmf' options specified") unless self.type.include?('dtmf')
47
+
48
+ if self.dtmf[:timeOut]
49
+ raise ClientError.new("Expected 'timeOut' to not be more than 10 seconds") if self.dtmf[:timeOut] > 10
50
+ end
51
+
52
+ if self.dtmf[:maxDigits]
53
+ raise ClientError.new("Expected 'maxDigits' to not be more than 22") if self.dtmf[:maxDigits] > 22
54
+ end
55
+
56
+ if self.dtmf[:submitOnHash]
57
+ raise ClientError.new("Invalid 'submitOnHash' value, must be a Boolean") unless self.dtmf[:submitOnHash] == true || self.dtmf[:submitOnHash] == false
58
+ end
59
+ end
60
+
61
+ def validate_speech
62
+ raise ClientError.new("Expected 'speech' to be included in 'type' parameter if 'speech' options specified") unless self.type.include?('speech')
63
+
64
+ if self.speech[:uuid]
65
+ raise ClientError.new("Invalid 'uuid' value, must be an Array containing a single call leg ID element") unless self.speech[:uuid].is_a?(Array)
66
+ end
67
+
68
+ if self.speech[:endOnSilence]
69
+ raise ClientError.new("Expected 'endOnSilence' to not be more than 10 seconds") unless self.speech[:endOnSilence] <= 10 && self.speech[:endOnSilence] >= 0
70
+ end
71
+
72
+ if self.speech[:context]
73
+ raise ClientError.new("Expected 'context' to be an Array of strings") unless self.speech[:context].is_a?(Array)
74
+ end
75
+
76
+ if self.speech[:startTimeout]
77
+ raise ClientError.new("Expected 'startTimeout' to not be more than 10 seconds") unless self.speech[:startTimeout] <= 10 && self.speech[:startTimeout] >= 0
78
+ end
79
+
80
+ if self.speech[:maxDuration]
81
+ raise ClientError.new("Expected 'maxDuration' to not be more than 60 seconds") unless self.speech[:maxDuration] <= 60 && self.speech[:maxDuration] >= 0
82
+ end
83
+ end
84
+
85
+ def validate_event_url
86
+ uri = URI.parse(self.eventUrl)
87
+
88
+ raise ClientError.new("Invalid 'eventUrl' value, must be a valid URL") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
89
+
90
+ self.eventUrl
91
+ end
92
+
93
+ def validate_event_method
94
+ valid_methods = ['GET', 'POST']
95
+
96
+ raise ClientError.new("Invalid 'eventMethod' value. must be either: 'GET' or 'POST'") unless valid_methods.include?(self.eventMethod.upcase)
97
+ end
98
+
99
+ def action
100
+ create_input!(self)
101
+ end
102
+
103
+ def create_input!(builder)
104
+ ncco = [
105
+ {
106
+ action: 'input',
107
+ type: builder.type
108
+ }
109
+ ]
110
+
111
+ ncco[0].merge!(dtmf: builder.dtmf) if builder.dtmf
112
+ ncco[0].merge!(speech: builder.speech) if builder.speech
113
+ ncco[0].merge!(eventUrl: builder.eventUrl) if builder.eventUrl
114
+ ncco[0].merge!(eventMethod: builder.eventMethod) if builder.eventMethod
115
+
116
+ ncco
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,57 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Voice::Actions::Notify
6
+ attr_accessor :payload, :eventUrl, :eventMethod
7
+
8
+ def initialize(attributes = {})
9
+ @payload = attributes.fetch(:payload)
10
+ @eventUrl = attributes.fetch(:eventUrl)
11
+ @eventMethod = attributes.fetch(:eventMethod, nil)
12
+
13
+ after_initialize!
14
+ end
15
+
16
+ def after_initialize!
17
+ validate_event_url
18
+
19
+ if self.eventMethod
20
+ validate_event_method
21
+ end
22
+ end
23
+
24
+ def validate_event_url
25
+ uri = URI.parse(self.eventUrl[0])
26
+
27
+ raise ClientError.new("Expected 'eventUrl' value to be an Array with a single string") unless self.eventUrl.is_a?(Array)
28
+ raise ClientError.new("Invalid 'eventUrl' value, must be a valid URL") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
29
+
30
+ self.eventUrl
31
+ end
32
+
33
+ def validate_event_method
34
+ valid_methods = ['GET', 'POST']
35
+
36
+ raise ClientError.new("Invalid 'eventMethod' value. must be either: 'GET' or 'POST'") unless valid_methods.include?(self.eventMethod.upcase)
37
+ end
38
+
39
+ def action
40
+ create_notify!(self)
41
+ end
42
+
43
+ def create_notify!(builder)
44
+ ncco = [
45
+ {
46
+ action: 'notify',
47
+ payload: builder.payload,
48
+ eventUrl: builder.eventUrl
49
+ }
50
+ ]
51
+
52
+ ncco[0].merge!(eventMethod: builder.eventMethod) if builder.eventMethod
53
+
54
+ ncco
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,130 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Voice::Actions::Record
6
+ attr_accessor :format, :split, :channels, :endOnSilence, :endOnKey, :timeOut, :beepStart, :eventUrl, :eventMethod
7
+
8
+ def initialize(attributes = {})
9
+ @format = attributes.fetch(:format, nil)
10
+ @split = attributes.fetch(:split, nil)
11
+ @channels = attributes.fetch(:channels, nil)
12
+ @endOnSilence = attributes.fetch(:endOnSilence, nil)
13
+ @endOnKey = attributes.fetch(:endOnKey, nil)
14
+ @timeOut = attributes.fetch(:timeOut, nil)
15
+ @beepStart = attributes.fetch(:beepStart, nil)
16
+ @eventUrl = attributes.fetch(:eventUrl, nil)
17
+ @eventMethod = attributes.fetch(:eventMethod, nil)
18
+
19
+ after_initialize!
20
+ end
21
+
22
+ def after_initialize!
23
+ if self.format
24
+ validate_format
25
+ end
26
+
27
+ if self.split
28
+ validate_split
29
+ end
30
+
31
+ if self.channels
32
+ validate_channels
33
+ end
34
+
35
+ if self.endOnSilence
36
+ validate_end_on_silence
37
+ end
38
+
39
+ if self.endOnKey
40
+ validate_end_on_key
41
+ end
42
+
43
+ if self.timeOut
44
+ validate_time_out
45
+ end
46
+
47
+ if self.beepStart
48
+ validate_beep_start
49
+ end
50
+
51
+ if self.eventUrl
52
+ validate_event_url
53
+ end
54
+
55
+ if self.eventMethod
56
+ validate_event_method
57
+ end
58
+ end
59
+
60
+ def validate_format
61
+ valid_formats = ['mp3', 'wav', 'ogg']
62
+
63
+ raise ClientError.new("Invalid format, must be one of: 'mp3', 'wav', 'ogg'") unless valid_formats.include?(self.format)
64
+ end
65
+
66
+ def validate_split
67
+ raise ClientError.new("Expected 'split' value to be 'conversation' if defined") unless self.split == 'conversation'
68
+ end
69
+
70
+ def validate_channels
71
+ raise ClientError.new("The 'split' parameter must be defined to 'conversation' to also define 'channels'") unless self.split
72
+
73
+ raise ClientError.new("Expected 'split' parameter to be equal to or less than 32") unless self.channels <= 32
74
+ end
75
+
76
+ def validate_end_on_silence
77
+ raise ClientError.new("Expected 'endOnSilence' value to be between 3 and 10") unless self.endOnSilence <= 10 && self.endOnSilence >= 3
78
+ end
79
+
80
+ def validate_end_on_key
81
+ raise ClientError.new("Expected 'endOnKey' value to be a one of the following: a single digit between 1-9, '*' or '#'") unless self.endOnKey.match(/^(\*|[1-9]|\#)$/)
82
+ end
83
+
84
+ def validate_time_out
85
+ raise ClientError.new("Expected 'timeOut' value to be between 3 and 7200 seconds") unless self.timeOut <= 7200 && self.timeOut >= 3
86
+ end
87
+
88
+ def validate_beep_start
89
+ raise ClientError.new("Expected 'beepStart' value to be a Boolean") unless self.beepStart == true || self.beepStart == false
90
+ end
91
+
92
+ def validate_event_url
93
+ uri = URI.parse(self.eventUrl)
94
+
95
+ raise ClientError.new("Invalid 'eventUrl' value, must be a valid URL") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
96
+
97
+ self.eventUrl
98
+ end
99
+
100
+ def validate_event_method
101
+ valid_methods = ['GET', 'POST']
102
+
103
+ raise ClientError.new("Invalid 'eventMethod' value. must be either: 'GET' or 'POST'") unless valid_methods.include?(self.eventMethod.upcase)
104
+ end
105
+
106
+ def action
107
+ create_record!(self)
108
+ end
109
+
110
+ def create_record!(builder)
111
+ ncco = [
112
+ {
113
+ action: 'record'
114
+ }
115
+ ]
116
+
117
+ ncco[0].merge!(format: builder.format) if builder.format
118
+ ncco[0].merge!(split: builder.split) if builder.split
119
+ ncco[0].merge!(channels: builder.channels) if builder.channels
120
+ ncco[0].merge!(endOnSilence: builder.endOnSilence) if builder.endOnSilence
121
+ ncco[0].merge!(endOnKey: builder.endOnKey) if builder.endOnKey
122
+ ncco[0].merge!(timeOut: builder.timeOut) if builder.timeOut
123
+ ncco[0].merge!(beepStart: builder.beepStart) if builder.beepStart
124
+ ncco[0].merge!(eventUrl: builder.eventUrl) if builder.eventUrl
125
+ ncco[0].merge!(eventMethod: builder.eventMethod) if builder.eventMethod
126
+
127
+ ncco
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,72 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Voice::Actions::Stream
6
+ attr_accessor :streamUrl, :level, :bargeIn, :loop
7
+
8
+ def initialize(attributes = {})
9
+ @streamUrl = attributes.fetch(:streamUrl)
10
+ @level = attributes.fetch(:level, nil)
11
+ @bargeIn = attributes.fetch(:bargeIn, nil)
12
+ @loop = attributes.fetch(:loop, nil)
13
+
14
+ after_initialize!
15
+ end
16
+
17
+ def after_initialize!
18
+ verify_stream_url
19
+
20
+ if self.level
21
+ verify_level
22
+ end
23
+
24
+ if self.bargeIn
25
+ verify_barge_in
26
+ end
27
+
28
+ if self.loop
29
+ verify_loop
30
+ end
31
+ end
32
+
33
+ def verify_stream_url
34
+ raise ClientError.new("Expected 'streamUrl' parameter to be an Array containing a single string item") unless self.streamUrl.is_a?(Array)
35
+
36
+ uri = URI.parse(self.streamUrl[0])
37
+
38
+ raise ClientError.new("Invalid 'streamUrl' value, must be a valid URL") unless uri.kind_of?(URI::HTTP) || uri.kind_of?(URI::HTTPS)
39
+ end
40
+
41
+ def verify_level
42
+ raise ClientError.new("Expected 'level' value to be a number between -1 and 1") unless self.level.between?(-1, 1)
43
+ end
44
+
45
+ def verify_barge_in
46
+ raise ClientError.new("Expected 'bargeIn' value to be a Boolean") unless self.bargeIn == true || self.bargeIn == false
47
+ end
48
+
49
+ def verify_loop
50
+ raise ClientError.new("Expected 'loop' value to be either 1 or 0") unless self.loop == 1 || self.loop == 0
51
+ end
52
+
53
+ def action
54
+ create_stream!(self)
55
+ end
56
+
57
+ def create_stream!(builder)
58
+ ncco = [
59
+ {
60
+ action: 'stream',
61
+ streamUrl: builder.streamUrl
62
+ }
63
+ ]
64
+
65
+ ncco[0].merge!(level: builder.level) if builder.level
66
+ ncco[0].merge!(bargeIn: builder.bargeIn) if (builder.bargeIn || builder.bargeIn == false)
67
+ ncco[0].merge!(loop: builder.loop) if builder.loop
68
+
69
+ ncco
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,73 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+ module Vonage
4
+ class Voice::Actions::Talk
5
+ attr_accessor :text, :bargeIn, :loop, :level, :language, :style
6
+
7
+ def initialize(attributes= {})
8
+ @text = attributes.fetch(:text)
9
+ @bargeIn = attributes.fetch(:bargeIn, nil)
10
+ @loop = attributes.fetch(:loop, nil)
11
+ @level = attributes.fetch(:level, nil)
12
+ @language = attributes.fetch(:language, nil)
13
+ @style = attributes.fetch(:style, nil)
14
+
15
+ after_initialize!
16
+ end
17
+
18
+ def after_initialize!
19
+ if self.bargeIn
20
+ verify_barge_in
21
+ end
22
+
23
+ if self.loop
24
+ verify_loop
25
+ end
26
+
27
+ if self.level
28
+ verify_level
29
+ end
30
+
31
+ if self.style
32
+ verify_style
33
+ end
34
+ end
35
+
36
+ def verify_barge_in
37
+ raise ClientError.new("Expected 'bargeIn' value to be a Boolean") unless self.bargeIn == true || self.bargeIn == false
38
+ end
39
+
40
+ def verify_loop
41
+ raise ClientError.new("Expected 'loop' value to be either 1 or 0") unless self.loop == 1 || self.loop == 0
42
+ end
43
+
44
+ def verify_level
45
+ raise ClientError.new("Expected 'level' value to be a number between -1 and 1") unless self.level.between?(-1, 1)
46
+ end
47
+
48
+ def verify_style
49
+ raise ClientError.new("Expected 'style' value to be an Integer") unless self.style.is_a?(Integer)
50
+ end
51
+
52
+ def action
53
+ create_talk!(self)
54
+ end
55
+
56
+ def create_talk!(builder)
57
+ ncco = [
58
+ {
59
+ action: 'talk',
60
+ text: builder.text
61
+ }
62
+ ]
63
+
64
+ ncco[0].merge!(bargeIn: builder.bargeIn) if (builder.bargeIn || builder.bargeIn == false)
65
+ ncco[0].merge!(loop: builder.loop) if builder.loop
66
+ ncco[0].merge!(level: builder.level) if builder.level
67
+ ncco[0].merge!(language: builder.language) if builder.language
68
+ ncco[0].merge!(style: builder.style) if builder.style
69
+
70
+ ncco
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,42 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Vonage
5
+ class Voice::Ncco
6
+ ACTIONS = {
7
+ connect: Vonage::Voice::Actions::Connect,
8
+ conversation: Vonage::Voice::Actions::Conversation,
9
+ input: Vonage::Voice::Actions::Input,
10
+ notify: Vonage::Voice::Actions::Notify,
11
+ record: Vonage::Voice::Actions::Record,
12
+ stream: Vonage::Voice::Actions::Stream,
13
+ talk: Vonage::Voice::Actions::Talk
14
+ }
15
+
16
+ ACTIONS.keys.each do |method|
17
+ self.class.send :define_method, method do |attributes|
18
+ ACTIONS[method].new(**attributes).action
19
+ end
20
+ end
21
+
22
+ def self.method_missing(method)
23
+ raise ClientError.new("NCCO action must be one of the valid options. Please refer to https://developer.nexmo.com/voice/voice-api/ncco-reference#ncco-actions for a complete list.")
24
+ end
25
+
26
+ # Create an NCCO
27
+ #
28
+ # @example
29
+ # talk = Vonage::Voice::Ncco.talk(text: 'This is sample text')
30
+ # input = Vonage::Voice::Ncco.input(type: ['dtmf'])
31
+ # ncco = Vonage::Voice::Ncco.create(talk, input)
32
+ #
33
+ # @option actions [Vonage::Voice::Ncco]
34
+ #
35
+ # @return [Array]
36
+ #
37
+ # @see https://developer.nexmo.com/voice/voice-api/ncco-reference
38
+ def self.create(*actions)
39
+ actions.flatten!
40
+ end
41
+ end
42
+ end
data/vonage.gemspec CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.add_dependency('zeitwerk', '~> 2', '>= 2.2')
17
17
  s.add_dependency('sorbet-runtime', '~> 0.5')
18
18
  s.add_runtime_dependency('rexml')
19
+ s.add_runtime_dependency('phonelib')
19
20
  s.require_path = 'lib'
20
21
  s.metadata = {
21
22
  'homepage' => 'https://github.com/Vonage/vonage-ruby-sdk',
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vonage
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.3.0
4
+ version: 7.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vonage
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-16 00:00:00.000000000 Z
11
+ date: 2021-03-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nexmo-jwt
@@ -72,6 +72,20 @@ dependencies:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
74
  version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: phonelib
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
75
89
  description: Vonage Server SDK for Ruby
76
90
  email:
77
91
  - devrel@vonage.com
@@ -132,8 +146,16 @@ files:
132
146
  - lib/vonage/verify.rb
133
147
  - lib/vonage/version.rb
134
148
  - lib/vonage/voice.rb
149
+ - lib/vonage/voice/actions/connect.rb
150
+ - lib/vonage/voice/actions/conversation.rb
151
+ - lib/vonage/voice/actions/input.rb
152
+ - lib/vonage/voice/actions/notify.rb
153
+ - lib/vonage/voice/actions/record.rb
154
+ - lib/vonage/voice/actions/stream.rb
155
+ - lib/vonage/voice/actions/talk.rb
135
156
  - lib/vonage/voice/dtmf.rb
136
157
  - lib/vonage/voice/list_response.rb
158
+ - lib/vonage/voice/ncco.rb
137
159
  - lib/vonage/voice/stream.rb
138
160
  - lib/vonage/voice/talk.rb
139
161
  - vonage.gemspec