signalwire 2.1.3 → 2.2.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: b79e0a17481f2f0fcb5cdf7a1b5860f86565e60826808b3b0c4c671f6797d173
4
- data.tar.gz: d798019a441d67cf6d6cdbba6093e13b43353a788465b7333bdf48bf9e0ab969
3
+ metadata.gz: 325c0f2a81d0a8486fdb356dc91c78284228b30440129ee2a60c3332b1e725b5
4
+ data.tar.gz: 1f5f8d9227de1b684f05e88d6b1b53246de7f29479c0e11cef94cd792dc8171c
5
5
  SHA512:
6
- metadata.gz: fcadfef6643451bea578ce6a8fd8355307f53704545c6078e0265ed109c03b7c24b196d25c8703416db25a93ca2aa3158bb49818197294493fa01f43922c054d
7
- data.tar.gz: 4cf8ec00e2eb43921000836f1f48012f82e05506c44070960c9b2736d8ffefeb32d5331c8ad5dabe7f537f56bd6a949ef3ab9a70668a3ab8532789dfd4323075
6
+ metadata.gz: 6362ec6503c1a04505f72b13edfbb29c2bb7ca4002f7e13fd7926099dbac08142254d0a41447c96f2b44a54639fdfb367d12ab710c6a6e628c39143a823e9028
7
+ data.tar.gz: e8dce6ff15120bc77452aa97667a0aa468d923cd744e0e4ec15c74db0100424b321e9d86438710750d1cb4285f04db4aa3ab1f09f5e93352a2ebfa7c59874edd
@@ -5,6 +5,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [2.2.0] - 2019-09-09
9
+ ### Added
10
+ - `detect_answering_machine` method with its `amd` alias
11
+
12
+ ### Changed
13
+ - Flatten parameter structures for better readability
14
+ - Log all exceptions as errors by default.
15
+ - Deprecated `detect_human` and `detect_machine` methods in favor of `detect_answering_machine`
16
+
8
17
  ## [2.1.3] - 2019-08-20
9
18
  ### Fixed
10
19
  - Restore the correct parameter for the REST client space URL
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
4
+ %w[
5
+ bundler/setup
6
+ signalwire
7
+ ].each { |f| require f }
8
+
9
+ # Setup your ENV with:
10
+ # SIGNALWIRE_PROJECT_KEY=YOUR_SIGNALWIRE_ACCOUNT_ID
11
+ # SIGNALWIRE_TOKEN=YOUR_SIGNALWIRE_ACCOUNT_TOKEN
12
+ #
13
+ Signalwire::Logger.logger.level = ::Logger::DEBUG
14
+
15
+ class MyConsumer < Signalwire::Relay::Consumer
16
+ contexts ['incoming']
17
+
18
+ def on_incoming_call(call)
19
+ call.answer
20
+ call.send_digits "1234"
21
+
22
+ call.hangup
23
+ rescue StandardError => e
24
+ logger.error e.inspect
25
+ logger.error e.backtrace
26
+ end
27
+ end
28
+
29
+ MyConsumer.new.run
@@ -13,7 +13,7 @@ class OutboundConsumer < Signalwire::Relay::Consumer
13
13
  def ready
14
14
  dial_result = client.calling.new_call(from: ENV['FROM_NUMBER'], to: ENV['TO_NUMBER']).dial
15
15
  collect_params = { "initial_timeout": 10.0, "digits": { "max": 1, "digit_timeout": 5.0 } }
16
- result = dial_result.call.prompt_tts collect: collect_params, text: 'how many hamburgers would you like to order?')
16
+ result = dial_result.call.prompt_tts collect: collect_params, text: 'how many hamburgers would you like to order?'
17
17
 
18
18
  dial_result.call.play_tts text: "You ordered #{result.result} hamburgers. Thank you!"
19
19
  dial_result.call.hangup
@@ -15,7 +15,7 @@ class OutboundConsumer < Signalwire::Relay::Consumer
15
15
  call = client.calling.new_call(from: ENV['FROM_NUMBER'], to: ENV['TO_NUMBER'])
16
16
  call.dial
17
17
  call.play_tts text: 'please leave your message after the beep. Press pound when done.'
18
- result = call.record({"audio": { "beep": "true", "terminators": "#"}})
18
+ result = call.record(beep: true, terminators: "#")
19
19
  call.play_tts text: 'you said:'
20
20
  call.play_audio result.url
21
21
  call.hangup
@@ -14,11 +14,8 @@ class OutboundConsumer < Signalwire::Relay::Consumer
14
14
  logger.info 'Dialing out'
15
15
  call = client.calling.new_call(from: ENV['FROM_NUMBER'], to: ENV['TO_NUMBER'])
16
16
  call.dial
17
-
18
- tap_arguments = { type: "audio", params: { direction: "listen" } }
19
- device_arguments = { type: "rtp", params: { addr: "127.0.0.1", port: 1234 } }
20
17
 
