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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +8 -0
  4. data/lib/punchblock/component/asterisk/agi/command.rb +0 -11
  5. data/lib/punchblock/connection/asterisk.rb +3 -3
  6. data/lib/punchblock/translator/asterisk/agi_command.rb +40 -0
  7. data/lib/punchblock/translator/asterisk/call.rb +56 -47
  8. data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +13 -37
  9. data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +24 -41
  10. data/lib/punchblock/translator/asterisk/component/input.rb +2 -1
  11. data/lib/punchblock/translator/asterisk/component/output.rb +16 -21
  12. data/lib/punchblock/translator/asterisk/component/record.rb +11 -19
  13. data/lib/punchblock/translator/asterisk/component.rb +12 -9
  14. data/lib/punchblock/translator/asterisk.rb +16 -22
  15. data/lib/punchblock/translator/dtmf_recognizer.rb +4 -4
  16. data/lib/punchblock/translator/freeswitch/component/input.rb +2 -1
  17. data/lib/punchblock/translator/input_component.rb +2 -2
  18. data/lib/punchblock/version.rb +1 -1
  19. data/punchblock.gemspec +1 -1
  20. data/spec/punchblock/connection/asterisk_spec.rb +8 -7
  21. data/spec/punchblock/translator/asterisk/call_spec.rb +262 -229
  22. data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +57 -29
  23. data/spec/punchblock/translator/asterisk/component/asterisk/ami_action_spec.rb +40 -46
  24. data/spec/punchblock/translator/asterisk/component/input_spec.rb +7 -7
  25. data/spec/punchblock/translator/asterisk/component/output_spec.rb +84 -53
  26. data/spec/punchblock/translator/asterisk/component/record_spec.rb +55 -83
  27. data/spec/punchblock/translator/asterisk/component/stop_by_redirect_spec.rb +5 -1
  28. data/spec/punchblock/translator/asterisk/component_spec.rb +2 -10
  29. data/spec/punchblock/translator/asterisk_spec.rb +73 -100
  30. metadata +5 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90ceeeef84eac2fec034a32edbc3fdd387c8d1f2
4
- data.tar.gz: ca82c8ffbfa67c81369eb3146b5c4f333d443613
3
+ metadata.gz: 64729c9456c13c6897d13dbac65657a51951f67a
4
+ data.tar.gz: aa7522da6cf77ebfbbb044865e7c43b79a1939d3
5
5
  SHA512:
6
- metadata.gz: 333ac7e2473e30d84f38f23493dba4c33d4422cb4725e7c5da3fd98c468ab170156922ad5f540936e871653a34993bf57dd9e227ebcf6b9091335ab27d5a1445
7
- data.tar.gz: bb079007e70f4e27552354c8327c5d602961b7335c6a102ef51d106c2ca847d972bec2852a99f5623381c88e1c377ce88d74e93cba5b49ec4cc88719e1f24041
6
+ metadata.gz: d39f61aaa9ffb224dfa32e9bd898fadc359a9ec6881682e709ee3d0df36afff2c26854baa30e0fc99794a73c18309e8f4922c17d065cb2f5402573947dfabba1
7
+ data.tar.gz: 5c93e10769dbbd141b71732f163783bf7ff653faa30d3886f382a87c5a3402c5e6bcd8784701d7b55adbfdf1a728f3841a0093d455c1a16e10e94e161473b902
data/.travis.yml CHANGED
@@ -2,6 +2,7 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
+ - 2.0.0
5
6
  - jruby-19mode
6
7
  - rbx-19mode
7
8
  - ruby-head
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::Client.new options.merge(:event_handler => lambda { |event| translator.async.handle_ami_event event }, :logger => pb_logger)
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.start
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.stop
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
- send_agi_action "EXEC Progress"
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
- @block_commands = true
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
- send_agi_action 'EXEC RINGING' do |response|
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 do |response|
185
- command.response = true
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
- send_agi_action 'EXEC Bridge', other_call.channel
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 do |response|
194
- case response
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
- send_agi_action rejection do |response|
213
- command.response = true
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 send_agi_action(command, *params, &block)
231
- @current_agi_command = Punchblock::Component::Asterisk::AGI::Command.new :name => command, :params => params, :target_call_id => id
232
- @current_agi_command.request!
233
- @current_agi_command.register_handler :internal, Punchblock::Event::Complete do |e|
234
- block.call e if block
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, &block)
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, &block
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
- @action = create_action
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
- @call.send_ami_action @action
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
- if event['SubEvent'] == 'Exec'
25
- send_complete_event success_reason(event)
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
- end
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
- parser = RubyAMI::AGIResultParser.new event['Result']
60
- Punchblock::Component::Asterisk::AGI::Command::Complete::Success.new :code => parser.code, :result => parser.result, :data => parser.data
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 :action, :translator
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
- end
25
-
26
- def handle_response(response)
27
- case response
28
- when RubyAMI::Error
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
- @translator.send_ami_action @action
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! @extra_complete_attributes if instance_variable_defined?(:@extra_complete_attributes)
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
- return unless @action.has_causal_events?
66
- @action.events.each do |e|
67
- if e.name.downcase == @action.causal_event_terminator_name
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
- @recognizer << event['Digit']
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 = Class.new OptionError
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
- call.register_handler :ami, :name => 'DTMF' do |event|
50
- output_component.stop_by_redirect Punchblock::Component::Output::Complete::Success.new if event['End'] == 'Yes'
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
- playback opts
54
+ @call.execute_agi_command 'EXEC Playback', opts
56
55
  when :unimrcp
57
56
  send_ref
58
- @call.async.send_agi_action 'EXEC MRCPSynth', escape_commas(escaped_doc), mrcpsynth_options do |complete_event|
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.async.send_agi_action 'EXEC Swift', swift_doc do |complete_event|
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, 'The renderer foobar is unsupported.'
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.async.send_agi_action 'STREAM FILE', 'beep', '""' do
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
- call.async.send_ami_action 'Monitor', 'Channel' => call.channel, 'File' => filename, 'Format' => @format, 'Mix' => true
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
- call.async.send_ami_action 'StopMonitor', 'Channel' => call.channel
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
- a = current_actor
55
- call.async.send_ami_action 'StopMonitor', 'Channel' => call.channel do |complete_event|
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
- a = current_actor
60
- call.async.send_ami_action 'PauseMonitor', 'Channel' => call.channel do |complete_event|
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
- a = current_actor
65
- call.async.send_ami_action 'ResumeMonitor', 'Channel' => call.channel do |complete_event|
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