punchblock 1.8.2 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|