21
- call.tap_media(tap: tap_arguments, device: device_arguments)
18
+ call.tap_media(audio_direction: "both", target_addr: "127.0.0.1", target_port: 1234)
22
19
 
23
20
  call.hangup
24
21
  rescue StandardError => e
@@ -9,7 +9,10 @@ module Signalwire::Blade
9
9
  alias once register_tmp_handler
10
10
 
11
11
  def broadcast(event_type, event)
12
- trigger_handler event_type, event, broadcast: true
12
+ trigger_handler event_type, event, broadcast: true, exception_callback: Proc.new { |e|
13
+ logger.error "#{e.class}: #{e.message}"
14
+ logger.error e.backtrace.join("\n")
15
+ }
13
16
  end
14
17
  end
15
18
  end
@@ -24,6 +24,7 @@ require 'signalwire/relay/calling/action/prompt_action'
24
24
  require 'signalwire/relay/calling/action/record_action'
25
25
  require 'signalwire/relay/calling/action/fax_action'
26
26
  require 'signalwire/relay/calling/action/tap_action'
27
+ require 'signalwire/relay/calling/action/send_digits_action'
27
28
 
28
29
  require 'signalwire/relay/calling/result'
29
30
  require 'signalwire/relay/calling/result/answer_result'
@@ -36,6 +37,7 @@ require 'signalwire/relay/calling/result/prompt_result'
36
37
  require 'signalwire/relay/calling/result/record_result'
37
38
  require 'signalwire/relay/calling/result/fax_result'
38
39
  require 'signalwire/relay/calling/result/tap_result'
40
+ require 'signalwire/relay/calling/result/send_digits_result'
39
41
 
40
42
  require 'signalwire/relay/calling/component'
41
43
  require 'signalwire/relay/calling/control_component'
@@ -52,6 +54,7 @@ require 'signalwire/relay/calling/component/base_fax'
52
54
  require 'signalwire/relay/calling/component/fax_send'
53
55
  require 'signalwire/relay/calling/component/fax_receive'
54
56
  require 'signalwire/relay/calling/component/tap'
57
+ require 'signalwire/relay/calling/component/send_digits'
55
58
 
56
59
  require 'signalwire/relay/messaging'
57
60
  require 'signalwire/relay/messaging/message'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
5
+ module Signalwire::Relay::Calling
6
+ class SendDigitsAction < Action
7
+ def result
8
+ SendDigitsResult.new(@component)
9
+ end
10
+ end
11
+ end
@@ -11,6 +11,13 @@ module Signalwire::Relay::Calling
11
11
  include Signalwire::Relay::Calling::CallDetectMethods
12
12
  extend Forwardable
13
13
 
14
+ extend Gem::Deprecate
15
+ DETECT_DEPRECATE_MONTH = 10
16
+ deprecate :detect_human, :amd, 2019, DETECT_DEPRECATE_MONTH
17
+ deprecate :detect_human!, :amd!, 2019, DETECT_DEPRECATE_MONTH
18
+ deprecate :detect_machine, :amd, 2019, DETECT_DEPRECATE_MONTH
19
+ deprecate :detect_machine!, :amd!, 2019, DETECT_DEPRECATE_MONTH
20
+
14
21
  attr_reader :device, :type, :node_id, :context, :from, :to,
15
22
  :timeout, :tag, :client, :state, :previous_state, :components,
16
23
  :busy, :failed, :peer_call
@@ -117,7 +124,12 @@ module Signalwire::Relay::Calling
117
124
  PlayAction.new(component: play_component)
118
125
  end
119
126
 
120
- def prompt(collect_p = nil, play_p = nil, collect: nil, play: nil)
127
+ def prompt(collect_p = nil, play_p = nil, **args)
128
+
129
+ collect = args.delete(:collect)
130
+ play = args.delete(:play)
131
+
132
+ collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
121
133
  set_parameters(binding, %i{collect play}, %i{collect play})
122
134
 
123
135
  component = Prompt.new(call: self, collect: collect, play: play)
@@ -127,7 +139,8 @@ module Signalwire::Relay::Calling
127
139
  PromptResult.new(component: component)
128
140
  end
129
141
 
130
- def prompt!(collect_p = nil, play_p = nil, collect: nil, play: nil)
142
+ def prompt!(collect_p = nil, play_p = nil, **args)
143
+ collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
131
144
  set_parameters(binding, %i{collect play}, %i{collect play})
132
145
 
133
146
  component = Prompt,new(call: self, collect: collect, play: play)
@@ -147,13 +160,47 @@ module Signalwire::Relay::Calling
147
160
  ConnectAction.new(component: component)
148
161
  end
149
162
 
