signalwire 2.2.0 → 2.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.drone.yml +2 -1
- data/CHANGELOG.md +31 -1
- data/examples/relay/outbound_amd.rb +30 -0
- data/examples/relay/outbound_collect_speech.rb +26 -0
- data/examples/relay/outbound_record.rb +1 -0
- data/lib/signalwire.rb +1 -0
- data/lib/signalwire/blade.rb +1 -0
- data/lib/signalwire/blade/connection.rb +38 -16
- data/lib/signalwire/blade/message/connect.rb +2 -1
- data/lib/signalwire/blade/message/ping.rb +16 -0
- data/lib/signalwire/relay.rb +7 -0
- data/lib/signalwire/relay/calling/action/connect_action.rb +1 -1
- data/lib/signalwire/relay/calling/action/detect_action.rb +1 -1
- data/lib/signalwire/relay/calling/action/fax_action.rb +1 -1
- data/lib/signalwire/relay/calling/action/play_action.rb +13 -1
- data/lib/signalwire/relay/calling/action/prompt_action.rb +5 -1
- data/lib/signalwire/relay/calling/action/record_action.rb +5 -1
- data/lib/signalwire/relay/calling/action/send_digits_action.rb +1 -1
- data/lib/signalwire/relay/calling/action/tap_action.rb +1 -1
- data/lib/signalwire/relay/calling/call.rb +32 -16
- data/lib/signalwire/relay/calling/call_convenience_methods.rb +58 -23
- data/lib/signalwire/relay/calling/call_detect_methods.rb +1 -2
- data/lib/signalwire/relay/calling/component.rb +7 -2
- data/lib/signalwire/relay/calling/component/connect.rb +6 -2
- data/lib/signalwire/relay/calling/component/detect.rb +33 -31
- data/lib/signalwire/relay/calling/component/play.rb +18 -2
- data/lib/signalwire/relay/calling/component/prompt.rb +9 -2
- data/lib/signalwire/relay/calling/component/record.rb +4 -0
- data/lib/signalwire/relay/calling/control_component.rb +9 -5
- data/lib/signalwire/relay/calling/result/base_action_result.rb +10 -0
- data/lib/signalwire/relay/calling/result/play_pause_result.rb +6 -0
- data/lib/signalwire/relay/calling/result/play_resume_result.rb +6 -0
- data/lib/signalwire/relay/calling/result/play_volume_result.rb +6 -0
- data/lib/signalwire/relay/calling/result/prompt_volume_result.rb +6 -0
- data/lib/signalwire/relay/calling/result/stop_result.rb +6 -0
- data/lib/signalwire/relay/constants.rb +1 -1
- data/lib/signalwire/version.rb +1 -1
- data/signalwire.gemspec +13 -13
- metadata +38 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d31f18c614ab0890b0c3000c45f607b636c8c3a3b7ad51d1e9dffae4c3b30165
|
4
|
+
data.tar.gz: 6ef1eeb8106c935d12beb8edafb67004d14c012761c43c675a9ce4d430c794d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4535d5d33d7d8152ffd349d0fbeb4cb544fc71931fb72f93c2c866eda4957249ace7b5337472193ebd58f0f6a987f546a226edc97618fb8bef03652162b534db
|
7
|
+
data.tar.gz: 6887060feada342534a3b4a365eb058e93276aa50ecd8e88456d1b4495e6aee7aa47be22e26adc22dfb292e24a0291ffe3376e06b6359d27fa6d0aaffeaba457
|
data/.drone.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -3,7 +3,37 @@ All notable changes to this project will be documented in this file.
|
|
3
3
|
|
4
4
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
5
5
|
|
6
|
-
## [Unreleased]
|
6
|
+
## [Unreleased]
|
7
|
+
|
8
|
+
## [2.3.4] - 2020-09-09
|
9
|
+
### Fixed
|
10
|
+
- Correctly ignore non-call events and clear handlers on call end
|
11
|
+
- Async method arguments fixed
|
12
|
+
- Fix ping race condition
|
13
|
+
|
14
|
+
## [2.3.3] - 2020-03-09
|
15
|
+
### Fixed
|
16
|
+
- `record` correctly set up for terminators now.
|
17
|
+
- Make AMD more usable and less complex.
|
18
|
+
- AMD now returns immediately in `wait_for_beep` mode if it detects a human.
|
19
|
+
- Relax `gemspec` dependencies.
|
20
|
+
|
21
|
+
## [2.3.2] - 2020-01-29
|
22
|
+
### Fixed
|
23
|
+
- Correctly return AMD result in Relay.
|
24
|
+
|
25
|
+
## [2.3.1] - 2019-12-20
|
26
|
+
### Changed
|
27
|
+
- Keepalive now uses `blade.ping`.
|
28
|
+
|
29
|
+
## [2.3.0] - 2019-10-22
|
30
|
+
### Added
|
31
|
+
- Add `pause` and `resume` on `PlayAction`.
|
32
|
+
- Add `volume` optional parameter to `play` and `prompt` methods
|
33
|
+
- Add `volume` method to `Play` and `Prompt` components
|
34
|
+
- Add `play_ringtone` and `play_ringtone!` for ringback.
|
35
|
+
- Add `prompt_ringtone` and `prompt_ringtone!` for ringback on a prompt.
|
36
|
+
- Added `ringback` parameter to the `connect` and `connect!` methods.
|
7
37
|
|
8
38
|
## [2.2.0] - 2019-09-09
|
9
39
|
### Added
|
@@ -0,0 +1,30 @@
|
|
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
|
+
# Set logging to debug for testing
|
10
|
+
Signalwire::Logger.logger.level = ::Logger::DEBUG
|
11
|
+
|
12
|
+
class OutboundConsumer < Signalwire::Relay::Consumer
|
13
|
+
def ready
|
14
|
+
logger.info 'Dialing out'
|
15
|
+
call = client.calling.new_call(from: ENV['FROM_NUMBER'], to: ENV['TO_NUMBER'])
|
16
|
+
call.dial
|
17
|
+
result = call.amd(timeout: 10, wait_for_beep: true)
|
18
|
+
pp "---------------------------------- Detect AM result was:"
|
19
|
+
pp result.type
|
20
|
+
pp result.result
|
21
|
+
pp result.successful
|
22
|
+
|
23
|
+
call.hangup
|
24
|
+
rescue StandardError => e
|
25
|
+
logger.error e.inspect
|
26
|
+
logger.error e.backtrace
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
OutboundConsumer.new.run
|
@@ -0,0 +1,26 @@
|
|
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
|
+
# Set logging to debug for testing
|
10
|
+
Signalwire::Logger.logger.level = ::Logger::DEBUG
|
11
|
+
|
12
|
+
class OutboundConsumer < Signalwire::Relay::Consumer
|
13
|
+
def ready
|
14
|
+
dial_result = client.calling.new_call(from: ENV['FROM_NUMBER'], to: ENV['TO_NUMBER']).dial
|
15
|
+
tts = [{ type: 'tts', params: { text: 'Say something funny!' } }]
|
16
|
+
result = dial_result.call.prompt collect: {speech: {language: 'en-US'}}, play: tts
|
17
|
+
dial_result.call.play_tts text: "You ordered #{result.result} hamburgers. Thank you!"
|
18
|
+
dial_result.call.hangup
|
19
|
+
# this makes it so the errors don't stop the process
|
20
|
+
rescue StandardError => e
|
21
|
+
logger.error e.inspect
|
22
|
+
logger.error e.backtrace
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
OutboundConsumer.new.run
|
@@ -16,6 +16,7 @@ class OutboundConsumer < Signalwire::Relay::Consumer
|
|
16
16
|
call.dial
|
17
17
|
call.play_tts text: 'please leave your message after the beep. Press pound when done.'
|
18
18
|
result = call.record(beep: true, terminators: "#")
|
19
|
+
sleep 1 # wait for recording to be uploaded
|
19
20
|
call.play_tts text: 'you said:'
|
20
21
|
call.play_audio result.url
|
21
22
|
call.hangup
|
data/lib/signalwire.rb
CHANGED
data/lib/signalwire/blade.rb
CHANGED
@@ -4,6 +4,7 @@ require 'has_guarded_handlers'
|
|
4
4
|
require 'eventmachine'
|
5
5
|
require 'faye/websocket'
|
6
6
|
require 'json'
|
7
|
+
require 'concurrent-ruby'
|
7
8
|
|
8
9
|
module Signalwire::Blade
|
9
10
|
class Connection
|
@@ -22,9 +23,15 @@ module Signalwire::Blade
|
|
22
23
|
@log_traffic = options.fetch(:log_traffic, true)
|
23
24
|
@authentication = options.fetch(:authentication, nil)
|
24
25
|
|
26
|
+
|
27
|
+
|
25
28
|
@inbound_queue = EM::Queue.new
|
26
29
|
@outbound_queue = EM::Queue.new
|
27
30
|
|
31
|
+
@pong = Concurrent::AtomicBoolean.new
|
32
|
+
@keep_alive_timer = nil
|
33
|
+
@ping_is_sent = Concurrent::AtomicBoolean.new
|
34
|
+
|
28
35
|
@shutdown_list = []
|
29
36
|
end
|
30
37
|
|
@@ -37,7 +44,7 @@ module Signalwire::Blade
|
|
37
44
|
end
|
38
45
|
|
39
46
|
def reconnect!
|
40
|
-
|
47
|
+
clear_connections
|
41
48
|
return if @shutdown
|
42
49
|
sleep Signalwire::Blade::RECONNECT_PERIOD
|
43
50
|
logger.info "Attempting reconnection"
|
@@ -46,6 +53,7 @@ module Signalwire::Blade
|
|
46
53
|
|
47
54
|
def main_loop!
|
48
55
|
EM.run do
|
56
|
+
logger.info "CREATING SOCKET"
|
49
57
|
@ws = Faye::WebSocket::Client.new(@url)
|
50
58
|
|
51
59
|
@ws.on(:open) { |event| broadcast :started, event }
|
@@ -69,13 +77,14 @@ module Signalwire::Blade
|
|
69
77
|
begin
|
70
78
|
@connected = true
|
71
79
|
myreq = connect_request
|
72
|
-
|
80
|
+
@pong.make_false
|
73
81
|
|
74
82
|
write_command(myreq) do |event|
|
75
83
|
@session_id = event.dig(:result, :sessionid) unless @session_id
|
76
84
|
@node_id = event.dig(:result, :nodeid) unless @node_d
|
77
85
|
logger.info "Blade Session connected with id: #{@session_id}"
|
78
86
|
broadcast :connected, event
|
87
|
+
keep_alive
|
79
88
|
end
|
80
89
|
|
81
90
|
rescue StandardError => e
|
@@ -127,18 +136,29 @@ module Signalwire::Blade
|
|
127
136
|
block_given? ? write_command(Execute.new(params), &block) : write_command(Execute.new(params))
|
128
137
|
end
|
129
138
|
|
139
|
+
def ping(&block)
|
140
|
+
ping_cmd = Ping.new
|
141
|
+
block_given? ? write_command(ping_cmd, &block) : write_command(ping_cmd)
|
142
|
+
ping_cmd
|
143
|
+
end
|
144
|
+
|
130
145
|
def subscribe(params, &block)
|
131
146
|
block_given? ? write_command(Subscribe.new(params), &block) : write_command(Subscribe.new(params))
|
132
147
|
end
|
133
148
|
|
134
149
|
def handle_close
|
150
|
+
logger.warn "WS Socket closed!"
|
135
151
|
reconnect!
|
136
152
|
end
|
137
153
|
|
138
|
-
def
|
139
|
-
# logger.info 'Stopping Blade event loop'
|
154
|
+
def clear_connections
|
140
155
|
@ws = nil
|
141
156
|
@connected = false
|
157
|
+
@keep_alive_timer.cancel if @keep_alive_timer
|
158
|
+
end
|
159
|
+
|
160
|
+
def disconnect!
|
161
|
+
clear_connections
|
142
162
|
EM.stop
|
143
163
|
end
|
144
164
|
|
@@ -169,20 +189,22 @@ module Signalwire::Blade
|
|
169
189
|
@connected == true
|
170
190
|
end
|
171
191
|
|
172
|
-
def
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
logger.error "We got disconnected!"
|
177
|
-
pinger.cancel
|
178
|
-
reconnect! if connected?
|
192
|
+
def keep_alive
|
193
|
+
if @ping_is_sent.false?
|
194
|
+
ping do
|
195
|
+
@pong.make_true
|
179
196
|
end
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
197
|
+
@ping_is_sent.make_true
|
198
|
+
else
|
199
|
+
if @pong.false?
|
200
|
+
logger.error "KEEPALIVE: Ping failed"
|
201
|
+
reconnect! if connected?
|
185
202
|
end
|
203
|
+
@ping_is_sent.make_false
|
204
|
+
end
|
205
|
+
|
206
|
+
@keep_alive_timer = EventMachine::Timer.new(Signalwire::Relay::PING_TIMEOUT) do
|
207
|
+
keep_alive
|
186
208
|
end
|
187
209
|
end
|
188
210
|
|
data/lib/signalwire/relay.rb
CHANGED
@@ -28,17 +28,24 @@ require 'signalwire/relay/calling/action/send_digits_action'
|
|
28
28
|
|
29
29
|
require 'signalwire/relay/calling/result'
|
30
30
|
require 'signalwire/relay/calling/result/answer_result'
|
31
|
+
require 'signalwire/relay/calling/result/base_action_result'
|
31
32
|
require 'signalwire/relay/calling/result/connect_result'
|
32
33
|
require 'signalwire/relay/calling/result/detect_result'
|
33
34
|
require 'signalwire/relay/calling/result/dial_result'
|
34
35
|
require 'signalwire/relay/calling/result/hangup_result'
|
35
36
|
require 'signalwire/relay/calling/result/play_result'
|
37
|
+
require 'signalwire/relay/calling/result/play_pause_result'
|
38
|
+
require 'signalwire/relay/calling/result/play_resume_result'
|
39
|
+
require 'signalwire/relay/calling/result/play_volume_result'
|
36
40
|
require 'signalwire/relay/calling/result/prompt_result'
|
41
|
+
require 'signalwire/relay/calling/result/prompt_volume_result'
|
37
42
|
require 'signalwire/relay/calling/result/record_result'
|
38
43
|
require 'signalwire/relay/calling/result/fax_result'
|
39
44
|
require 'signalwire/relay/calling/result/tap_result'
|
40
45
|
require 'signalwire/relay/calling/result/send_digits_result'
|
41
46
|
|
47
|
+
require 'signalwire/relay/calling/result/stop_result'
|
48
|
+
|
42
49
|
require 'signalwire/relay/calling/component'
|
43
50
|
require 'signalwire/relay/calling/control_component'
|
44
51
|
require 'signalwire/relay/calling/component/answer'
|
@@ -5,11 +5,23 @@ require 'forwardable'
|
|
5
5
|
module Signalwire::Relay::Calling
|
6
6
|
class PlayAction < Action
|
7
7
|
def result
|
8
|
-
PlayResult.new(@component)
|
8
|
+
PlayResult.new(component: @component)
|
9
9
|
end
|
10
10
|
|
11
11
|
def stop
|
12
12
|
@component.stop
|
13
13
|
end
|
14
|
+
|
15
|
+
def pause
|
16
|
+
@component.pause
|
17
|
+
end
|
18
|
+
|
19
|
+
def resume
|
20
|
+
@component.resume
|
21
|
+
end
|
22
|
+
|
23
|
+
def volume(setting)
|
24
|
+
@component.volume setting
|
25
|
+
end
|
14
26
|
end
|
15
27
|
end
|
@@ -5,11 +5,15 @@ require 'forwardable'
|
|
5
5
|
module Signalwire::Relay::Calling
|
6
6
|
class PromptAction < Action
|
7
7
|
def result
|
8
|
-
PromptResult.new(@component)
|
8
|
+
PromptResult.new(component: @component)
|
9
9
|
end
|
10
10
|
|
11
11
|
def stop
|
12
12
|
@component.stop
|
13
13
|
end
|
14
|
+
|
15
|
+
def volume(setting)
|
16
|
+
@component.volume setting
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
@@ -5,11 +5,15 @@ require 'forwardable'
|
|
5
5
|
module Signalwire::Relay::Calling
|
6
6
|
class RecordAction < Action
|
7
7
|
def result
|
8
|
-
RecordResult.new(@component)
|
8
|
+
RecordResult.new(component: @component)
|
9
9
|
end
|
10
10
|
|
11
11
|
def stop
|
12
12
|
@component.stop
|
13
13
|
end
|
14
|
+
|
15
|
+
def url
|
16
|
+
@component.url
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
@@ -45,7 +45,7 @@ module Signalwire::Relay::Calling
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def setup_call_event_handlers
|
48
|
-
@client.on(:event, proc { |evt| call_match_event(evt) }) do |event|
|
48
|
+
@call_event_handler = @client.on(:event, proc { |evt| call_match_event(evt) }) do |event|
|
49
49
|
case event.event_type
|
50
50
|
when 'calling.call.connect'
|
51
51
|
change_connect_state(event.call_params[:connect_state])
|
@@ -81,7 +81,8 @@ module Signalwire::Relay::Calling
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def call_match_event(event)
|
84
|
-
event.event_type
|
84
|
+
event.event_type &&
|
85
|
+
event.event_type.match(/calling\.call/) &&
|
85
86
|
!event.event_type.match(/receive/) &&
|
86
87
|
(event.call_id == id || event.call_params[:tag] == tag)
|
87
88
|
end
|
@@ -112,50 +113,64 @@ module Signalwire::Relay::Calling
|
|
112
113
|
AnswerResult.new(component: answer_component)
|
113
114
|
end
|
114
115
|
|
115
|
-
def play(
|
116
|
-
|
116
|
+
def play(play_p = nil, volume_p = nil, **args)
|
117
|
+
play = args.delete(:play)
|
118
|
+
volume = args.delete(:volume)
|
119
|
+
set_parameters(binding, %i{play volume}, %i{play})
|
120
|
+
|
121
|
+
play_component = Signalwire::Relay::Calling::Play.new(call: self, play: play, volume: volume)
|
117
122
|
play_component.wait_for(Relay::CallPlayState::FINISHED, Relay::CallPlayState::ERROR)
|
118
123
|
PlayResult.new(component: play_component)
|
119
124
|
end
|
120
125
|
|
121
|
-
def play!(
|
122
|
-
|
126
|
+
def play!(play_p = nil, volume_p = nil, **args)
|
127
|
+
play = args.delete(:play)
|
128
|
+
volume = args.delete(:volume)
|
129
|
+
set_parameters(binding, %i{play volume}, %i{play})
|
130
|
+
|
131
|
+
play_component = Signalwire::Relay::Calling::Play.new(call: self, play: play, volume: volume)
|
123
132
|
play_component.execute
|
124
133
|
PlayAction.new(component: play_component)
|
125
134
|
end
|
126
135
|
|
127
|
-
def prompt(collect_p = nil, play_p = nil, **args)
|
128
|
-
|
136
|
+
def prompt(collect_p = nil, play_p = nil, volume_p = nil, **args)
|
129
137
|
collect = args.delete(:collect)
|
130
138
|
play = args.delete(:play)
|
139
|
+
volume = args.delete(:volume)
|
131
140
|
|
132
141
|
collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
|
133
142
|
set_parameters(binding, %i{collect play}, %i{collect play})
|
134
143
|
|
135
|
-
component = Prompt.new(call: self, collect: collect, play: play)
|
144
|
+
component = Prompt.new(call: self, collect: collect, play: play, volume: volume)
|
136
145
|
component.wait_for(Relay::CallPromptState::ERROR, Relay::CallPromptState::NO_INPUT,
|
137
146
|
Relay::CallPromptState::NO_MATCH, Relay::CallPromptState::DIGIT,
|
138
147
|
Relay::CallPromptState::SPEECH)
|
139
148
|
PromptResult.new(component: component)
|
140
149
|
end
|
141
150
|
|
142
|
-
def prompt!(collect_p = nil, play_p = nil, **args)
|
151
|
+
def prompt!(collect_p = nil, play_p = nil, volume_p = nil, **args)
|
152
|
+
collect = args.delete(:collect)
|
153
|
+
play = args.delete(:play)
|
154
|
+
volume = args.delete(:volume)
|
155
|
+
|
143
156
|
collect = compile_collect_arguments(args) if collect.nil? && collect_p.nil?
|
144
157
|
set_parameters(binding, %i{collect play}, %i{collect play})
|
145
158
|
|
146
|
-
component = Prompt
|
159
|
+
component = Prompt.new(call: self, collect: collect, play: play, volume: volume)
|
147
160
|
component.execute
|
148
161
|
PromptAction.new(component: component)
|
149
162
|
end
|
150
163
|
|
151
|
-
def connect(
|
152
|
-
|
164
|
+
def connect(devices_p = nil, ringback_p = nil, devices: nil, ringback: nil)
|
165
|
+
set_parameters(binding, %i{devices ringback}, %i{devices})
|
166
|
+
component = ringback.nil? ? Connect.new(call: self, devices: devices) : Connect.new(call: self, devices: devices, ringback: ringback)
|
153
167
|
component.wait_for(Relay::CallConnectState::CONNECTED, Relay::CallConnectState::FAILED)
|
154
168
|
ConnectResult.new(component: component)
|
155
169
|
end
|
156
170
|
|
157
|
-
def connect!(
|
158
|
-
|
171
|
+
def connect!(devices_p = nil, ringback_p = nil, devices: nil, ringback: nil)
|
172
|
+
set_parameters(binding, %i{devices ringback}, %i{devices})
|
173
|
+
component = ringback.nil? ? Connect.new(call: self, devices: devices) : Connect.new(call: self, devices: devices, ringback: ringback)
|
159
174
|
component.execute
|
160
175
|
ConnectAction.new(component: component)
|
161
176
|
end
|
@@ -302,6 +317,7 @@ module Signalwire::Relay::Calling
|
|
302
317
|
end
|
303
318
|
|
304
319
|
def finish_call(params)
|
320
|
+
@client.unregister_handler(:event, @call_event_handler)
|
305
321
|
terminate_components(params)
|
306
322
|
client.calling.end_call(id)
|
307
323
|
@busy = true if params[:reason] == Relay::DisconnectReason::BUSY
|
@@ -375,7 +391,7 @@ module Signalwire::Relay::Calling
|
|
375
391
|
speech_obj[:language] = args[:speech_language] if args[:speech_language]
|
376
392
|
speech_obj[:hints] = args[:speech_hints] if args[:speech_hints]
|
377
393
|
speech_obj[:end_silence_timeout] = args[:end_silence_timeout] if args[:end_silence_timeout]
|
378
|
-
collect[:
|
394
|
+
collect[:speech] = speech_obj
|
379
395
|
end
|
380
396
|
|
381
397
|
collect
|