punchblock 1.8.2 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +8 -0
- data/lib/punchblock/component/asterisk/agi/command.rb +0 -11
- data/lib/punchblock/connection/asterisk.rb +3 -3
- data/lib/punchblock/translator/asterisk/agi_command.rb +40 -0
- data/lib/punchblock/translator/asterisk/call.rb +56 -47
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +13 -37
- data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +24 -41
- data/lib/punchblock/translator/asterisk/component/input.rb +2 -1
- data/lib/punchblock/translator/asterisk/component/output.rb +16 -21
- data/lib/punchblock/translator/asterisk/component/record.rb +11 -19
- data/lib/punchblock/translator/asterisk/component.rb +12 -9
- data/lib/punchblock/translator/asterisk.rb +16 -22
- data/lib/punchblock/translator/dtmf_recognizer.rb +4 -4
- data/lib/punchblock/translator/freeswitch/component/input.rb +2 -1
- data/lib/punchblock/translator/input_component.rb +2 -2
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +1 -1
- data/spec/punchblock/connection/asterisk_spec.rb +8 -7
- data/spec/punchblock/translator/asterisk/call_spec.rb +262 -229
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +57 -29
- data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +40 -46
- data/spec/punchblock/translator/asterisk/component/input_spec.rb +7 -7
- data/spec/punchblock/translator/asterisk/component/output_spec.rb +84 -53
- data/spec/punchblock/translator/asterisk/component/record_spec.rb +55 -83
- data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +5 -1
- data/spec/punchblock/translator/asterisk/component_spec.rb +2 -10
- data/spec/punchblock/translator/asterisk_spec.rb +73 -100
- metadata +5 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 64729c9456c13c6897d13dbac65657a51951f67a
|
|
4
|
+
data.tar.gz: aa7522da6cf77ebfbbb044865e7c43b79a1939d3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d39f61aaa9ffb224dfa32e9bd898fadc359a9ec6881682e709ee3d0df36afff2c26854baa30e0fc99794a73c18309e8f4922c17d065cb2f5402573947dfabba1
|
|
7
|
+
data.tar.gz: 5c93e10769dbbd141b71732f163783bf7ff653faa30d3886f382a87c5a3402c5e6bcd8784701d7b55adbfdf1a728f3841a0093d455c1a16e10e94e161473b902
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# [develop](https://github.com/adhearsion/punchblock)
|
|
2
2
|
|
|
3
|
+
# [v1.9.0](https://github.com/adhearsion/punchblock/compare/v1.8.2...v1.9.0) - [2013-05-03](https://rubygems.org/gems/punchblock/versions/1.9.0)
|
|
4
|
+
* Feature: Use RubyAMI 2.0 with a single AMI connection.
|
|
5
|
+
* Feature: Cache channel variables on Asterisk calls.
|
|
6
|
+
* Feature: Allow optional sending of end event when breaking from AsyncAGI on Asterisk. This enables dialplan handback. Only triggers if the channel variable 'PUNCHBLOCK_END_ON_ASYNCAGI_BREAK' is set.
|
|
7
|
+
* Bugfix: Avoid DTMF recognizer failures and race conditions by bringing DTMFRecognizer back into the Input component actor.
|
|
8
|
+
* Bugfix: Catch Asterisk AMI errors in all cases and fail accordingly, instead of ploughing ahead in the face of adversity.
|
|
9
|
+
* Bugfix: Improve performance of Asterisk implementation by no longer spinning up a component actor for AGI command execution.
|
|
10
|
+
|
|
3
11
|
# [v1.8.2](https://github.com/adhearsion/punchblock/compare/v1.8.1...v1.8.2) - [2013-04-19](https://rubygems.org/gems/punchblock/versions/1.8.2)
|
|
4
12
|
* Bugfix: Input initial timeout was being set as a float rather than an integer
|
|
5
13
|
|
|
@@ -86,17 +86,6 @@ module Punchblock
|
|
|
86
86
|
class Success < Event::Complete::Reason
|
|
87
87
|
register :success, :agi_complete
|
|
88
88
|
|
|
89
|
-
def self.new(options = {})
|
|
90
|
-
super().tap do |new_node|
|
|
91
|
-
case options
|
|
92
|
-
when Nokogiri::XML::Node
|
|
93
|
-
new_node.inherit options
|
|
94
|
-
else
|
|
95
|
-
options.each_pair { |k,v| new_node.send :"#{k}=", v }
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
89
|
def node_with_name(name)
|
|
101
90
|
n = if self.class.registered_ns
|
|
102
91
|
find_first "ns:#{name}", :ns => self.class.registered_ns
|
|
@@ -9,19 +9,19 @@ module Punchblock
|
|
|
9
9
|
attr_accessor :event_handler
|
|
10
10
|
|
|
11
11
|
def initialize(options = {})
|
|
12
|
-
@ami_client = RubyAMI::
|
|
12
|
+
@ami_client = RubyAMI::Stream.new options[:host], options[:port], options[:username], options[:password], ->(event) { translator.async.handle_ami_event event }, pb_logger
|
|
13
13
|
@translator = Translator::Asterisk.new @ami_client, self, options[:media_engine]
|
|
14
14
|
super()
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def run
|
|
18
|
-
ami_client.
|
|
18
|
+
ami_client.run
|
|
19
19
|
raise DisconnectedError
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def stop
|
|
23
23
|
translator.async.shutdown
|
|
24
|
-
ami_client.
|
|
24
|
+
ami_client.terminate
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def write(command, options)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'active_support/core_ext/string/filters'
|
|
4
|
+
|
|
5
|
+
module Punchblock
|
|
6
|
+
module Translator
|
|
7
|
+
class Asterisk
|
|
8
|
+
class AGICommand
|
|
9
|
+
ARG_QUOTER = /["\\]/.freeze
|
|
10
|
+
|
|
11
|
+
attr_reader :id
|
|
12
|
+
|
|
13
|
+
def initialize(id, channel, command, *params)
|
|
14
|
+
@id, @channel, @command, @params = id, channel, command, params
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def execute(ami_client)
|
|
18
|
+
ami_client.send_action 'AGI', 'Channel' => @channel, 'Command' => agi_command, 'CommandID' => id
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def parse_result(event)
|
|
22
|
+
parser = RubyAMI::AGIResultParser.new event['Result']
|
|
23
|
+
{code: parser.code, result: parser.result, data: parser.data}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def agi_command
|
|
29
|
+
"#{@command} #{@params.map { |arg| quote_arg(arg) }.join(' ')}".squish
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Arguments surrounded by quotes; quotes backslash-escaped.
|
|
33
|
+
# See parse_args in asterisk/res/res_agi.c (Asterisk 1.4.21.1)
|
|
34
|
+
def quote_arg(arg)
|
|
35
|
+
'"' + arg.to_s.gsub(ARG_QUOTER) { |m| "\\#{m}" } + '"'
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -22,14 +22,15 @@ module Punchblock
|
|
|
22
22
|
|
|
23
23
|
trap_exit :actor_died
|
|
24
24
|
|
|
25
|
-
def initialize(channel, translator, agi_env = nil)
|
|
26
|
-
@channel, @translator = channel, translator
|
|
25
|
+
def initialize(channel, translator, ami_client, connection, agi_env = nil)
|
|
26
|
+
@channel, @translator, @ami_client, @connection = channel, translator, ami_client, connection
|
|
27
27
|
@agi_env = agi_env || {}
|
|
28
28
|
@id, @components = Punchblock.new_uuid, {}
|
|
29
29
|
@answered = false
|
|
30
30
|
@pending_joins = {}
|
|
31
31
|
@progress_sent = false
|
|
32
32
|
@block_commands = false
|
|
33
|
+
@channel_variables = {}
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def register_component(component)
|
|
@@ -49,6 +50,10 @@ module Punchblock
|
|
|
49
50
|
terminate
|
|
50
51
|
end
|
|
51
52
|
|
|
53
|
+
def channel_var(variable)
|
|
54
|
+
@channel_variables[variable] || fetch_channel_var(variable)
|
|
55
|
+
end
|
|
56
|
+
|
|
52
57
|
def to_s
|
|
53
58
|
"#<#{self.class}:#{id} Channel: #{channel.inspect}>"
|
|
54
59
|
end
|
|
@@ -89,7 +94,7 @@ module Punchblock
|
|
|
89
94
|
def send_progress
|
|
90
95
|
return if answered? || outbound? || @progress_sent
|
|
91
96
|
@progress_sent = true
|
|
92
|
-
|
|
97
|
+
execute_agi_command "EXEC Progress"
|
|
93
98
|
end
|
|
94
99
|
|
|
95
100
|
def channel=(other)
|
|
@@ -101,13 +106,7 @@ module Punchblock
|
|
|
101
106
|
|
|
102
107
|
case ami_event.name
|
|
103
108
|
when 'Hangup'
|
|
104
|
-
|
|
105
|
-
@components.dup.each_pair do |id, component|
|
|
106
|
-
safe_from_dead_actors do
|
|
107
|
-
component.call_ended if component.alive?
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
send_end_event HANGUP_CAUSE_TO_END_REASON[ami_event['Cause'].to_i]
|
|
109
|
+
handle_hangup_event HANGUP_CAUSE_TO_END_REASON[ami_event['Cause'].to_i]
|
|
111
110
|
when 'AsyncAGI'
|
|
112
111
|
if component = component_with_id(ami_event['CommandID'])
|
|
113
112
|
component.handle_ami_event ami_event
|
|
@@ -151,6 +150,8 @@ module Punchblock
|
|
|
151
150
|
end
|
|
152
151
|
send_pb_event event
|
|
153
152
|
end
|
|
153
|
+
when 'VarSet'
|
|
154
|
+
@channel_variables[ami_event['Variable']] = ami_event['Value']
|
|
154
155
|
end
|
|
155
156
|
trigger_handler :ami, ami_event
|
|
156
157
|
end
|
|
@@ -172,32 +173,23 @@ module Punchblock
|
|
|
172
173
|
if outbound?
|
|
173
174
|
command.response = true
|
|
174
175
|
else
|
|
175
|
-
|
|
176
|
-
command.response = true
|
|
177
|
-
end
|
|
178
|
-
end
|
|
179
|
-
when Command::Answer
|
|
180
|
-
send_agi_action 'ANSWER' do |response|
|
|
176
|
+
execute_agi_command 'EXEC RINGING'
|
|
181
177
|
command.response = true
|
|
182
178
|
end
|
|
179
|
+
when Command::Answer
|
|
180
|
+
execute_agi_command 'ANSWER'
|
|
181
|
+
command.response = true
|
|
183
182
|
when Command::Hangup
|
|
184
|
-
send_ami_action 'Hangup', 'Channel' => channel, 'Cause' => 16
|
|
185
|
-
|
|
186
|
-
end
|
|
183
|
+
send_ami_action 'Hangup', 'Channel' => channel, 'Cause' => 16
|
|
184
|
+
command.response = true
|
|
187
185
|
when Command::Join
|
|
188
186
|
other_call = translator.call_with_id command.call_id
|
|
189
187
|
@pending_joins[other_call.channel] = command
|
|
190
|
-
|
|
188
|
+
execute_agi_command 'EXEC Bridge', other_call.channel
|
|
191
189
|
when Command::Unjoin
|
|
192
190
|
other_call = translator.call_with_id command.call_id
|
|
193
|
-
redirect_back other_call
|
|
194
|
-
|
|
195
|
-
when RubyAMI::Error
|
|
196
|
-
command.response = ProtocolError.new.setup 'error', response.message
|
|
197
|
-
else
|
|
198
|
-
command.response = true
|
|
199
|
-
end
|
|
200
|
-
end
|
|
191
|
+
redirect_back other_call
|
|
192
|
+
command.response = true
|
|
201
193
|
when Command::Reject
|
|
202
194
|
rejection = case command.reason
|
|
203
195
|
when :busy
|
|
@@ -209,9 +201,8 @@ module Punchblock
|
|
|
209
201
|
else
|
|
210
202
|
'EXEC Congestion'
|
|
211
203
|
end
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
end
|
|
204
|
+
execute_agi_command rejection
|
|
205
|
+
command.response = true
|
|
215
206
|
when Punchblock::Component::Asterisk::AGI::Command
|
|
216
207
|
execute_component Component::Asterisk::AGICommand, command
|
|
217
208
|
when Punchblock::Component::Output
|
|
@@ -223,31 +214,31 @@ module Punchblock
|
|
|
223
214
|
else
|
|
224
215
|
command.response = ProtocolError.new.setup 'command-not-acceptable', "Did not understand command for call #{id}", id
|
|
225
216
|
end
|
|
217
|
+
rescue RubyAMI::Error => e
|
|
218
|
+
command.response = ProtocolError.new.setup 'error', e.message
|
|
226
219
|
rescue Celluloid::DeadActorError
|
|
227
220
|
command.response = ProtocolError.new.setup :item_not_found, "Could not find a component with ID #{command.component_id} for call #{id}", id, command.component_id
|
|
228
221
|
end
|
|
229
222
|
|
|
230
|
-
def
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
end
|
|
236
|
-
execute_component Component::Asterisk::AGICommand, @current_agi_command, :internal => true
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
def send_ami_action(name, headers = {}, &block)
|
|
240
|
-
(name.is_a?(RubyAMI::Action) ? name : RubyAMI::Action.new(name, headers, &block)).tap do |action|
|
|
241
|
-
@current_ami_action = action
|
|
242
|
-
translator.send_ami_action action
|
|
223
|
+
def execute_agi_command(command, *params)
|
|
224
|
+
agi = AGICommand.new Punchblock.new_uuid, channel, command, *params
|
|
225
|
+
condition = Celluloid::Condition.new
|
|
226
|
+
register_tmp_handler :ami, name: 'AsyncAGI', [:[], 'SubEvent'] => 'Exec', [:[], 'CommandID'] => agi.id do |event|
|
|
227
|
+
condition.signal event
|
|
243
228
|
end
|
|
229
|
+
agi.execute @ami_client
|
|
230
|
+
event = condition.wait
|
|
231
|
+
return unless event
|
|
232
|
+
agi.parse_result event
|
|
233
|
+
rescue RubyAMI::Error => e
|
|
234
|
+
abort e
|
|
244
235
|
end
|
|
245
236
|
|
|
246
237
|
def logger_id
|
|
247
238
|
"#{self.class}: #{id}"
|
|
248
239
|
end
|
|
249
240
|
|
|
250
|
-
def redirect_back(other_call = nil
|
|
241
|
+
def redirect_back(other_call = nil)
|
|
251
242
|
redirect_options = {
|
|
252
243
|
'Channel' => channel,
|
|
253
244
|
'Exten' => Asterisk::REDIRECT_EXTENSION,
|
|
@@ -260,7 +251,17 @@ module Punchblock
|
|
|
260
251
|
'ExtraPriority' => Asterisk::REDIRECT_PRIORITY,
|
|
261
252
|
'ExtraContext' => Asterisk::REDIRECT_CONTEXT
|
|
262
253
|
}) if other_call
|
|
263
|
-
send_ami_action 'Redirect', redirect_options
|
|
254
|
+
send_ami_action 'Redirect', redirect_options
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def handle_hangup_event(reason = :hangup)
|
|
258
|
+
@block_commands = true
|
|
259
|
+
@components.dup.each_pair do |id, component|
|
|
260
|
+
safe_from_dead_actors do
|
|
261
|
+
component.call_ended if component.alive?
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
send_end_event reason
|
|
264
265
|
end
|
|
265
266
|
|
|
266
267
|
def actor_died(actor, reason)
|
|
@@ -274,6 +275,15 @@ module Punchblock
|
|
|
274
275
|
|
|
275
276
|
private
|
|
276
277
|
|
|
278
|
+
def fetch_channel_var(variable)
|
|
279
|
+
result = @ami_client.send_action 'GetVar', 'Channel' => channel, 'Variable' => variable
|
|
280
|
+
result['Value'] == '(null)' ? nil : result['Value']
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def send_ami_action(name, headers = {})
|
|
284
|
+
@ami_client.send_action name, headers
|
|
285
|
+
end
|
|
286
|
+
|
|
277
287
|
def send_end_event(reason)
|
|
278
288
|
send_pb_event Event::End.new(:reason => reason)
|
|
279
289
|
translator.deregister_call current_actor
|
|
@@ -283,7 +293,6 @@ module Punchblock
|
|
|
283
293
|
def execute_component(type, command, options = {})
|
|
284
294
|
type.new_link(command, current_actor).tap do |component|
|
|
285
295
|
register_component component
|
|
286
|
-
component.internal = true if options[:internal]
|
|
287
296
|
component.async.execute
|
|
288
297
|
end
|
|
289
298
|
end
|
|
@@ -1,63 +1,39 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
|
-
require 'active_support/core_ext/string/filters'
|
|
4
|
-
|
|
5
3
|
module Punchblock
|
|
6
4
|
module Translator
|
|
7
5
|
class Asterisk
|
|
8
6
|
module Component
|
|
9
7
|
module Asterisk
|
|
10
8
|
class AGICommand < Component
|
|
11
|
-
attr_reader :action
|
|
12
|
-
|
|
13
9
|
def setup
|
|
14
|
-
@
|
|
10
|
+
@agi = Punchblock::Translator::Asterisk::AGICommand.new id, @call.channel, @component_node.name, *@component_node.params_array
|
|
15
11
|
end
|
|
16
12
|
|
|
17
13
|
def execute
|
|
14
|
+
@agi.execute ami_client
|
|
18
15
|
send_ref
|
|
19
|
-
|
|
16
|
+
rescue RubyAMI::Error
|
|
17
|
+
set_node_response false
|
|
18
|
+
terminate
|
|
20
19
|
end
|
|
20
|
+
exclusive :execute
|
|
21
21
|
|
|
22
22
|
def handle_ami_event(event)
|
|
23
|
-
if event.name == 'AsyncAGI'
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if event.name == 'AsyncAGI' && event['SubEvent'] == 'Exec'
|
|
24
|
+
send_complete_event success_reason(event), nil, false
|
|
25
|
+
if @component_node.name == 'ASYNCAGI BREAK' && @call.channel_var('PUNCHBLOCK_END_ON_ASYNCAGI_BREAK')
|
|
26
|
+
@call.handle_hangup_event
|
|
26
27
|
end
|
|
27
|
-
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def handle_response(response)
|
|
31
|
-
case response
|
|
32
|
-
when RubyAMI::Error
|
|
33
|
-
set_node_response false
|
|
34
|
-
when RubyAMI::Response
|
|
35
|
-
send_ref
|
|
28
|
+
terminate
|
|
36
29
|
end
|
|
37
30
|
end
|
|
38
31
|
|
|
39
32
|
private
|
|
40
33
|
|
|
41
|
-
def create_action
|
|
42
|
-
command = current_actor
|
|
43
|
-
RubyAMI::Action.new 'AGI', 'Channel' => @call.channel, 'Command' => agi_command, 'CommandID' => id do |response|
|
|
44
|
-
command.handle_response response
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def agi_command
|
|
49
|
-
"#{@component_node.name} #{@component_node.params_array.map { |arg| quote_arg(arg) }.join(' ')}".squish
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Arguments surrounded by quotes; quotes backslash-escaped.
|
|
53
|
-
# See parse_args in asterisk/res/res_agi.c (Asterisk 1.4.21.1)
|
|
54
|
-
def quote_arg(arg)
|
|
55
|
-
'"' + arg.to_s.gsub(/["\\]/) { |m| "\\#{m}" } + '"'
|
|
56
|
-
end
|
|
57
|
-
|
|
58
34
|
def success_reason(event)
|
|
59
|
-
|
|
60
|
-
Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new
|
|
35
|
+
result = @agi.parse_result event
|
|
36
|
+
Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new result
|
|
61
37
|
end
|
|
62
38
|
end
|
|
63
39
|
end
|
|
@@ -6,70 +6,45 @@ module Punchblock
|
|
|
6
6
|
module Component
|
|
7
7
|
module Asterisk
|
|
8
8
|
class AMIAction < Component
|
|
9
|
-
attr_reader :
|
|
9
|
+
attr_reader :translator
|
|
10
10
|
|
|
11
|
-
def initialize(component_node, translator)
|
|
11
|
+
def initialize(component_node, translator, ami_client)
|
|
12
12
|
super component_node, nil
|
|
13
|
-
@translator = translator
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def setup
|
|
17
|
-
@action = create_action
|
|
18
|
-
@id = @action.action_id
|
|
13
|
+
@translator, @ami_client = translator, ami_client
|
|
19
14
|
end
|
|
20
15
|
|
|
21
16
|
def execute
|
|
22
|
-
send_action
|
|
23
17
|
send_ref
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
send_complete_event error_reason(response)
|
|
30
|
-
when RubyAMI::Response
|
|
31
|
-
send_events
|
|
32
|
-
send_complete_event success_reason(response)
|
|
33
|
-
end
|
|
18
|
+
response = send_action
|
|
19
|
+
final_event = send_events response
|
|
20
|
+
send_complete_event success_reason(response, final_event)
|
|
21
|
+
rescue RubyAMI::Error => e
|
|
22
|
+
send_complete_event error_reason(e)
|
|
34
23
|
end
|
|
35
24
|
|
|
36
25
|
private
|
|
37
26
|
|
|
38
|
-
def create_action
|
|
39
|
-
headers = {}
|
|
40
|
-
@component_node.params_hash.each_pair do |key, value|
|
|
41
|
-
headers[key.to_s.capitalize] = value
|
|
42
|
-
end
|
|
43
|
-
component = current_actor
|
|
44
|
-
RubyAMI::Action.new @component_node.name, headers do |response|
|
|
45
|
-
component.async.handle_response response
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
27
|
def send_action
|
|
50
|
-
@
|
|
28
|
+
@ami_client.send_action @component_node.name, action_headers
|
|
51
29
|
end
|
|
52
30
|
|
|
53
31
|
def error_reason(response)
|
|
54
32
|
Punchblock::Event::Complete::Error.new :details => response.message
|
|
55
33
|
end
|
|
56
34
|
|
|
57
|
-
def success_reason(response)
|
|
35
|
+
def success_reason(response, final_event = nil)
|
|
58
36
|
headers = response.headers
|
|
59
|
-
headers.merge!
|
|
37
|
+
headers.merge! final_event.headers if final_event
|
|
60
38
|
headers.delete 'ActionID'
|
|
61
39
|
Punchblock::Component::Asterisk::AMI::Action::Complete::Success.new :message => headers.delete('Message'), :attributes => headers
|
|
62
40
|
end
|
|
63
41
|
|
|
64
|
-
def send_events
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
@extra_complete_attributes = e.headers
|
|
69
|
-
else
|
|
70
|
-
send_event pb_event_from_ami_event(e)
|
|
71
|
-
end
|
|
42
|
+
def send_events(response)
|
|
43
|
+
final_event = response.events.pop
|
|
44
|
+
response.events.each do |e|
|
|
45
|
+
send_event pb_event_from_ami_event(e)
|
|
72
46
|
end
|
|
47
|
+
final_event
|
|
73
48
|
end
|
|
74
49
|
|
|
75
50
|
def pb_event_from_ami_event(ami_event)
|
|
@@ -77,6 +52,14 @@ module Punchblock
|
|
|
77
52
|
headers.delete 'ActionID'
|
|
78
53
|
Event::Asterisk::AMI::Event.new :name => ami_event.name, :attributes => headers
|
|
79
54
|
end
|
|
55
|
+
|
|
56
|
+
def action_headers
|
|
57
|
+
headers = {}
|
|
58
|
+
@component_node.params_hash.each_pair do |key, value|
|
|
59
|
+
headers[key.to_s.capitalize] = value
|
|
60
|
+
end
|
|
61
|
+
headers
|
|
62
|
+
end
|
|
80
63
|
end
|
|
81
64
|
end
|
|
82
65
|
end
|
|
@@ -16,8 +16,9 @@ module Punchblock
|
|
|
16
16
|
private
|
|
17
17
|
|
|
18
18
|
def register_dtmf_event_handler
|
|
19
|
+
component = current_actor
|
|
19
20
|
call.register_handler :ami, :name => 'DTMF', [:[], 'End'] => 'Yes' do |event|
|
|
20
|
-
|
|
21
|
+
component.process_dtmf event['Digit']
|
|
21
22
|
end
|
|
22
23
|
end
|
|
23
24
|
|
|
@@ -9,7 +9,7 @@ module Punchblock
|
|
|
9
9
|
class Output < Component
|
|
10
10
|
include StopByRedirect
|
|
11
11
|
|
|
12
|
-
UnrenderableDocError
|
|
12
|
+
UnrenderableDocError = Class.new OptionError
|
|
13
13
|
|
|
14
14
|
def setup
|
|
15
15
|
@media_engine = @call.translator.media_engine
|
|
@@ -25,8 +25,6 @@ module Punchblock
|
|
|
25
25
|
|
|
26
26
|
early = !@call.answered?
|
|
27
27
|
|
|
28
|
-
output_component = current_actor
|
|
29
|
-
|
|
30
28
|
rendering_engine = @component_node.renderer || @media_engine || :asterisk
|
|
31
29
|
|
|
32
30
|
case rendering_engine.to_sym
|
|
@@ -46,26 +44,26 @@ module Punchblock
|
|
|
46
44
|
@call.send_progress if early
|
|
47
45
|
|
|
48
46
|
if interrupt
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
output_component = current_actor
|
|
48
|
+
call.register_handler :ami, :name => 'DTMF', [:[], 'End'] => 'Yes' do |event|
|
|
49
|
+
output_component.stop_by_redirect Punchblock::Component::Output::Complete::Success.new
|
|
51
50
|
end
|
|
52
51
|
end
|
|
53
52
|
|
|
54
53
|
opts = early ? "#{path},noanswer" : path
|
|
55
|
-
|
|
54
|
+
@call.execute_agi_command 'EXEC Playback', opts
|
|
56
55
|
when :unimrcp
|
|
57
56
|
send_ref
|
|
58
|
-
@call.
|
|
59
|
-
output_component.async.send_complete_event success_reason
|
|
60
|
-
end
|
|
57
|
+
@call.execute_agi_command 'EXEC MRCPSynth', escape_commas(escaped_doc), mrcpsynth_options
|
|
61
58
|
when :swift
|
|
62
59
|
send_ref
|
|
63
|
-
@call.
|
|
64
|
-
output_component.async.send_complete_event success_reason
|
|
65
|
-
end
|
|
60
|
+
@call.execute_agi_command 'EXEC Swift', swift_doc
|
|
66
61
|
else
|
|
67
|
-
raise OptionError,
|
|
62
|
+
raise OptionError, "The renderer #{rendering_engine} is unsupported."
|
|
68
63
|
end
|
|
64
|
+
send_success
|
|
65
|
+
rescue RubyAMI::Error => e
|
|
66
|
+
complete_with_error "Terminated due to AMI error '#{e.message}'"
|
|
69
67
|
rescue UnrenderableDocError => e
|
|
70
68
|
with_error 'unrenderable document error', e.message
|
|
71
69
|
rescue OptionError => e
|
|
@@ -90,19 +88,12 @@ module Punchblock
|
|
|
90
88
|
raise UnrenderableDocError, 'The provided document could not be rendered. See http://adhearsion.com/docs/common_problems#unrenderable-document-error for details.'
|
|
91
89
|
end
|
|
92
90
|
|
|
93
|
-
def playback(path)
|
|
94
|
-
op = current_actor
|
|
95
|
-
@call.async.send_agi_action 'EXEC Playback', path do |complete_event|
|
|
96
|
-
op.async.send_complete_event success_reason
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
91
|
def escaped_doc
|
|
101
92
|
@component_node.ssml.to_s.squish.gsub(/["\\]/) { |m| "\\#{m}" }
|
|
102
93
|
end
|
|
103
94
|
|
|
104
95
|
def escape_commas(text)
|
|
105
|
-
text.gsub(
|
|
96
|
+
text.gsub(',', '\\,')
|
|
106
97
|
end
|
|
107
98
|
|
|
108
99
|
def mrcpsynth_options
|
|
@@ -119,6 +110,10 @@ module Punchblock
|
|
|
119
110
|
doc
|
|
120
111
|
end
|
|
121
112
|
|
|
113
|
+
def send_success
|
|
114
|
+
send_complete_event success_reason
|
|
115
|
+
end
|
|
116
|
+
|
|
122
117
|
def success_reason
|
|
123
118
|
Punchblock::Component::Output::Complete::Success.new
|
|
124
119
|
end
|
|
@@ -22,7 +22,6 @@ module Punchblock
|
|
|
22
22
|
|
|
23
23
|
@format = @component_node.format || 'wav'
|
|
24
24
|
|
|
25
|
-
|
|
26
25
|
component = current_actor
|
|
27
26
|
call.register_tmp_handler :ami, :name => 'MonitorStop' do |event|
|
|
28
27
|
component.finished
|
|
@@ -31,18 +30,17 @@ module Punchblock
|
|
|
31
30
|
send_ref
|
|
32
31
|
|
|
33
32
|
if @component_node.start_beep
|
|
34
|
-
@call.
|
|
35
|
-
component.async.signal :beep_finished
|
|
36
|
-
end
|
|
37
|
-
wait :beep_finished
|
|
33
|
+
@call.execute_agi_command 'STREAM FILE', 'beep', '""'
|
|
38
34
|
end
|
|
39
35
|
|
|
40
|
-
|
|
36
|
+
ami_client.send_action 'Monitor', 'Channel' => call.channel, 'File' => filename, 'Format' => @format, 'Mix' => true
|
|
41
37
|
unless max_duration == -1
|
|
42
38
|
after max_duration/1000 do
|
|
43
|
-
|
|
39
|
+
ami_client.send_action 'StopMonitor', 'Channel' => call.channel
|
|
44
40
|
end
|
|
45
41
|
end
|
|
42
|
+
rescue RubyAMI::Error => e
|
|
43
|
+
complete_with_error "Terminated due to AMI error '#{e.message}'"
|
|
46
44
|
rescue OptionError => e
|
|
47
45
|
with_error 'option error', e.message
|
|
48
46
|
end
|
|
@@ -51,20 +49,14 @@ module Punchblock
|
|
|
51
49
|
case command
|
|
52
50
|
when Punchblock::Component::Stop
|
|
53
51
|
command.response = true
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
@complete_reason = stop_reason
|
|
57
|
-
end
|
|
52
|
+
ami_client.send_action 'StopMonitor', 'Channel' => call.channel
|
|
53
|
+
@complete_reason = stop_reason
|
|
58
54
|
when Punchblock::Component::Record::Pause
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
command.response = true
|
|
62
|
-
end
|
|
55
|
+
ami_client.send_action 'PauseMonitor', 'Channel' => call.channel
|
|
56
|
+
command.response = true
|
|
63
57
|
when Punchblock::Component::Record::Resume
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
command.response = true
|
|
67
|
-
end
|
|
58
|
+
ami_client.send_action 'ResumeMonitor', 'Channel' => call.channel
|
|
59
|
+
command.response = true
|
|
68
60
|
else
|
|
69
61
|
super
|
|
70
62
|
end
|