150
- def record(record_object)
163
+ def record(audio: nil, type: 'audio', beep: false, format: 'mp3', stereo: false, direction: 'speak', initial_timeout: 5, end_silence_timeout: 1, terminators: "#*")
164
+ if audio.nil?
165
+ record_object = {
166
+ "#{type}":
167
+ {
168
+ beep: beep,
169
+ format: format,
170
+ stereo: stereo,
171
+ direction: direction,
172
+ initial_timeout: initial_timeout,
173
+ end_silence_timeout: end_silence_timeout,
174
+ terminators: terminators
175
+ }
176
+ }
177
+ else
178
+ record_object = { "#{type}": audio }
179
+ end
180
+
151
181
  component = Record.new(call: self, record: record_object)
152
182
  component.wait_for(Relay::CallRecordState::NO_INPUT, Relay::CallRecordState::FINISHED)
153
183
  RecordResult.new(component: component)
154
184
  end
155
185
 
156
- def record!(record_object)
186
+ def record!(audio: nil, type: 'audio', beep: false, format: 'mp3', stereo: false, direction: 'speak', initial_timeout: 5, end_silence_timeout: 1, terminators: "#*")
187
+ if audio.nil?
188
+ record_object = {
189
+ "#{type}":
190
+ {
191
+ beep: beep,
192
+ format: format,
193
+ stereo: stereo,
194
+ direction: direction,
195
+ initial_timeout: initial_timeout,
196
+ end_silence_timeout: end_silence_timeout,
197
+ terminators: terminators
198
+ }
199
+ }
200
+ else
201
+ record_object = { "#{type}": audio }
202
+ end
203
+
157
204
  component = Record.new(call: self, record: record_object)
158
205
  component.execute
159
206
  RecordAction.new(component: component)
@@ -183,30 +230,54 @@ module Signalwire::Relay::Calling
183
230
  FaxAction.new(component: component)
184
231
  end
185
232
 
186
- def fax_send(document: , identity: nil, header: nil)
233
+ def fax_send(document:, identity: nil, header: nil)
187
234
  component = Signalwire::Relay::Calling::FaxSend.new(call: self, document: document, identity: identity, header: header)
188
235
  component.wait_for(Relay::CallFaxState::ERROR, Relay::CallFaxState::FINISHED)
189
236
  FaxResult.new(component: component)
190
237
  end
191
238
 
192
- def fax_send!(document: , identity: nil, header: nil)
239
+ def fax_send!(document:, identity: nil, header: nil)
193
240
  component = Signalwire::Relay::Calling::FaxSend.new(call: self, document: document, identity: identity, header: header)
194
241
  component.execute
195
242
  FaxAction.new(component: component)
196
243
  end
197
244
 
198
- def tap_media(tap:, device:)
245
+ def tap_media(**args)
246
+ tap = args.delete(:tap)
247
+ device = args.delete(:device)
248
+
249
+ tap = compile_tap_arguments(args) if tap.nil?
250
+ device = compile_tap_device_arguments(args) if device.nil?
251
+
199
252
  component = Signalwire::Relay::Calling::Tap.new(call: self, tap: tap, device: device)
200
253
  component.wait_for(Relay::CallTapState::FINISHED)
201
254
  TapResult.new(component: component)
202
255
  end
203
256
 
204
- def tap_media!(tap:, device:)
257
+ def tap_media!(**args)
258
+ tap = args.delete(:tap)
259
+ device = args.delete(:device)
260
+
261
+ tap = compile_tap_arguments(args) if tap.nil?
262
+ device = compile_tap_device_arguments(args) if device.nil?
263
+
205
264
  component = Signalwire::Relay::Calling::Tap.new(call: self, tap: tap, device: device)
206
265
  component.execute
207
266
  TapAction.new(component: component)
208
267
  end
209
268
 
269
+ def send_digits(digits)
270
+ component = Signalwire::Relay::Calling::SendDigits.new(call: self, digits: digits)
271
+ component.wait_for(Relay::CallSendDigitsState::FINISHED)
272
+ SendDigitsResult.new(component: component)
273
+ end
274
+
275
+ def send_digits!(digits)
276
+ component = Signalwire::Relay::Calling::SendDigits.new(call: self, digits: digits)
277
+ component.execute
278
+ SendDigitsAction.new(component: component)
279
+ end
280
+
210
281
  def wait_for(*events)
211
282
  events = [Relay::CallState::ENDED] if events.empty?
212
283
 
@@ -226,7 +297,7 @@ module Signalwire::Relay::Calling
226
297
 
227
298
  def terminate_components(params = {})
228
299
  @components.each do |comp|
229
- comp.terminate(params) unless component.completed
300
+ comp.terminate(params) unless comp.completed
230
301
  end
231
302
  end
232
303
 
@@ -257,8 +328,57 @@ module Signalwire::Relay::Calling
257
328
  passed_binding.local_variable_set(x, passed_binding.local_variable_get(x) || passed_binding.local_variable_get("#{x}_p"))
258
329
  end
259
330
  mandatory_keys.each do |x|
260
- raise ArgumentError if passed_binding.local_variable_get(x).nil?
331
+ raise ArgumentError, "The #{x} argument must be provided" if passed_binding.local_variable_get(x).nil?
261
332
  end
262
333
  end
