signalwire 1.4.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +55 -0
- data/.rubocop.yml +14 -2
- data/AUTHORS.md +1 -0
- data/CHANGELOG.md +30 -5
- data/Gemfile +4 -18
- data/LICENSE +21 -0
- data/README.md +22 -82
- data/Rakefile +0 -17
- data/examples/relay/inbound_consumer.rb +26 -0
- data/examples/relay/inbound_dial.rb +28 -0
- data/examples/relay/outbound_collect.rb +27 -0
- data/examples/relay/outbound_consumer.rb +22 -0
- data/examples/relay/outbound_record.rb +25 -0
- data/lib/signalwire.rb +13 -0
- data/lib/signalwire/blade.rb +12 -0
- data/lib/signalwire/blade/connection.rb +200 -0
- data/lib/signalwire/blade/event_handler.rb +15 -0
- data/lib/signalwire/blade/message.rb +38 -0
- data/lib/signalwire/blade/message/connect.rb +18 -0
- data/lib/signalwire/blade/message/execute.rb +16 -0
- data/lib/signalwire/blade/message/subscribe.rb +15 -0
- data/lib/signalwire/common.rb +6 -0
- data/lib/signalwire/logger.rb +27 -0
- data/lib/signalwire/relay.rb +40 -0
- data/lib/signalwire/relay/calling.rb +82 -0
- data/lib/signalwire/relay/calling/action.rb +16 -0
- data/lib/signalwire/relay/calling/action/connect_action.rb +11 -0
- data/lib/signalwire/relay/calling/action/play_action.rb +15 -0
- data/lib/signalwire/relay/calling/action/prompt_action.rb +15 -0
- data/lib/signalwire/relay/calling/action/record_action.rb +15 -0
- data/lib/signalwire/relay/calling/call.rb +210 -0
- data/lib/signalwire/relay/calling/call_convenience_methods.rb +79 -0
- data/lib/signalwire/relay/calling/component.rb +115 -0
- data/lib/signalwire/relay/calling/component/answer.rb +27 -0
- data/lib/signalwire/relay/calling/component/await.rb +20 -0
- data/lib/signalwire/relay/calling/component/connect.rb +45 -0
- data/lib/signalwire/relay/calling/component/dial.rb +34 -0
- data/lib/signalwire/relay/calling/component/hangup.rb +41 -0
- data/lib/signalwire/relay/calling/component/play.rb +46 -0
- data/lib/signalwire/relay/calling/component/prompt.rb +62 -0
- data/lib/signalwire/relay/calling/component/record.rb +53 -0
- data/lib/signalwire/relay/calling/control_component.rb +35 -0
- data/lib/signalwire/relay/calling/result.rb +16 -0
- data/lib/signalwire/relay/calling/result/answer_result.rb +6 -0
- data/lib/signalwire/relay/calling/result/connect_result.rb +9 -0
- data/lib/signalwire/relay/calling/result/dial_result.rb +7 -0
- data/lib/signalwire/relay/calling/result/hangup_result.rb +7 -0
- data/lib/signalwire/relay/calling/result/play_result.rb +6 -0
- data/lib/signalwire/relay/calling/result/prompt_result.rb +11 -0
- data/lib/signalwire/relay/calling/result/record_result.rb +7 -0
- data/lib/signalwire/relay/client.rb +147 -0
- data/lib/signalwire/relay/constants.rb +109 -0
- data/lib/signalwire/relay/consumer.rb +88 -0
- data/lib/signalwire/relay/event.rb +41 -0
- data/lib/signalwire/relay/request.rb +6 -0
- data/lib/signalwire/rest/client.rb +5 -5
- data/lib/signalwire/sdk/fax_response.rb +3 -3
- data/lib/signalwire/sdk/messaging_response.rb +0 -2
- data/lib/signalwire/sdk/twilio_set_fax.rb +9 -8
- data/lib/signalwire/sdk/twilio_set_host.rb +8 -3
- data/lib/signalwire/version.rb +5 -0
- data/signalwire.gemspec +36 -106
- metadata +173 -76
- data/LICENSE.txt +0 -20
- data/VERSION +0 -1
- data/spec/signalwire/rest/client_spec.rb +0 -30
- data/spec/signalwire/rest/integration_spec.rb +0 -102
- data/spec/signalwire/sdk/configuration_spec.rb +0 -28
- data/spec/signalwire/sdk/fax_response_spec.rb +0 -26
- data/spec/signalwire/sdk/messaging_response_spec.rb +0 -18
- data/spec/signalwire/sdk/voice_response_spec.rb +0 -20
- data/spec/signalwire/sdk_spec.rb +0 -27
- data/spec/spec_helper.rb +0 -119
- data/spec/vcr_cassettes/accounts.yml +0 -27
- data/spec/vcr_cassettes/applications.yml +0 -29
- data/spec/vcr_cassettes/get_fax.yml +0 -25
- data/spec/vcr_cassettes/get_fax_media_instance.yml +0 -27
- data/spec/vcr_cassettes/get_fax_media_list.yml +0 -47
- data/spec/vcr_cassettes/list_faxes.yml +0 -25
- data/spec/vcr_cassettes/local_numbers.yml +0 -26
- data/spec/vcr_cassettes/recordings.yml +0 -27
- data/spec/vcr_cassettes/send_fax.yml +0 -27
- data/spec/vcr_cassettes/toll_free_numbers.yml +0 -34
- data/spec/vcr_cassettes/transcriptions.yml +0 -28
@@ -0,0 +1,25 @@
|
|
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
|
+
call.play_tts 'please leave your message after the beep. Press pound when done.'
|
18
|
+
result = call.record({"audio": { "beep": "true", "terminators": "#"}})
|
19
|
+
call.play_tts 'you said:'
|
20
|
+
call.play_audio result.url
|
21
|
+
call.hangup
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
OutboundConsumer.new.run
|
data/lib/signalwire.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Signalwire::Blade
|
4
|
+
RECONNECT_PERIOD = 5
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'signalwire/blade/event_handler'
|
8
|
+
require 'signalwire/blade/connection'
|
9
|
+
require 'signalwire/blade/message'
|
10
|
+
require 'signalwire/blade/message/connect'
|
11
|
+
require 'signalwire/blade/message/execute'
|
12
|
+
require 'signalwire/blade/message/subscribe'
|
@@ -0,0 +1,200 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'has_guarded_handlers'
|
4
|
+
require 'eventmachine'
|
5
|
+
require 'faye/websocket'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module Signalwire::Blade
|
9
|
+
class Connection
|
10
|
+
include Signalwire::Logger
|
11
|
+
include Signalwire::Blade::EventHandler
|
12
|
+
include Signalwire::Common
|
13
|
+
|
14
|
+
attr_reader :session_id, :connected, :node_id, :connection
|
15
|
+
|
16
|
+
def initialize(**options)
|
17
|
+
@options = options
|
18
|
+
@session_id = nil
|
19
|
+
@node_id = nil
|
20
|
+
@connected = false
|
21
|
+
@url = @options.fetch(:url, 'wss://relay.signalwire.com')
|
22
|
+
@log_traffic = options.fetch(:log_traffic, true)
|
23
|
+
@authentication = options.fetch(:authentication, nil)
|
24
|
+
|
25
|
+
@inbound_queue = EM::Queue.new
|
26
|
+
@outbound_queue = EM::Queue.new
|
27
|
+
|
28
|
+
@counter = 1
|
29
|
+
end
|
30
|
+
|
31
|
+
def connect!
|
32
|
+
setup_started_event
|
33
|
+
enable_epoll
|
34
|
+
handle_signals
|
35
|
+
|
36
|
+
main_loop!
|
37
|
+
end
|
38
|
+
|
39
|
+
def reconnect!
|
40
|
+
return if @shutdown
|
41
|
+
sleep Signalwire::Blade::RECONNECT_PERIOD
|
42
|
+
@connected = false
|
43
|
+
logger.info "Attempting reconnection"
|
44
|
+
main_loop!
|
45
|
+
end
|
46
|
+
|
47
|
+
def main_loop!
|
48
|
+
EM.run do
|
49
|
+
@ws = Faye::WebSocket::Client.new(@url)
|
50
|
+
|
51
|
+
@ws.on(:open) { |event| broadcast :started, event }
|
52
|
+
@ws.on(:message) { |event| enqueue_inbound event }
|
53
|
+
@ws.on(:close) { handle_close }
|
54
|
+
|
55
|
+
@ws.on :error do |error|
|
56
|
+
logger.error "Error occurred: #{error.message}"
|
57
|
+
end
|
58
|
+
|
59
|
+
EM.next_tick { flush_queues }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup_started_event
|
64
|
+
on :started do |_event|
|
65
|
+
@connected = true
|
66
|
+
myreq = connect_request
|
67
|
+
start_periodic_timer
|
68
|
+
|
69
|
+
write_command(myreq) do |event|
|
70
|
+
@session_id = event.dig(:result, :sessionid) unless @session_id
|
71
|
+
@node_id = event.dig(:result, :nodeid) unless @node_d
|
72
|
+
logger.info "Blade Session connected with id: #{@session_id}"
|
73
|
+
broadcast :connected, event
|
74
|
+
end
|
75
|
+
|
76
|
+
rescue StandardError => e
|
77
|
+
logger.error e.inspect
|
78
|
+
logger.error e.backtrace
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def enable_epoll
|
83
|
+
# This is only enabled on Linux
|
84
|
+
EM.epoll
|
85
|
+
logger.debug "Running with epoll #{EM.epoll?}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def transmit(message)
|
89
|
+
enqueue_outbound message
|
90
|
+
end
|
91
|
+
|
92
|
+
def write(message)
|
93
|
+
log_traffic :send, message
|
94
|
+
@ws.send(message)
|
95
|
+
end
|
96
|
+
|
97
|
+
def receive(message)
|
98
|
+
event = Message.from_json(message.data)
|
99
|
+
log_traffic :recv, event.payload
|
100
|
+
EM.defer do
|
101
|
+
broadcast :message, event
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def write_command(command, &block)
|
106
|
+
once(:message, id: command.id, &block) if block_given?
|
107
|
+
transmit(command.build_request.to_json)
|
108
|
+
end
|
109
|
+
|
110
|
+
def execute(params, &block)
|
111
|
+
block_given? ? write_command(Execute.new(params), &block) : write_command(Execute.new(params))
|
112
|
+
end
|
113
|
+
|
114
|
+
def subscribe(params, &block)
|
115
|
+
block_given? ? write_command(Subscribe.new(params), &block) : write_command(Subscribe.new(params))
|
116
|
+
end
|
117
|
+
|
118
|
+
def handle_close
|
119
|
+
reconnect!
|
120
|
+
end
|
121
|
+
|
122
|
+
def disconnect!
|
123
|
+
# logger.info 'Stopping Blade event loop'
|
124
|
+
@ws = nil
|
125
|
+
@connected = false
|
126
|
+
EM.stop
|
127
|
+
end
|
128
|
+
|
129
|
+
def flush_queues
|
130
|
+
@inbound_queue.pop { |inbound| receive(inbound) } until @inbound_queue.empty?
|
131
|
+
if connected?
|
132
|
+
@outbound_queue.pop { |outbound| write(outbound) } until @outbound_queue.empty?
|
133
|
+
end
|
134
|
+
|
135
|
+
EM.next_tick { flush_queues }
|
136
|
+
end
|
137
|
+
|
138
|
+
def enqueue_inbound(message)
|
139
|
+
@inbound_queue.push message
|
140
|
+
end
|
141
|
+
|
142
|
+
def enqueue_outbound(message)
|
143
|
+
@outbound_queue.push message
|
144
|
+
end
|
145
|
+
|
146
|
+
def connect_request
|
147
|
+
req = Connect.new
|
148
|
+
req[:params][:authentication] = @authentication if @authentication
|
149
|
+
req
|
150
|
+
end
|
151
|
+
|
152
|
+
def connected?
|
153
|
+
@connected == true
|
154
|
+
end
|
155
|
+
|
156
|
+
def start_periodic_timer
|
157
|
+
pinger = EventMachine::PeriodicTimer.new(Signalwire::Relay::PING_TIMEOUT) do
|
158
|
+
timeouter = EventMachine::Timer.new(2) do
|
159
|
+
# reconnect logic goes here
|
160
|
+
logger.error "We got disconnected!"
|
161
|
+
pinger.cancel
|
162
|
+
reconnect!
|
163
|
+
end
|
164
|
+
|
165
|
+
@ws.ping 'detecting presence' do
|
166
|
+
timeouter.cancel
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def log_traffic(direction, message)
|
172
|
+
if @log_traffic
|
173
|
+
pretty = case direction
|
174
|
+
when :send
|
175
|
+
JSON.pretty_generate(JSON.parse(message))
|
176
|
+
when :recv
|
177
|
+
JSON.pretty_generate(message)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
logger.debug "#{direction.to_s.upcase}: #{pretty}"
|
181
|
+
end
|
182
|
+
|
183
|
+
def handle_signals
|
184
|
+
Signal.trap('INT') do
|
185
|
+
shutdown_from_signal
|
186
|
+
end
|
187
|
+
|
188
|
+
Signal.trap('TERM') do
|
189
|
+
shutdown_from_signal
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
def shutdown_from_signal
|
195
|
+
@shutdown = true
|
196
|
+
disconnect!
|
197
|
+
exit
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'has_guarded_handlers'
|
4
|
+
|
5
|
+
module Signalwire::Blade
|
6
|
+
module EventHandler
|
7
|
+
include HasGuardedHandlers
|
8
|
+
alias on register_handler
|
9
|
+
alias once register_tmp_handler
|
10
|
+
|
11
|
+
def broadcast(event_type, event)
|
12
|
+
trigger_handler event_type, event, broadcast: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Signalwire::Blade
|
6
|
+
class Message
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@payload, :[], :[]=, :dig
|
9
|
+
|
10
|
+
def initialize(params = {})
|
11
|
+
@payload = params
|
12
|
+
@id = params[:id]
|
13
|
+
end
|
14
|
+
|
15
|
+
def id
|
16
|
+
@id ||= SecureRandom.uuid
|
17
|
+
end
|
18
|
+
|
19
|
+
def payload
|
20
|
+
@payload ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_request
|
24
|
+
payload.merge(
|
25
|
+
jsonrpc: '2.0',
|
26
|
+
id: id
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.from_json(json_hash)
|
31
|
+
new JSON.parse(json_hash, symbolize_names: true)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
inspect
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Signalwire::Blade
|
4
|
+
class Connect < Message
|
5
|
+
def initialize
|
6
|
+
@payload = {
|
7
|
+
method: 'blade.connect',
|
8
|
+
params: {
|
9
|
+
version: {
|
10
|
+
major: 2,
|
11
|
+
minor: 1,
|
12
|
+
revision: 0
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Signalwire::Blade
|
4
|
+
class Execute < Message
|
5
|
+
# Creates an Execute message
|
6
|
+
#
|
7
|
+
# @param params [Hash] The "params" portion of the execute
|
8
|
+
def initialize(params = {})
|
9
|
+
@payload =
|
10
|
+
{
|
11
|
+
method: 'blade.execute',
|
12
|
+
params: params
|
13
|
+
}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Signalwire::Blade
|
4
|
+
class Subscribe < Message
|
5
|
+
# Creates a Subscribe message
|
6
|
+
#
|
7
|
+
# @param params [Hash] The "params" portion of the message
|
8
|
+
def initialize(params = {})
|
9
|
+
@payload = {
|
10
|
+
method: 'blade.subscription',
|
11
|
+
params: params
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module Signalwire
|
6
|
+
module Logger
|
7
|
+
class << self
|
8
|
+
# A global logger object
|
9
|
+
# @return [Logger] a Logger instance
|
10
|
+
def logger
|
11
|
+
@logger ||= begin
|
12
|
+
logger = ::Logger.new(STDERR, progname: 'SignalWire', level: ::Logger::DEBUG)
|
13
|
+
logger.level = ENV.fetch('SIGNALWIRE_LOG_LEVEL', ::Logger::WARN)
|
14
|
+
logger
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def logger
|
20
|
+
Signalwire::Logger.logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def level=(level)
|
24
|
+
Signalwire::Logger.logger.level = level
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Signalwire::Relay
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'signalwire/relay/constants'
|
7
|
+
require 'signalwire/relay/client'
|
8
|
+
require 'signalwire/relay/request'
|
9
|
+
require 'signalwire/relay/event'
|
10
|
+
require 'signalwire/relay/consumer'
|
11
|
+
|
12
|
+
require 'signalwire/relay/calling'
|
13
|
+
require 'signalwire/relay/calling/call_convenience_methods'
|
14
|
+
require 'signalwire/relay/calling/call'
|
15
|
+
|
16
|
+
require 'signalwire/relay/calling/action'
|
17
|
+
require 'signalwire/relay/calling/action/connect_action'
|
18
|
+
require 'signalwire/relay/calling/action/play_action'
|
19
|
+
require 'signalwire/relay/calling/action/prompt_action'
|
20
|
+
require 'signalwire/relay/calling/action/record_action'
|
21
|
+
|
22
|
+
require 'signalwire/relay/calling/result'
|
23
|
+
require 'signalwire/relay/calling/result/answer_result'
|
24
|
+
require 'signalwire/relay/calling/result/connect_result'
|
25
|
+
require 'signalwire/relay/calling/result/dial_result'
|
26
|
+
require 'signalwire/relay/calling/result/hangup_result'
|
27
|
+
require 'signalwire/relay/calling/result/play_result'
|
28
|
+
require 'signalwire/relay/calling/result/prompt_result'
|
29
|
+
require 'signalwire/relay/calling/result/record_result'
|
30
|
+
|
31
|
+
require 'signalwire/relay/calling/component'
|
32
|
+
require 'signalwire/relay/calling/control_component'
|
33
|
+
require 'signalwire/relay/calling/component/answer'
|
34
|
+
require 'signalwire/relay/calling/component/connect'
|
35
|
+
require 'signalwire/relay/calling/component/dial'
|
36
|
+
require 'signalwire/relay/calling/component/hangup'
|
37
|
+
require 'signalwire/relay/calling/component/play'
|
38
|
+
require 'signalwire/relay/calling/component/prompt'
|
39
|
+
require 'signalwire/relay/calling/component/record'
|
40
|
+
require 'signalwire/relay/calling/component/await'
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'concurrent-ruby'
|
5
|
+
|
6
|
+
module Signalwire::Relay
|
7
|
+
module Calling
|
8
|
+
class Instance
|
9
|
+
extend Forwardable
|
10
|
+
include Signalwire::Logger
|
11
|
+
include Signalwire::Common
|
12
|
+
|
13
|
+
def_delegators :@client, :relay_execute, :protocol, :on, :once, :broadcast
|
14
|
+
|
15
|
+
def initialize(client)
|
16
|
+
@client = client
|
17
|
+
end
|
18
|
+
|
19
|
+
def calls
|
20
|
+
@calls ||= Concurrent::Array.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def contexts
|
24
|
+
@contexts ||= Concurrent::Array.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def receive(context:, &block)
|
28
|
+
@client.on :event, event_type: 'calling.call.receive' do |event|
|
29
|
+
logger.info "Starting up call for #{event.call_params}"
|
30
|
+
call_obj = Signalwire::Relay::Calling::Call.from_event(self, event)
|
31
|
+
calls << call_obj
|
32
|
+
block.call(call_obj) if block_given?
|
33
|
+
end
|
34
|
+
|
35
|
+
receive_command = {
|
36
|
+
protocol: protocol,
|
37
|
+
method: 'call.receive',
|
38
|
+
params: {
|
39
|
+
context: context
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
relay_execute receive_command do
|
44
|
+
contexts << context
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_call_by_id(call_id)
|
49
|
+
calls.find { |call| call.id == call_id }
|
50
|
+
end
|
51
|
+
|
52
|
+
def find_call_by_tag(tag)
|
53
|
+
calls.find { |call| call.tag == tag }
|
54
|
+
end
|
55
|
+
|
56
|
+
def end_call(call_id)
|
57
|
+
calls.delete find_call_by_id(call_id)
|
58
|
+
end
|
59
|
+
|
60
|
+
def new_call(from:, to:, device_type: 'phone', timeout: 30)
|
61
|
+
params = {
|
62
|
+
device: {
|
63
|
+
type: device_type,
|
64
|
+
params: {
|
65
|
+
from_number: from,
|
66
|
+
to_number: to,
|
67
|
+
timeout: timeout
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
call = Call.new(self, params)
|
72
|
+
calls << call
|
73
|
+
call
|
74
|
+
end
|
75
|
+
|
76
|
+
def dial(from:, to:, device_type: 'phone', timeout: 30)
|
77
|
+
handle = new_call(from: from, to: to, device_type: device_type, timeout: timeout)
|
78
|
+
handle.dial
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|