334
+
335
+ def compile_tap_arguments(args)
336
+ tap = { params: {} }
337
+ tap[:type] = args[:media_type] || 'audio'
338
+ tap[:params][:direction] = args[:audio_direction] if args[:audio_direction]
339
+ tap[:params][:rate] = args[:rate] if args[:rate]
340
+ tap[:params][:codec] = args[:codec] if args[:codec]
341
+ tap
342
+ end
343
+
344
+ def compile_tap_device_arguments(args)
345
+ device = { params: {} }
346
+ device[:type] = args[:target_type] || 'rtp'
347
+ device[:params][:addr] = args[:target_addr] if args[:target_addr]
348
+ device[:params][:port] = args[:target_port] if args[:target_port]
349
+ device[:params][:ptime] = args[:target_ptime] if args[:target_ptime]
350
+ device[:params][:uri] = args[:target_uri] if args[:target_uri]
351
+ device
352
+ end
353
+
354
+ def compile_collect_arguments(args)
355
+ generic_fields = %i{initial_timeout partial_results}
356
+ digit_fields = %i{digits_max digits_terminators digits_timeout}
357
+ speech_fields = %i{speech_timeout speech_language speech_hints end_silence_timeout}
358
+ has_digits = (digit_fields & args.keys).any?
359
+ has_speech = (speech_fields & args.keys).any?
360
+
361
+ collect = {}
362
+ generic_fields.each { |gf| collect[gf] = args[gf] if args[gf] }
363
+
364
+ if has_digits
365
+ digits_obj = {}
366
+ digits_obj[:max] = args[:digits_max] if args[:digits_max]
367
+ digits_obj[:terminators] = args[:digits_terminators] if args[:digits_terminators]
368
+ digits_obj[:digit_timeout] = args[:digits_timeout] if args[:digits_timeout]
369
+ collect[:digits] = digits_obj
370
+ end
371
+
372
+ if has_speech
373
+ speech_obj = {}
374
+ speech_obj[:timeout] = args[:speech_timeout] if args[:speech_timeout]
375
+ speech_obj[:language] = args[:speech_language] if args[:speech_language]
376
+ speech_obj[:hints] = args[:speech_hints] if args[:speech_hints]
377
+ speech_obj[:end_silence_timeout] = args[:end_silence_timeout] if args[:end_silence_timeout]
378
+ collect[:digits] = digits_obj
379
+ end
380
+
381
+ collect
382
+ end
263
383
  end
264
384
  end
@@ -27,39 +27,64 @@ module Signalwire::Relay::Calling
27
27
  play! tts_payload(text, language, gender)
28
28
  end
29
29
 
30
- def prompt_audio(collect_p = nil, url_p = nil, collect: nil, url: nil)
30
+ def prompt_audio(collect_p = nil, url_p = nil, **args)
31
+ collect = args.delete(:collect)
32
+ url = args.delete(:url)
33
+ collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
31
34
  set_parameters(binding, %i{collect url}, %i{collect url})
32
35
 
33
- prompt(collect, audio_payload(url))
36
+ prompt(collect: collect, play: audio_payload(url))
34
37
  end
35
38
 
36
- def prompt_audio!(collect_p = nil, url_p = nil, collect: nil, url: nil)
39
+ def prompt_audio!(collect_p = nil, url_p = nil, **args)
40
+ collect = args.delete(:collect)
41
+ url = args.delete(:url)
42
+ collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
37
43
  set_parameters(binding, %i{collect url}, %i{collect url})
38
44
 
39
- prompt!(collect, audio_payload(url))
45
+ prompt!(collect: collect, play: audio_payload(url))
40
46
  end
41
47
 
42
- def prompt_silence(collect_p = nil, duration_p = nil, collect: nil, duration: nil)
48
+ def prompt_silence(collect_p = nil, duration_p = nil, **args)
49
+ collect = args.delete(:collect)
50
+ duration = args.delete(:duration)
51
+ collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
43
52
  set_parameters(binding, %i{collect duration}, %i{collect duration})
44
53
 
45
- prompt(collect, silence_payload(duration))
54
+ prompt(collect: collect, play: silence_payload(duration))
46
55
  end
47
56
 
48
- def prompt_silence!(collect_p = nil, duration_p = nil, collect: nil, duration: nil)
57
+ def prompt_silence!(collect_p = nil, duration_p = nil, **args)
58
+ collect = args.delete(:collect)
59
+ duration = args.delete(:duration)
60
+ collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
49
61
  set_parameters(binding, %i{collect duration}, %i{collect duration})
50
62
 
51
- prompt!(collect, silence_payload(duration))
63
+ prompt!(collect: collect, play: silence_payload(duration))
52
64
  end
53
65
 
54
- def prompt_tts(collect_p = nil, text_p = nil, language_p=Relay::DEFAULT_LANGUAGE, gender_p=Relay::DEFAULT_GENDER, collect: nil, text: nil, language: Relay::DEFAULT_LANGUAGE, gender: Relay::DEFAULT_GENDER)
66
+ def prompt_tts(collect_p = nil, text_p = nil, language_p=Relay::DEFAULT_LANGUAGE, gender_p=Relay::DEFAULT_GENDER, **args)
67
+ collect = args.delete(:collect)
68
+ text = args.delete(:text)
69
+ language = args.delete(:language) || Relay::DEFAULT_LANGUAGE
70
+ gender = args.delete(:gender) || Relay::DEFAULT_GENDER
71
+ collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
72
+
55
73
  set_parameters(binding, %i{collect text language gender}, %i{collect text})
56
- prompt(collect, tts_payload(text, language, gender))
74
+
75
+ prompt(collect: collect, play: tts_payload(text, language, gender))
57
76
  end
58
77
 
59
- def prompt_tts!(collect_p = nil, text_p = nil, language_p=Relay::DEFAULT_LANGUAGE, gender_p=Relay::DEFAULT_GENDER, collect: nil, text: nil, language: Relay::DEFAULT_LANGUAGE, gender: Relay::DEFAULT_GENDER)
78
+ def prompt_tts!(collect_p = nil, text_p = nil, language_p=Relay::DEFAULT_LANGUAGE, gender_p=Relay::DEFAULT_GENDER, **args)
79
+ collect = args.delete(:collect)
80
+ text = args.delete(:text)
81
+ language = args.delete(:language) || Relay::DEFAULT_LANGUAGE
82
+ gender = args.delete(:gender) || Relay::DEFAULT_GENDER
83
+ collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
84
+
60
85
  set_parameters(binding, %i{collect text language gender}, %i{collect text})
61
86
 
62
- prompt!(collect, tts_payload(text, language, gender))
87
+ prompt!(collect: collect, play: tts_payload(text, language, gender))
63
88
  end
64
89
 
65
90
  def wait_for_ringing
@@ -1,110 +1,120 @@
1
1
  module Signalwire::Relay::Calling
2
2
  module CallDetectMethods
3
- def detect(detect:, timeout: Relay::DEFAULT_CALL_TIMEOUT)
4
- component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, timeout: timeout)
5
- component.wait_for(Relay::CallDetectState::FINISHED, Relay::CallDetectState::ERROR)
3
+
4
+ def detect(type:, **args)
5
+ component = build_detect_component(type, args)
6
+ component.wait_for(Relay::CallDetectState::MACHINE, Relay::CallDetectState::HUMAN,
7
+ Relay::CallDetectState::UNKNOWN, Relay::CallDetectState::CED, Relay::CallDetectState::CNG)
6
8
  DetectResult.new(component: component)
7
9
  end
8
10
 
9
- def detect!(detect:, timeout: Relay::DEFAULT_CALL_TIMEOUT)
10
- component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, timeout: timeout)
11
+ def detect!(type:, **args)
12
+ component = build_detect_component(type, args)
11
13
  component.execute
12
14
  DetectAction.new(component: component)
13
15
  end
14
16
 
17
+ def detect_answering_machine(**args)
18
+ detect(type: :machine, **args)
19
+ end
20
+
21
+ def detect_answering_machine!(**args)
22
+ detect!(type: :machine, **args)
23
+ end
24
+
25
+ def amd(**args)
26
+ detect(type: :machine, **args)
27
+ end
28
+
29
+ def amd!(**args)
30
+ detect!(type: :machine, **args)
31
+ end
32
+
33
+ # deprecated since version 2.2. Will be deleted in version 3.0.
15
34
  def detect_human(params: {}, timeout: Relay::DEFAULT_CALL_TIMEOUT)
16
35
  detect = {
17
36
  type: Relay::CallDetectType::MACHINE,
18
37
  params: params
19
38
  }
20
39
  component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, timeout: timeout)
21
- component.wait_for(Relay::CallDetectState::FINISHED, Relay::CallDetectState::ERROR, Relay::CallDetectState::HUMAN)
40
+ component.wait_for(Relay::CallDetectState::MACHINE, Relay::CallDetectState::UNKNOWN, Relay::CallDetectState::HUMAN)
22
41
  DetectResult.new(component: component)
23
42
  end
24
43
 
44
+ # deprecated since version 2.2. Will be deleted in version 3.0.
25
45
  def detect_human!(params: {}, timeout: Relay::DEFAULT_CALL_TIMEOUT)
26
46
  detect = {
27
47
  type: Relay::CallDetectType::MACHINE,
28
48
  params: params
29
49
  }
30
50
  component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, timeout: timeout)
31
- component.setup_waiting_events([Relay::CallDetectState::FINISHED, Relay::CallDetectState::ERROR, Relay::CallDetectState::HUMAN])
51
+ component.setup_waiting_events([Relay::CallDetectState::MACHINE, Relay::CallDetectState::UNKNOWN, Relay::CallDetectState::HUMAN])
32
52
  component.execute
33
53
  DetectAction.new(component: component)
34
54
  end
35
55
 
56
+ # deprecated since version 2.2. Will be deleted in version 3.0.
36
57
  def detect_machine(params: {}, timeout: Relay::DEFAULT_CALL_TIMEOUT)
37
58
  detect = {
38
59
  type: Relay::CallDetectType::MACHINE,
39
60
  params: params
40
61
  }
41
62
  component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, timeout: timeout)
42
- component.wait_for(Relay::CallDetectState::FINISHED, Relay::CallDetectState::ERROR,
43
- Relay::CallDetectState::MACHINE, Relay::CallDetectState::READY,
44
- Relay::CallDetectState::NOT_READY)
63
+ component.wait_for(Relay::CallDetectState::MACHINE, Relay::CallDetectState::UNKNOWN, Relay::CallDetectState::HUMAN)
45
64
  DetectResult.new(component: component)
46
65
  end
47
66
 
67
+ # deprecated since version 2.2. Will be deleted in version 3.0.
48
68
  def detect_machine!(params: {}, timeout: Relay::DEFAULT_CALL_TIMEOUT)
49
69
  detect = {
50
70
  type: Relay::CallDetectType::MACHINE,
51
71
  params: params
52
72
  }
53
73
  component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, timeout: timeout)
54
- component.setup_waiting_events([Relay::CallDetectState::FINISHED, Relay::CallDetectState::ERROR,
55
- Relay::CallDetectState::MACHINE, Relay::CallDetectState::READY,
56
- Relay::CallDetectState::NOT_READY])
74
+ component.setup_waiting_events([Relay::CallDetectState::MACHINE, Relay::CallDetectState::UNKNOWN, Relay::CallDetectState::HUMAN])
57
75
  component.execute
58
76
  DetectAction.new(component: component)
59
77
  end
60
78
 
61
79
  def detect_fax(tone: nil, timeout: Relay::DEFAULT_CALL_TIMEOUT)
62
- detect, events = prepare_fax_arguments(tone)
63
-
64
- component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, timeout: timeout)
65
- component.wait_for(*events)
66
- DetectResult.new(component: component)
80
+ detect(type: :fax, tone: tone, timeout: timeout)
67
81
  end
68
82
 
69
83
  def detect_fax!(tone: nil, timeout: Relay::DEFAULT_CALL_TIMEOUT)
70
- detect, events = prepare_fax_arguments(tone)
71
- component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, timeout: timeout)
72
- component.setup_waiting_events(events)
73
- component.execute
74
- DetectAction.new(component: component)
84
+ detect!(type: :fax, tone: tone, timeout: timeout)
75
85
  end
76
86
 
77
87
  def detect_digit(digits: nil, timeout: Relay::DEFAULT_CALL_TIMEOUT)
78
- detect = {
79
- type: Relay::CallDetectType::DIGIT,
80
- params: {}
81
- }
82
- detect[:params][:digits] = digits if digits
83
- detect(detect: detect, timeout: timeout)
88
+ detect(type: :digit, digits: digits, timeout: timeout)
84
89
  end
85
90
 
86
91
  def detect_digit!(digits: nil, timeout: Relay::DEFAULT_CALL_TIMEOUT)
87
- detect = {
88
- type: Relay::CallDetectType::DIGIT,
89
- params: {}
90
- }
91
- detect[:params][:digits] = digits if digits
92
- detect!(detect: detect, timeout: timeout)
92
+ detect!(type: :digit, digits: digits, timeout: timeout)
93
93
  end
94
94
 
95
- def prepare_fax_arguments(tone)
96
- fax_events = [Relay::CallDetectState::CED, Relay::CallDetectState::CNG]
97
- events = [Relay::CallDetectState::FINISHED, Relay::CallDetectState::ERROR]
98
- detect = { type: Relay::CallDetectType::FAX, params: {} }
95
+ private
96
+
97
+ def build_detect_component(type, args)
98
+ detect = args.delete(:detect)
99
+ timeout = args.delete(:timeout) || Relay::DEFAULT_CALL_TIMEOUT
100
+ wait_for_beep = args.delete(:wait_for_beep)
99
101
 
100
- if fax_events.include?(tone)
101
- detect[:params] = { tone: tone }
102
- events << tone
103
- else
104
- events = events + fax_events
102
+ if detect.nil?
103
+ detect = { params: {} }
104
+ detect[:type] = type.to_sym || :machine
105
+
106
+ if detect[:type] == :machine
107
+ %i{initial_timeout end_silence_timeout machine_voice_threshold machine_words_threshold}.each do |key|
108
+ detect[:params][key] = args[key] if args[key]
109
+ end
110
+ elsif detect[:type] == :fax
111
+ detect[:params][:tone] = args[:tone]
112
+ elsif detect[:type] == :digit
113
+ detect[:params][:digits] = args[:digits]
114
+ end
105
115
  end
106
116
 
107
- [detect, events]
117
+ component = Signalwire::Relay::Calling::Detect.new(call: self, detect: detect, wait_for_beep: wait_for_beep, timeout: timeout)
108
118
  end
109
119
  end
110
120
  end
@@ -2,13 +2,14 @@
2
2
 
3
3
  module Signalwire::Relay::Calling
4
4
  class Component
5
+ include Signalwire::Logger
5
6
  attr_reader :completed, :state, :successful, :event, :execute_result, :call
6
7
 
7
8
  def initialize(call:)
8
9
  @call = call
9
10
  @completed = false
10
11
  @successful = false
11
-
12
+ @events_waiting = []
12
13
  @call.register_component(self)
13
14
  end
14
15
 
@@ -69,7 +70,7 @@ module Signalwire::Relay::Calling
69
70
  @successful = false
70
71
  @state = 'failed'
71
72
  @event = event if event
72
- blocker&.reject
73
+ blocker&.reject false
73
74
  end
74
75
 
75
76
  def setup_waiting_events(events)
@@ -87,6 +88,10 @@ module Signalwire::Relay::Calling
87
88
  blocker.wait
88
89
  end
89
90
 
91
+ def has_blocker?
92
+ !@blocker.nil?
93
+ end
94
+
90
95
  # This is the most important method to implement in a subclass
91
96
  #
92
97
  def notification_handler(_event)
@@ -103,7 +108,7 @@ module Signalwire::Relay::Calling
103
108
  end
104
109
 
105
110
  def unblock(value)
106
- blocker&.resolve value
111
+ blocker&.resolve value if has_blocker? && blocker.pending?
107
112
  end
108
113
 
109
114
  attr_reader :blocker
@@ -4,11 +4,16 @@ module Signalwire::Relay::Calling
4
4
  class Detect < ControlComponent
5
5
  attr_reader :result, :type
6
6
 
7
- def initialize(call:, detect:, timeout: 30)
7
+ FINISHED_EVENTS = [Relay::CallDetectState::FINISHED, Relay::CallDetectState::ERROR]
8
+ MACHINE_EVENTS = [Relay::CallDetectState::READY, Relay::CallDetectState::NOT_READY]
9
+
10
+ def initialize(call:, detect:, wait_for_beep: false, timeout: 30)
8
11
  super(call: call)
9
12
  @detect = detect
10
13
  @timeout = timeout
14
+ @wait_for_beep = wait_for_beep
11
15
  @received_events = Concurrent::Array.new
16
+ @waiting_for_ready = false
12
17
  end
13
18
 
14
19
  def method
@@ -30,33 +35,56 @@ module Signalwire::Relay::Calling
30
35
  end
31
36
 
32
37
  def notification_handler(event)
33
- result = event.call_params[:detect]
34
- @type = result[:type]
35
- params = result[:params]
38
+ detect_result = event.call_params[:detect]
39
+ @type = detect_result[:type]
40
+ params = detect_result[:params]
36
41
  res_event = params[:event]
37
42
  @state = res_event
38
43
 
39
- if @state != Relay::CallDetectState::FINISHED && @state != Relay::CallDetectState::ERROR
40
- @received_events << res_event
41
- end
44
+ return complete(event) if FINISHED_EVENTS.include?(@state) || @type == Relay::CallDetectType::DIGIT
42
45
 
43
- @completed = @events_waiting.include?(@state)
46
+ if has_blocker?
47
+ @received_events << @state
48
+ return
49
+ end
44
50
 
45
- if @completed
46
- @successful = @state != Relay::CallDetectState::ERROR
47
- @result = @received_events.join(' ')
48
- @event = event
49
- unblock(event)
51
+ if @waiting_for_ready
52
+ return (@state == Relay::CallDetectState::READY ? complete(event) : nil)
50
53
  end
51
54
 
55
+ if (@wait_for_beep && @state == Relay::CallDetectState::MACHINE)
56
+ @waiting_for_ready = true
57
+ return
58
+ end
59
+
60
+ check_for_waiting_events
52
61
  broadcast_event(event)
53
- # This component has complex logic so we handle it separately
54
- # check_for_waiting_events
55
62
  end
56
63
 
57
64
  def broadcast_event(event)
58
65
  @call.broadcast "detect_#{@state}".to_sym, event
59
66
  @call.broadcast :detect_state_change, event
60
67
  end
68
+
69
+ private
70
+
71
+ def complete(event)
72
+ @completed = true
73
+ @event = event
74
+
75
+ if has_blocker?
76
+ @successful = !FINISHED_EVENTS.include?(@state)
77
+
78
+ if MACHINE_EVENTS.include?(@state)
79
+ @result = Relay::CallDetectState::MACHINE
80
+ else
81
+ @result = @state
82
+ end
83
+ unblock(event)
84
+ else
85
+ @result = @received_events.join(',')
86
+ @successful = @state != Relay::CallDetectState::ERROR
87
+ end
88
+ end
61
89
  end
62
90
  end
@@ -0,0 +1,45 @@
1
+ module Signalwire::Relay::Calling
2
+ class SendDigits < ControlComponent
3
+ def initialize(call:, digits:)
4
+ super(call: call)
5
+ @digits = digits
6
+ end
7
+
8
+ def method
9
+ Relay::ComponentMethod::SEND_DIGITS
10
+ end
11
+
12
+ def event_type
13
+ Relay::CallNotification::SEND_DIGITS
14
+ end
15
+
16
+ def inner_params
17
+ {
18
+ node_id: @call.node_id,
19
+ call_id: @call.id,
20
+ control_id: control_id,
21
+ digits: @digits
22
+ }
23
+ end
24
+
25
+ def notification_handler(event)
26
+ @state = event.call_params[:state]
27
+
28
+ @completed = @state == Relay::CallSendDigitsState::FINISHED
29
+ @successful = @completed
30
+ @event = event
31
+
32
+ broadcast_event(event)
33
+ check_for_waiting_events
34
+ end
35
+
36
+ def broadcast_event(event)
37
+ @call.broadcast "send_digits_#{@state}".to_sym, event
38
+ @call.broadcast :send_digits_change, event
39
+ end
40
+
41
+ def stop
42
+ logger.warn "SendDigits does not implement a stop action"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Signalwire::Relay::Calling
4
+ class SendDigitsResult < Result
5
+ end
6
+ end
@@ -33,6 +33,7 @@ module Signalwire::Relay
33
33
  NO_ANSWER = 'noAnswer'
34
34
  DECLINE = 'decline'
35
35
  ERROR = 'error'
36
+ FAILED = 'failed'
36
37
  end
37
38
 
38
39
  module DisconnectSource
@@ -65,6 +66,7 @@ module Signalwire::Relay
65
66
  FAX = 'calling.call.fax'
66
67
  TAP = 'calling.call.tap'
67
68
  DETECT = 'calling.call.detect'
69
+ SEND_DIGITS = 'calling.call.send_digits'
68
70
  end
69
71
 
70
72
  CALL_EVENT_STATE_FIELDS = {
@@ -117,6 +119,7 @@ module Signalwire::Relay
117
119
  RECEIVE_FAX = 'calling.receive_fax'
118
120
  TAP = 'calling.tap'
119
121
  DETECT = 'calling.detect'
122
+ SEND_DIGITS = 'calling.send_digits'
120
123
  end
121
124
 
122
125
  module CommonState
@@ -151,6 +154,10 @@ module Signalwire::Relay
151
154
  MACHINE = "machine"
152
155
  DIGIT = "digit"
153
156
  end
157
+
158
+ module CallSendDigitsState
159
+ FINISHED = 'finished'
160
+ end
154
161
  end
155
162
 
156
163
  Relay = Signalwire::Relay
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Signalwire
4
- VERSION = '2.1.3'
4
+ VERSION = '2.2.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: signalwire
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.3
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SignalWire Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-20 00:00:00.000000000 Z
11
+ date: 2019-09-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -259,6 +259,7 @@ files:
259
259
  - examples/relay/fax/fax_send.rb
260
260
  - examples/relay/inbound_consumer.rb
261
261
  - examples/relay/inbound_dial.rb
262
+ - examples/relay/inbound_send_digits.rb
262
263
  - examples/relay/messaging/messaging_receive.rb
263
264
  - examples/relay/messaging/messaging_send.rb
264
265
  - examples/relay/outbound_collect.rb
@@ -288,6 +289,7 @@ files:
288
289
  - lib/signalwire/relay/calling/action/play_action.rb
289
290
  - lib/signalwire/relay/calling/action/prompt_action.rb
290
291
  - lib/signalwire/relay/calling/action/record_action.rb
292
+ - lib/signalwire/relay/calling/action/send_digits_action.rb
291
293
  - lib/signalwire/relay/calling/action/tap_action.rb
292
294
  - lib/signalwire/relay/calling/call.rb
293
295
  - lib/signalwire/relay/calling/call_convenience_methods.rb
@@ -305,6 +307,7 @@ files:
305
307
  - lib/signalwire/relay/calling/component/play.rb
306
308
  - lib/signalwire/relay/calling/component/prompt.rb
307
309
  - lib/signalwire/relay/calling/component/record.rb
310
+ - lib/signalwire/relay/calling/component/send_digits.rb
308
311
  - lib/signalwire/relay/calling/component/tap.rb
309
312
  - lib/signalwire/relay/calling/control_component.rb
310
313
  - lib/signalwire/relay/calling/result.rb
@@ -317,6 +320,7 @@ files:
317
320
  - lib/signalwire/relay/calling/result/play_result.rb
318
321
  - lib/signalwire/relay/calling/result/prompt_result.rb
319
322
  - lib/signalwire/relay/calling/result/record_result.rb
323
+ - lib/signalwire/relay/calling/result/send_digits_result.rb
320
324
  - lib/signalwire/relay/calling/result/tap_result.rb
321
325
  - lib/signalwire/relay/client.rb
322
326
  - lib/signalwire/relay/constants.rb