adhearsion 2.6.4 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -9
  3. data/CHANGELOG.md +22 -7
  4. data/Gemfile +2 -0
  5. data/README.markdown +4 -5
  6. data/Rakefile +1 -2
  7. data/adhearsion.gemspec +19 -8
  8. data/features/cli_create.feature +19 -3
  9. data/features/step_definitions/cli_steps.rb +0 -11
  10. data/features/support/env.rb +3 -4
  11. data/lib/adhearsion.rb +48 -27
  12. data/lib/adhearsion/call.rb +34 -50
  13. data/lib/adhearsion/call_controller.rb +6 -12
  14. data/lib/adhearsion/call_controller/dial.rb +15 -53
  15. data/lib/adhearsion/call_controller/input.rb +39 -162
  16. data/lib/adhearsion/call_controller/input/ask_grammar_builder.rb +44 -0
  17. data/lib/adhearsion/call_controller/input/menu_builder.rb +136 -0
  18. data/lib/adhearsion/call_controller/input/prompt_builder.rb +78 -0
  19. data/lib/adhearsion/call_controller/input/result.rb +46 -0
  20. data/lib/adhearsion/call_controller/output.rb +48 -67
  21. data/lib/adhearsion/call_controller/output/abstract_player.rb +3 -3
  22. data/lib/adhearsion/call_controller/output/async_player.rb +3 -3
  23. data/lib/adhearsion/call_controller/output/player.rb +1 -1
  24. data/lib/adhearsion/call_controller/record.rb +23 -8
  25. data/lib/adhearsion/calls.rb +1 -1
  26. data/lib/adhearsion/cli_commands/ahn_command.rb +2 -65
  27. data/lib/adhearsion/cli_commands/thor_errors.rb +0 -6
  28. data/lib/adhearsion/configuration.rb +91 -39
  29. data/lib/adhearsion/core_ext/blather/stanza.rb +41 -0
  30. data/lib/adhearsion/core_ext/blather/stanza/presence.rb +13 -0
  31. data/lib/adhearsion/error.rb +5 -0
  32. data/lib/adhearsion/event.rb +21 -0
  33. data/lib/adhearsion/event/active_speaker.rb +11 -0
  34. data/lib/adhearsion/event/answered.rb +11 -0
  35. data/lib/adhearsion/event/asterisk.rb +10 -0
  36. data/lib/adhearsion/event/asterisk/ami.rb +34 -0
  37. data/lib/adhearsion/event/complete.rb +75 -0
  38. data/lib/adhearsion/event/dtmf.rb +11 -0
  39. data/lib/adhearsion/event/end.rb +22 -0
  40. data/lib/adhearsion/event/input_timers_started.rb +9 -0
  41. data/lib/adhearsion/event/joined.rb +17 -0
  42. data/lib/adhearsion/event/offer.rb +14 -0
  43. data/lib/adhearsion/event/ringing.rb +11 -0
  44. data/lib/adhearsion/event/started_speaking.rb +13 -0
  45. data/lib/adhearsion/event/stopped_speaking.rb +13 -0
  46. data/lib/adhearsion/event/unjoined.rb +17 -0
  47. data/lib/adhearsion/events.rb +47 -66
  48. data/lib/adhearsion/foundation.rb +0 -1
  49. data/lib/adhearsion/foundation/object.rb +0 -5
  50. data/lib/adhearsion/generators/app/app_generator.rb +4 -1
  51. data/lib/adhearsion/generators/app/templates/Gemfile.erb +2 -10
  52. data/lib/adhearsion/generators/app/templates/adhearsion.erb +9 -9
  53. data/lib/adhearsion/generators/app/templates/config.ru +7 -0
  54. data/lib/adhearsion/generators/app/templates/en.yml +4 -0
  55. data/lib/adhearsion/generators/app/templates/events.erb +2 -2
  56. data/lib/adhearsion/generators/app/templates/hello_world.wav +0 -0
  57. data/lib/adhearsion/generators/app/templates/simon_game.rb +2 -1
  58. data/lib/adhearsion/generators/app/templates/simon_game_spec.rb +2 -2
  59. data/lib/adhearsion/has_headers.rb +34 -0
  60. data/lib/adhearsion/http_server.rb +37 -0
  61. data/lib/adhearsion/initializer.rb +19 -153
  62. data/lib/adhearsion/logging.rb +6 -25
  63. data/lib/adhearsion/outbound_call.rb +5 -5
  64. data/lib/adhearsion/plugin.rb +1 -0
  65. data/lib/adhearsion/protocol_error.rb +26 -0
  66. data/lib/adhearsion/rayo.rb +30 -0
  67. data/lib/adhearsion/rayo/client.rb +62 -0
  68. data/lib/adhearsion/rayo/client/component_registry.rb +33 -0
  69. data/lib/adhearsion/rayo/command.rb +21 -0
  70. data/lib/adhearsion/rayo/command/accept.rb +16 -0
  71. data/lib/adhearsion/rayo/command/answer.rb +16 -0
  72. data/lib/adhearsion/rayo/command/dial.rb +57 -0
  73. data/lib/adhearsion/rayo/command/hangup.rb +16 -0
  74. data/lib/adhearsion/rayo/command/join.rb +43 -0
  75. data/lib/adhearsion/rayo/command/mute.rb +13 -0
  76. data/lib/adhearsion/rayo/command/redirect.rb +23 -0
  77. data/lib/adhearsion/rayo/command/reject.rb +40 -0
  78. data/lib/adhearsion/rayo/command/unjoin.rb +24 -0
  79. data/lib/adhearsion/rayo/command/unmute.rb +13 -0
  80. data/lib/adhearsion/rayo/command_node.rb +47 -0
  81. data/lib/adhearsion/rayo/component.rb +21 -0
  82. data/lib/adhearsion/rayo/component/asterisk.rb +13 -0
  83. data/lib/adhearsion/rayo/component/asterisk/agi.rb +14 -0
  84. data/lib/adhearsion/rayo/component/asterisk/agi/command.rb +46 -0
  85. data/lib/adhearsion/rayo/component/asterisk/ami.rb +14 -0
  86. data/lib/adhearsion/rayo/component/asterisk/ami/action.rb +61 -0
  87. data/lib/adhearsion/rayo/component/component_node.rb +90 -0
  88. data/lib/adhearsion/rayo/component/input.rb +186 -0
  89. data/lib/adhearsion/rayo/component/output.rb +471 -0
  90. data/lib/adhearsion/rayo/component/prompt.rb +53 -0
  91. data/lib/adhearsion/rayo/component/receive_fax.rb +26 -0
  92. data/lib/adhearsion/rayo/component/record.rb +165 -0
  93. data/lib/adhearsion/rayo/component/send_fax.rb +64 -0
  94. data/lib/adhearsion/rayo/component/stop.rb +11 -0
  95. data/lib/adhearsion/rayo/connection.rb +12 -0
  96. data/lib/adhearsion/rayo/connection/asterisk.rb +74 -0
  97. data/lib/adhearsion/rayo/connection/connected.rb +22 -0
  98. data/lib/adhearsion/rayo/connection/generic_connection.rb +22 -0
  99. data/lib/adhearsion/rayo/connection/xmpp.rb +198 -0
  100. data/lib/adhearsion/rayo/disconnected_error.rb +22 -0
  101. data/lib/adhearsion/{punchblock_plugin → rayo}/initializer.rb +19 -19
  102. data/lib/adhearsion/rayo/rayo_node.rb +127 -0
  103. data/lib/adhearsion/rayo/ref.rb +57 -0
  104. data/lib/adhearsion/statistics.rb +1 -1
  105. data/lib/adhearsion/tasks.rb +1 -2
  106. data/lib/adhearsion/tasks/configuration.rb +1 -1
  107. data/lib/adhearsion/tasks/environment.rb +0 -2
  108. data/lib/adhearsion/tasks/i18n.rb +49 -0
  109. data/lib/adhearsion/translator.rb +11 -0
  110. data/lib/adhearsion/translator/asterisk.rb +234 -0
  111. data/lib/adhearsion/translator/asterisk/agi_app.rb +17 -0
  112. data/lib/adhearsion/translator/asterisk/agi_command.rb +45 -0
  113. data/lib/adhearsion/translator/asterisk/ami_error_converter.rb +20 -0
  114. data/lib/adhearsion/translator/asterisk/call.rb +416 -0
  115. data/lib/adhearsion/translator/asterisk/channel.rb +43 -0
  116. data/lib/adhearsion/translator/asterisk/component.rb +88 -0
  117. data/lib/adhearsion/translator/asterisk/component/asterisk.rb +15 -0
  118. data/lib/adhearsion/translator/asterisk/component/asterisk/agi_command.rb +42 -0
  119. data/lib/adhearsion/translator/asterisk/component/asterisk/ami_action.rb +68 -0
  120. data/lib/adhearsion/translator/asterisk/component/composed_prompt.rb +76 -0
  121. data/lib/adhearsion/translator/asterisk/component/dtmf_recognizer.rb +137 -0
  122. data/lib/adhearsion/translator/asterisk/component/input.rb +34 -0
  123. data/lib/adhearsion/translator/asterisk/component/input_component.rb +90 -0
  124. data/lib/adhearsion/translator/asterisk/component/mrcp_native_prompt.rb +71 -0
  125. data/lib/adhearsion/translator/asterisk/component/mrcp_prompt.rb +55 -0
  126. data/lib/adhearsion/translator/asterisk/component/mrcp_recog_prompt.rb +165 -0
  127. data/lib/adhearsion/translator/asterisk/component/output.rb +233 -0
  128. data/lib/adhearsion/translator/asterisk/component/record.rb +101 -0
  129. data/lib/adhearsion/translator/asterisk/component/stop_by_redirect.rb +30 -0
  130. data/lib/adhearsion/translator/asterisk/unimrcp_app.rb +28 -0
  131. data/lib/adhearsion/uri_list.rb +21 -0
  132. data/lib/adhearsion/version.rb +1 -1
  133. data/spec/adhearsion/call_controller/dial_spec.rb +79 -1420
  134. data/spec/adhearsion/call_controller/input_spec.rb +1141 -237
  135. data/spec/adhearsion/call_controller/output/async_player_spec.rb +10 -10
  136. data/spec/adhearsion/call_controller/output/player_spec.rb +8 -8
  137. data/spec/adhearsion/call_controller/output_spec.rb +162 -215
  138. data/spec/adhearsion/call_controller/record_spec.rb +15 -16
  139. data/spec/adhearsion/call_controller_spec.rb +23 -40
  140. data/spec/adhearsion/call_spec.rb +123 -129
  141. data/spec/adhearsion/calls_spec.rb +3 -3
  142. data/spec/adhearsion/configuration_spec.rb +94 -108
  143. data/spec/adhearsion/event/answered_spec.rb +50 -0
  144. data/spec/adhearsion/event/asterisk/Find Results +402 -0
  145. data/spec/adhearsion/event/asterisk/ami_spec.rb +81 -0
  146. data/spec/adhearsion/event/complete_spec.rb +176 -0
  147. data/spec/adhearsion/event/dtmf_spec.rb +35 -0
  148. data/spec/adhearsion/event/end_spec.rb +85 -0
  149. data/spec/adhearsion/event/input_timers_started_spec.rb +19 -0
  150. data/spec/adhearsion/event/joined_spec.rb +53 -0
  151. data/spec/adhearsion/event/offer_spec.rb +106 -0
  152. data/spec/adhearsion/event/ringing_spec.rb +50 -0
  153. data/spec/adhearsion/event/started_speaking_spec.rb +37 -0
  154. data/spec/adhearsion/event/stopped_speaking_spec.rb +37 -0
  155. data/spec/adhearsion/event/unjoined_spec.rb +48 -0
  156. data/spec/adhearsion/event/untitled +0 -0
  157. data/spec/adhearsion/events_spec.rb +19 -45
  158. data/spec/adhearsion/initializer_spec.rb +12 -184
  159. data/spec/adhearsion/logging_spec.rb +5 -20
  160. data/spec/adhearsion/outbound_call_spec.rb +13 -13
  161. data/spec/adhearsion/plugin_spec.rb +3 -4
  162. data/spec/adhearsion/protocol_error_spec.rb +91 -0
  163. data/spec/adhearsion/rayo/client/component_registry_spec.rb +26 -0
  164. data/spec/adhearsion/rayo/client_spec.rb +134 -0
  165. data/spec/adhearsion/rayo/command/accept_spec.rb +63 -0
  166. data/spec/adhearsion/rayo/command/answer_spec.rb +73 -0
  167. data/spec/adhearsion/rayo/command/dial_spec.rb +156 -0
  168. data/spec/adhearsion/rayo/command/hangup_spec.rb +63 -0
  169. data/spec/adhearsion/rayo/command/join_spec.rb +158 -0
  170. data/spec/adhearsion/rayo/command/mute_spec.rb +32 -0
  171. data/spec/adhearsion/rayo/command/redirect_spec.rb +89 -0
  172. data/spec/adhearsion/rayo/command/reject_spec.rb +117 -0
  173. data/spec/adhearsion/rayo/command/unjoin_spec.rb +82 -0
  174. data/spec/adhearsion/rayo/command/unmute_spec.rb +32 -0
  175. data/spec/adhearsion/rayo/command_node_spec.rb +101 -0
  176. data/spec/adhearsion/rayo/component/asterisk/agi/command_spec.rb +111 -0
  177. data/spec/adhearsion/rayo/component/asterisk/ami/action_spec.rb +173 -0
  178. data/spec/adhearsion/rayo/component/component_node_spec.rb +110 -0
  179. data/spec/adhearsion/rayo/component/input_spec.rb +715 -0
  180. data/spec/adhearsion/rayo/component/output_spec.rb +1030 -0
  181. data/spec/adhearsion/rayo/component/prompt_spec.rb +171 -0
  182. data/spec/adhearsion/rayo/component/receive_fax_spec.rb +136 -0
  183. data/spec/adhearsion/rayo/component/record_spec.rb +497 -0
  184. data/spec/adhearsion/rayo/component/send_fax_spec.rb +144 -0
  185. data/spec/adhearsion/rayo/connection/asterisk_spec.rb +118 -0
  186. data/spec/adhearsion/rayo/connection/xmpp_spec.rb +449 -0
  187. data/spec/adhearsion/rayo/initializer_spec.rb +353 -0
  188. data/spec/adhearsion/rayo/ref_spec.rb +168 -0
  189. data/spec/adhearsion/rayo_spec.rb +7 -0
  190. data/spec/adhearsion/router/route_spec.rb +1 -1
  191. data/spec/adhearsion/statistics_spec.rb +2 -5
  192. data/spec/adhearsion/translator/asterisk/call_spec.rb +2047 -0
  193. data/spec/adhearsion/translator/asterisk/component/asterisk/agi_command_spec.rb +256 -0
  194. data/spec/adhearsion/translator/asterisk/component/asterisk/ami_action_spec.rb +151 -0
  195. data/spec/adhearsion/translator/asterisk/component/composed_prompt_spec.rb +257 -0
  196. data/spec/adhearsion/translator/asterisk/component/input_spec.rb +571 -0
  197. data/spec/adhearsion/translator/asterisk/component/mrcp_native_prompt_spec.rb +774 -0
  198. data/spec/adhearsion/translator/asterisk/component/mrcp_prompt_spec.rb +1244 -0
  199. data/spec/adhearsion/translator/asterisk/component/output_spec.rb +1850 -0
  200. data/spec/adhearsion/translator/asterisk/component/record_spec.rb +426 -0
  201. data/spec/adhearsion/translator/asterisk/component/stop_by_redirect_spec.rb +62 -0
  202. data/spec/adhearsion/translator/asterisk/component_spec.rb +83 -0
  203. data/spec/adhearsion/translator/asterisk_spec.rb +685 -0
  204. data/spec/adhearsion/uri_list_spec.rb +88 -0
  205. data/spec/adhearsion_spec.rb +89 -14
  206. data/spec/fixtures/locale/en.yml +13 -0
  207. data/spec/fixtures/locale/it.yml +13 -0
  208. data/spec/spec_helper.rb +18 -5
  209. data/spec/support/call_controller_test_helpers.rb +3 -2
  210. data/spec/support/initializer_stubs.rb +3 -1
  211. data/spec/support/punchblock_examples.rb +65 -0
  212. data/spec/support/punchblock_mocks.rb +12 -0
  213. metadata +412 -70
  214. data/features/cli_daemon.feature +0 -20
  215. data/features/cli_restart.feature +0 -52
  216. data/features/cli_stop.feature +0 -50
  217. data/lib/adhearsion/call_controller/menu_dsl.rb +0 -21
  218. data/lib/adhearsion/call_controller/menu_dsl/array_match_calculator.rb +0 -26
  219. data/lib/adhearsion/call_controller/menu_dsl/calculated_match.rb +0 -43
  220. data/lib/adhearsion/call_controller/menu_dsl/calculated_match_collection.rb +0 -45
  221. data/lib/adhearsion/call_controller/menu_dsl/fixnum_match_calculator.rb +0 -11
  222. data/lib/adhearsion/call_controller/menu_dsl/match_calculator.rb +0 -40
  223. data/lib/adhearsion/call_controller/menu_dsl/menu.rb +0 -207
  224. data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +0 -92
  225. data/lib/adhearsion/call_controller/menu_dsl/range_match_calculator.rb +0 -60
  226. data/lib/adhearsion/call_controller/menu_dsl/string_match_calculator.rb +0 -25
  227. data/lib/adhearsion/call_controller/utility.rb +0 -77
  228. data/lib/adhearsion/foundation/custom_daemonizer.rb +0 -52
  229. data/lib/adhearsion/punchblock_plugin.rb +0 -63
  230. data/scripts/cloc-1.64.pl +0 -10483
  231. data/spec/adhearsion/call_controller/menu_dsl/array_match_calculator_spec.rb +0 -76
  232. data/spec/adhearsion/call_controller/menu_dsl/calculated_match_collection_spec.rb +0 -60
  233. data/spec/adhearsion/call_controller/menu_dsl/calculated_match_spec.rb +0 -61
  234. data/spec/adhearsion/call_controller/menu_dsl/fixnum_match_calculator_spec.rb +0 -39
  235. data/spec/adhearsion/call_controller/menu_dsl/match_calculator_spec.rb +0 -17
  236. data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +0 -165
  237. data/spec/adhearsion/call_controller/menu_dsl/menu_spec.rb +0 -420
  238. data/spec/adhearsion/call_controller/menu_dsl/range_match_calculator_spec.rb +0 -32
  239. data/spec/adhearsion/call_controller/menu_dsl/string_match_calculator_spec.rb +0 -40
  240. data/spec/adhearsion/call_controller/utility_spec.rb +0 -90
  241. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +0 -356
  242. data/spec/adhearsion/punchblock_plugin_spec.rb +0 -59
@@ -17,7 +17,7 @@ module Adhearsion
17
17
  ExpiredError = Class.new Celluloid::DeadActorError
18
18
 
19
19
  # @private
20
- class ActorProxy < Celluloid::ActorProxy
20
+ class ActorProxy < Celluloid::CellProxy
21
21
  def method_missing(meth, *args, &block)
22
22
  super(meth, *args, &block)
23
23
  rescue ::Celluloid::DeadActorError
@@ -89,7 +89,6 @@ module Adhearsion
89
89
  @duration = nil
90
90
  @auto_hangup = true
91
91
  @after_hangup_lifetime = nil
92
- @call_terminating = false
93
92
 
94
93
  self << offer if offer
95
94
  end
@@ -161,14 +160,18 @@ module Adhearsion
161
160
 
162
161
  #
163
162
  # Wait for the call to end. Returns immediately if the call has already ended, else blocks until it does so.
163
+ # @param [Integer, nil] timeout a timeout after which to unblock, returning `:timeout`
164
164
  # @return [Symbol] the reason for the call ending
165
+ # @raises [Celluloid::ConditionError] in case of a specified timeout expiring
165
166
  #
166
- def wait_for_end
167
+ def wait_for_end(timeout = nil)
167
168
  if end_reason
168
169
  end_reason
169
170
  else
170
- @end_blocker.wait
171
+ @end_blocker.wait(timeout)
171
172
  end
173
+ rescue Celluloid::ConditionError => e
174
+ abort e
172
175
  end
173
176
 
174
177
  #
@@ -200,22 +203,16 @@ module Adhearsion
200
203
 
201
204
  # @private
202
205
  def register_initial_handlers
203
- register_event_handler Punchblock::Event::Offer do |offer|
206
+ register_event_handler Adhearsion::Event::Offer do |offer|
204
207
  @offer = offer
205
208
  @client = offer.client
206
209
  @start_time = offer.timestamp.to_time
207
210
  end
208
211
 
209
- register_event_handler Punchblock::HasHeaders do |event|
212
+ register_event_handler Adhearsion::HasHeaders do |event|
210
213
  merge_headers event.headers
211
214
  end
212
215
 
213
- register_event_handler Punchblock::Event::Complete do |event|
214
- if event.reason.is_a? Punchblock::Event::Complete::Hangup
215
- terminating! unless terminating?
216
- end
217
- end
218
-
219
216
  on_joined do |event|
220
217
  if event.call_uri
221
218
  target = event.call_uri
@@ -245,7 +242,6 @@ module Adhearsion
245
242
 
246
243
  on_end do |event|
247
244
  logger.info "Call #{from} -> #{to} ended due to #{event.reason}#{" (code #{event.platform_code})" if event.platform_code}"
248
- terminating! unless terminating?
249
245
  @end_time = event.timestamp.to_time
250
246
  @duration = @end_time - @start_time if @start_time
251
247
  clear_from_active_calls
@@ -253,7 +249,7 @@ module Adhearsion
253
249
  @end_code = event.platform_code
254
250
  @end_blocker.broadcast event.reason
255
251
  @commands.terminate
256
- after(@after_hangup_lifetime || Adhearsion.config.platform.after_hangup_lifetime) { terminate }
252
+ after(@after_hangup_lifetime || Adhearsion.config.core.after_hangup_lifetime) { terminate }
257
253
  end
258
254
  end
259
255
 
@@ -276,7 +272,7 @@ module Adhearsion
276
272
  # @option target [String] mixer_name The mixer name to guard on
277
273
  #
278
274
  def on_joined(target = nil, &block)
279
- register_event_handler Punchblock::Event::Joined, *guards_for_target(target) do |event|
275
+ register_event_handler Adhearsion::Event::Joined, *guards_for_target(target) do |event|
280
276
  block.call event
281
277
  end
282
278
  end
@@ -289,7 +285,7 @@ module Adhearsion
289
285
  # @option target [String] mixer_name The mixer name to guard on
290
286
  #
291
287
  def on_unjoined(target = nil, &block)
292
- register_event_handler Punchblock::Event::Unjoined, *guards_for_target(target), &block
288
+ register_event_handler Adhearsion::Event::Unjoined, *guards_for_target(target), &block
293
289
  end
294
290
 
295
291
  # @private
@@ -298,19 +294,7 @@ module Adhearsion
298
294
  end
299
295
 
300
296
  def on_end(&block)
301
- register_event_handler Punchblock::Event::End, &block
302
- end
303
-
304
- def terminating!
305
- logger.debug "Call is terminating"
306
- @call_terminating = true
307
- end
308
-
309
- #
310
- # @return [Boolean] if the call is currently terminating/terminated
311
- #
312
- def terminating?
313
- !active? || @call_terminating
297
+ register_event_handler Adhearsion::Event::End, &block
314
298
  end
315
299
 
316
300
  #
@@ -321,21 +305,21 @@ module Adhearsion
321
305
  end
322
306
 
323
307
  def accept(headers = nil)
324
- @accept_command ||= write_and_await_response Punchblock::Command::Accept.new(:headers => headers)
325
- rescue Punchblock::ProtocolError => e
308
+ @accept_command ||= write_and_await_response Adhearsion::Rayo::Command::Accept.new(:headers => headers)
309
+ rescue Adhearsion::ProtocolError => e
326
310
  abort e
327
311
  end
328
312
 
329
313
  def answer(headers = nil)
330
- write_and_await_response Punchblock::Command::Answer.new(:headers => headers)
331
- rescue Punchblock::ProtocolError => e
314
+ write_and_await_response Adhearsion::Rayo::Command::Answer.new(:headers => headers)
315
+ rescue Adhearsion::ProtocolError => e
332
316
  abort e
333
317
  end
334
318
 
335
319
  def reject(reason = :busy, headers = nil)
336
- write_and_await_response Punchblock::Command::Reject.new(:reason => reason, :headers => headers)
320
+ write_and_await_response Adhearsion::Rayo::Command::Reject.new(:reason => reason, :headers => headers)
337
321
  Adhearsion::Events.trigger_immediately :call_rejected, call: current_actor, reason: reason
338
- rescue Punchblock::ProtocolError => e
322
+ rescue Adhearsion::ProtocolError => e
339
323
  abort e
340
324
  end
341
325
 
@@ -354,8 +338,8 @@ module Adhearsion
354
338
  # @param [String] to the target to redirect to, eg a SIP URI
355
339
  # @param [Hash, optional] headers a set of headers to send along with the redirect instruction
356
340
  def redirect(to, headers = nil)
357
- write_and_await_response Punchblock::Command::Redirect.new(to: to, headers: headers)
358
- rescue Punchblock::ProtocolError => e
341
+ write_and_await_response Adhearsion::Rayo::Command::Redirect.new(to: to, headers: headers)
342
+ rescue Adhearsion::ProtocolError => e
359
343
  abort e
360
344
  end
361
345
 
@@ -363,8 +347,8 @@ module Adhearsion
363
347
  return false unless active?
364
348
  logger.info "Hanging up"
365
349
  @end_reason = true
366
- write_and_await_response Punchblock::Command::Hangup.new(:headers => headers)
367
- rescue Punchblock::ProtocolError => e
350
+ write_and_await_response Adhearsion::Rayo::Command::Hangup.new(:headers => headers)
351
+ rescue Adhearsion::ProtocolError => e
368
352
  abort e
369
353
  end
370
354
 
@@ -401,10 +385,10 @@ module Adhearsion
401
385
  unjoined_condition.countdown!
402
386
  end
403
387
 
404
- command = Punchblock::Command::Join.new options.merge(join_options_with_target(target))
388
+ command = Adhearsion::Rayo::Command::Join.new options.merge(join_options_with_target(target))
405
389
  write_and_await_response command
406
390
  {command: command, joined_condition: joined_condition, unjoined_condition: unjoined_condition}
407
- rescue Punchblock::ProtocolError => e
391
+ rescue Adhearsion::ProtocolError => e
408
392
  abort e
409
393
  end
410
394
 
@@ -417,9 +401,9 @@ module Adhearsion
417
401
  #
418
402
  def unjoin(target = nil)
419
403
  logger.info "Unjoining from #{target}"
420
- command = Punchblock::Command::Unjoin.new join_options_with_target(target)
404
+ command = Adhearsion::Rayo::Command::Unjoin.new join_options_with_target(target)
421
405
  write_and_await_response command
422
- rescue Punchblock::ProtocolError => e
406
+ rescue Adhearsion::ProtocolError => e
423
407
  abort e
424
408
  end
425
409
 
@@ -462,14 +446,14 @@ module Adhearsion
462
446
  end
463
447
 
464
448
  def mute
465
- write_and_await_response Punchblock::Command::Mute.new
466
- rescue Punchblock::ProtocolError => e
449
+ write_and_await_response Adhearsion::Rayo::Command::Mute.new
450
+ rescue Adhearsion::ProtocolError => e
467
451
  abort e
468
452
  end
469
453
 
470
454
  def unmute
471
- write_and_await_response Punchblock::Command::Unmute.new
472
- rescue Punchblock::ProtocolError => e
455
+ write_and_await_response Adhearsion::Rayo::Command::Unmute.new
456
+ rescue Adhearsion::ProtocolError => e
473
457
  abort e
474
458
  end
475
459
 
@@ -482,7 +466,7 @@ module Adhearsion
482
466
 
483
467
  response = defer { command.response timeout }
484
468
  case response
485
- when Punchblock::ProtocolError
469
+ when Adhearsion::ProtocolError
486
470
  if response.name == :item_not_found
487
471
  error_handler[Hangup.new(@end_reason)]
488
472
  else
@@ -501,10 +485,10 @@ module Adhearsion
501
485
 
502
486
  # @private
503
487
  def write_command(command)
504
- abort Hangup.new(@end_reason) unless active? || command.is_a?(Punchblock::Command::Hangup)
488
+ abort Hangup.new(@end_reason) unless active? || command.is_a?(Adhearsion::Rayo::Command::Hangup)
505
489
  merge_headers command.headers if command.respond_to? :headers
506
490
  logger.debug "Executing command #{command.inspect}"
507
- unless command.is_a?(Punchblock::Command::Dial)
491
+ unless command.is_a?(Adhearsion::Rayo::Command::Dial)
508
492
  command.target_call_id = id
509
493
  command.domain = domain
510
494
  end
@@ -1,13 +1,12 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'countdownlatch'
4
+
4
5
  %w(
5
6
  dial
6
7
  input
7
- menu_dsl
8
8
  output
9
9
  record
10
- utility
11
10
  ).each { |r| require "adhearsion/call_controller/#{r}" }
12
11
 
13
12
  module Adhearsion
@@ -16,7 +15,6 @@ module Adhearsion
16
15
  include Input
17
16
  include Output
18
17
  include Record
19
- include Utility
20
18
 
21
19
  class_attribute :callbacks
22
20
 
@@ -99,7 +97,7 @@ module Adhearsion
99
97
  end
100
98
 
101
99
  def bg_exec(completion_callback = nil)
102
- Celluloid::ThreadHandle.new do
100
+ Celluloid::ThreadHandle.new(Celluloid.actor_system) do
103
101
  catching_standard_errors do
104
102
  exec_with_callback completion_callback
105
103
  end
@@ -164,7 +162,7 @@ module Adhearsion
164
162
  @active_components.each do |component|
165
163
  begin
166
164
  component.stop!
167
- rescue Punchblock::Component::InvalidActionError
165
+ rescue Adhearsion::Rayo::Component::InvalidActionError
168
166
  end
169
167
  end
170
168
  end
@@ -205,8 +203,8 @@ module Adhearsion
205
203
  def write_and_await_response(command)
206
204
  block_until_resumed
207
205
  call.write_and_await_response command
208
- if command.is_a?(Punchblock::Component::ComponentNode)
209
- command.register_event_handler Punchblock::Event::Complete do |event|
206
+ if command.is_a?(Adhearsion::Rayo::Component::ComponentNode)
207
+ command.register_event_handler Adhearsion::Event::Complete do |event|
210
208
  @active_components.delete command
211
209
  throw :pass
212
210
  end
@@ -221,11 +219,7 @@ module Adhearsion
221
219
  yield component if block_given?
222
220
 
223
221
  complete_event = component.complete_event
224
- raise Adhearsion::Error, [complete_event.reason.details, component.inspect].join(": ") if complete_event.reason.is_a? Punchblock::Event::Complete::Error
225
- if complete_event.reason.is_a? Punchblock::Event::Complete::Hangup
226
- call.terminating! unless call.terminating?
227
- raise Call::Hangup
228
- end
222
+ raise Adhearsion::Error, [complete_event.reason.details, component.inspect].join(": ") if complete_event.reason.is_a? Adhearsion::Event::Complete::Error
229
223
  component
230
224
  end
231
225
 
@@ -33,9 +33,11 @@ module Adhearsion
33
33
  # @option options [Numeric] :for this option can be thought of best as a timeout.
34
34
  # i.e. timeout after :for if no one answers the call
35
35
  #
36
- # @option options [CallController] :confirm the controller to execute on the first outbound call to be answered, to give an opportunity to screen the call. The calls will be joined if the outbound call is still active after this controller completes.
36
+ # @option options [CallController] :confirm Confirmation controller to execute. Confirmation will be attempted on all answered calls, and calls will be allowed to progress through confirmation in parallel. The first to complete confirmation will be joined to the A-leg, with the others being hung up.
37
37
  # @option options [Hash] :confirm_metadata Metadata to set on the confirmation controller before executing it. This is shared between all calls if dialing multiple endpoints; if you care about it being mutated, you should provide an immutable value (using eg https://github.com/harukizaemon/hamster).
38
38
  #
39
+ # @option options [CallController] :apology controller to execute on calls which lose the race to complete confirmation before they are hung up
40
+ #
39
41
  # @option options [CallController] :cleanup The controller to execute on each call being cleaned up. This can be used, for instance, to notify that the call is being terminated. Calls are terminated right after this controller completes execution. If this is not specified, calls are silently terminated during cleanup.
40
42
  # @option options [Hash] :cleanup_metadata Metadata to set on the cleanup controller before executing it. Defaults to :confirm_metadata if not specified.
41
43
  #
@@ -68,23 +70,6 @@ module Adhearsion
68
70
  catching_standard_errors { dial.delete_logger if dial }
69
71
  end
70
72
 
71
- # Dial one or more third parties and join one to this call after execution of a confirmation controller.
72
- # Confirmation will be attempted on all answered calls, and calls will be allowed to progress through confirmation in parallel. The first to complete confirmation will be joined to the A-leg, with the others being hung up.
73
- #
74
- # @option options [CallController] :apology controller to execute on calls which lose the race to complete confirmation before they are hung up
75
- #
76
- # @see #dial
77
- def dial_and_confirm(to, options = {})
78
- dial = ParallelConfirmationDial.new to, options, call
79
- dial.run(self)
80
- dial.await_completion
81
- dial.terminate_ringback
82
- dial.cleanup_calls
83
- dial.status
84
- ensure
85
- catching_standard_errors { dial.delete_logger if dial }
86
- end
87
-
88
73
  class Dial
89
74
  attr_accessor :status
90
75
 
@@ -163,8 +148,6 @@ module Adhearsion
163
148
  end
164
149
 
165
150
  new_call.on_answer do |event|
166
- pre_confirmation_tasks new_call
167
-
168
151
  new_call.on_joined @call do |joined|
169
152
  join_status.started joined.timestamp.to_time
170
153
  end
@@ -377,6 +360,8 @@ module Adhearsion
377
360
  @confirmation_controller = @options.delete :confirm
378
361
  @confirmation_metadata = @options.delete :confirm_metadata
379
362
 
363
+ @apology_controller = @options.delete :apology
364
+
380
365
  @pre_join = @options.delete :pre_join
381
366
  @ringback = @options.delete :ringback
382
367
 
@@ -388,16 +373,18 @@ module Adhearsion
388
373
  @skip_cleanup = false
389
374
  end
390
375
 
391
- def pre_confirmation_tasks(call)
392
- on_all_except call do |target_call|
393
- logger.info "#dial hanging up call #{target_call.id} because this call has been answered by another channel"
394
- target_call.hangup
395
- end
396
- end
397
-
398
376
  def pre_join_tasks(call)
399
377
  @pre_join.call(call) if @pre_join
400
378
  terminate_ringback
379
+ on_all_except call do |target_call|
380
+ if @apology_controller
381
+ logger.info "#dial executing apology controller #{@apology_controller} on call #{target_call.id} because this call has been confirmed by another channel"
382
+ target_call.async.execute_controller @apology_controller.new(target_call, @confirmation_metadata), ->(call) { call.hangup }
383
+ else
384
+ logger.info "#dial hanging up call #{target_call.id} because this call has been confirmed by another channel"
385
+ target_call.hangup
386
+ end
387
+ end
401
388
  end
402
389
 
403
390
  def on_all_except(call)
@@ -411,7 +398,7 @@ module Adhearsion
411
398
 
412
399
  def ignoring_missing_joins
413
400
  yield
414
- rescue Punchblock::ProtocolError => e
401
+ rescue Adhearsion::ProtocolError => e
415
402
  raise unless e.name == :service_unavailable
416
403
  end
417
404
 
@@ -422,31 +409,6 @@ module Adhearsion
422
409
  end
423
410
  end
424
411
 
425
- class ParallelConfirmationDial < Dial
426
- private
427
-
428
- def set_defaults
429
- super
430
- @apology_controller = @options.delete :apology
431
- end
432
-
433
- def pre_confirmation_tasks(call)
434
- end
435
-
436
- def pre_join_tasks(call)
437
- super
438
- on_all_except call do |target_call|
439
- if @apology_controller
440
- logger.info "#dial executing apology controller #{@apology_controller} on call #{target_call.id} because this call has been confirmed by another channel"
441
- target_call.async.execute_controller @apology_controller.new(target_call, @confirmation_metadata), ->(call) { call.hangup }
442
- else
443
- logger.info "#dial hanging up call #{target_call.id} because this call has been confirmed by another channel"
444
- target_call.hangup
445
- end
446
- end
447
- end
448
- end
449
-
450
412
  class DialStatus
451
413
  # The collection of calls created during the dial operation
452
414
  attr_accessor :calls
@@ -1,90 +1,60 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'adhearsion/call_controller/input/ask_grammar_builder'
4
+ require 'adhearsion/call_controller/input/prompt_builder'
5
+ require 'adhearsion/call_controller/input/menu_builder'
6
+
3
7
  module Adhearsion
4
8
  class CallController
5
9
  module Input
6
10
 
7
- Result = Struct.new(:response, :status, :menu) do
8
- def to_s
9
- response
10
- end
11
-
12
- def inspect
13
- "#<Adhearsion::CallController::Input::Result response=#{response.inspect}, status=#{status.inspect}>"
14
- end
15
- end
11
+ InputError = Class.new Adhearsion::Error
16
12
 
17
13
  #
18
- # Prompts for input via DTMF, handling playback of prompts,
19
- # timeouts, digit limits and terminator digits.
14
+ # Prompts for input, handling playback of prompts, DTMF grammar construction, and execution
20
15
  #
21
- # @example A basic digit collection:
16
+ # @example A basic DTMF digit collection:
22
17
  # ask "Welcome, ", "/opt/sounds/menu-prompt.mp3",
23
- # :timeout => 10, :terminator => '#', :limit => 3 do |buffer|
24
- # buffer == "12980"
25
- # end
18
+ # timeout: 10, terminator: '#', limit: 3
26
19
  #
27
20
  # The first arguments will be a list of sounds to play, as accepted by #play, including strings for TTS, Date and Time objects, and file paths.
28
- # :timeout, :terminator and :limit options may then be specified.
29
- # A block may be passed which is invoked on each digit being collected. If it returns true, the collection is terminated.
21
+ # :timeout, :terminator and :limit options may be specified to automatically construct a grammar, or grammars may be manually specified.
30
22
  #
31
23
  # @param [Object, Array<Object>] args A list of outputs to play, as accepted by #play
32
- # @param [Hash] options Options to use for the menu
24
+ # @param [Hash] options Options to modify the grammar
33
25
  # @option options [Boolean] :interruptible If the prompt should be interruptible or not. Defaults to true
34
26
  # @option options [Integer] :limit Digit limit (causes collection to cease after a specified number of digits have been collected)
35
27
  # @option options [Integer] :timeout Timeout in seconds before the first and between each input digit
36
28
  # @option options [String] :terminator Digit to terminate input
29
+ # @option options [RubySpeech::GRXML::Grammar, Array<RubySpeech::GRXML::Grammar>] :grammar One of a collection of grammars to execute
30
+ # @option options [String, Array<String>] :grammar_url One of a collection of URLs for grammars to execute
31
+ # @option options [Hash] :input_options A hash of options passed directly to the Input constructor
32
+ # @option options [Hash] :output_options A hash of options passed directly to the Output constructor
37
33
  #
38
- # @return [Result] a result object from which the #response and #status may be established
34
+ # @return [Result] a result object from which the details of the utterance may be established
39
35
  #
40
36
  # @see Output#play
41
- # @see CallController#pass
37
+ # @see Adhearsion::Rayo::Component::Input.new
38
+ # @see Adhearsion::Rayo::Component::Output.new
42
39
  #
43
- def ask(*args, options, &block)
44
- logger.warn "This implementation of #ask is deprecated due to issues with dropped DTMF. For a solution, see http://adhearsion.com/docs/common_problems#toc_3"
40
+ def ask(*args)
41
+ options = args.last.kind_of?(Hash) ? args.pop : {}
42
+ prompts = args.flatten.compact
45
43
 
46
- unless options.is_a?(Hash)
47
- args << options
48
- options = {}
49
- end
50
- sound_files = args.flatten
44
+ options[:grammar] || options[:grammar_url] || options[:limit] || options[:terminator] || raise(ArgumentError, "You must specify at least one of limit, terminator or grammar")
51
45
 
52
- menu_instance = MenuDSL::Menu.new options do
53
- validator(&block) if block
54
- end
55
- menu_instance.validate :basic
56
- result_of_menu = nil
46
+ grammars = AskGrammarBuilder.new(options).grammars
57
47
 
58
- catch :finish do
59
- until MenuDSL::Menu::MenuResultDone === result_of_menu
60
- raise unless menu_instance.should_continue?
48
+ output_document = prompts.empty? ? nil : output_formatter.ssml_for_collection(prompts)
61
49
 
62
- result_of_menu = menu_instance.continue
63
-
64
- if result_of_menu.is_a?(MenuDSL::Menu::MenuGetAnotherDigit)
65
- next_digit = play_sound_files_for_menu menu_instance, sound_files
66
- if next_digit
67
- menu_instance << next_digit
68
- else
69
- menu_instance.timeout!
70
- throw :finish
71
- end
72
- end
73
- end
74
- end
75
-
76
- Result.new.tap do |result|
77
- result.response = menu_instance.result
78
- result.status = menu_instance.status
79
- result.menu = menu_instance
80
- end
50
+ PromptBuilder.new(output_document, grammars, options).execute self
81
51
  end
82
52
 
83
53
  # Creates and manages a multiple choice menu driven by DTMF, handling playback of prompts,
84
54
  # invalid input, retries and timeouts, and final failures.
85
55
  #
86
56
  # @example A complete example of the method is as follows:
87
- # menu "Welcome, ", "/opt/sounds/menu-prompt.mp3", :tries => 2, :timeout => 10 do
57
+ # menu "Welcome, ", "/opt/sounds/menu-prompt.mp3", tries: 2, timeout: 10 do
88
58
  # match 1, OperatorController
89
59
  #
90
60
  # match 10..19 do
@@ -92,7 +62,7 @@ module Adhearsion
92
62
  # end
93
63
  #
94
64
  # match 5, 6, 9 do |exten|
95
- # play "The #{exten} extension is currently not active"
65
+ # play "The #{exten} extension is currently not active"
96
66
  # end
97
67
  #
98
68
  # match '7', OfficeController
@@ -111,14 +81,12 @@ module Adhearsion
111
81
  # Input is matched against patterns, and the first exact match has it's payload executed.
112
82
  # Matched input is passed in to the associated block, or to the controller through #options.
113
83
  #
114
- # Allowed payloads are the name of a controller class, in which case it is executed through its #run method, or a block.
84
+ # Allowed payloads are the name of a controller class, in which case it is executed through its #run method, or a block, which is executed in the context of the current controller.
115
85
  #
116
86
  # #invalid has its associated block executed when the input does not possibly match any pattern.
117
- # #timeout's block is run when time expires before or between input digits.
87
+ # #timeout's block is run when timeout expires before receiving any input
118
88
  # #failure runs its block when the maximum number of tries is reached without an input match.
119
89
  #
120
- # #validator runs its block on each digit being collected. If it returns true, the collection is terminated.
121
- #
122
90
  # Execution of the current context resumes after #menu finishes. If you wish to jump to an entirely different controller, use #pass.
123
91
  # Menu will return :failed if failure was reached, or :done if a match was executed.
124
92
  #
@@ -127,117 +95,26 @@ module Adhearsion
127
95
  # @option options [Integer] :tries Number of tries allowed before failure
128
96
  # @option options [Integer] :timeout Timeout in seconds before the first and between each input digit
129
97
  # @option options [Boolean] :interruptible If the prompt should be interruptible or not. Defaults to true
98
+ # @option options [String, Symbol] :mode Input mode to accept. May be :voice or :dtmf.
99
+ # @option options [Hash] :input_options A hash of options passed directly to the Input constructor
100
+ # @option options [Hash] :output_options A hash of options passed directly to the Output constructor
130
101
  #
131
- # @return [Result] a result object from which the #response and #status may be established
102
+ # @return [Result] a result object from which the details of the utterance may be established
132
103
  #
133
104
  # @see Output#play
134
105
  # @see CallController#pass
135
106
  #
136
- def menu(*args, options, &block)
137
- logger.warn "This implementation of #menu is deprecated due to issues with dropped DTMF. For a solution, see http://adhearsion.com/docs/common_problems#toc_3"
107
+ def menu(*args, &block)
108
+ raise ArgumentError, "You must specify a block to build the menu" unless block
109
+ options = args.last.kind_of?(Hash) ? args.pop : {}
110
+ prompts = args.flatten.compact
138
111
 
139
- unless options.is_a?(Hash)
140
- args << options
141
- options = {}
142
- end
143
- sound_files = args.flatten
112
+ menu_builder = MenuBuilder.new(options, &block)
144
113
 
145
- menu_instance = MenuDSL::Menu.new options, &block
146
- menu_instance.validate
147
- result_of_menu = nil
114
+ output_document = prompts.empty? ? nil : output_formatter.ssml_for_collection(prompts)
148
115
 
149
- catch :finish do
150
- until MenuDSL::Menu::MenuResultDone === result_of_menu
151
- if menu_instance.should_continue?
152
- result_of_menu = menu_instance.continue
153
- else
154
- logger.debug "Menu failed to get valid input. Calling \"failure\" hook."
155
- menu_instance.execute_failure_hook
156
- throw :finish
157
- end
158
-
159
- case result_of_menu
160
- when MenuDSL::Menu::MenuResultInvalid
161
- logger.debug "Menu received invalid input. Calling \"invalid\" hook and restarting."
162
- menu_instance.execute_invalid_hook
163
- menu_instance.restart!
164
- result_of_menu = nil
165
- when MenuDSL::Menu::MenuGetAnotherDigit
166
- next_digit = play_sound_files_for_menu menu_instance, sound_files
167
- if next_digit
168
- menu_instance << next_digit
169
- else
170
- case result_of_menu
171
- when MenuDSL::Menu::MenuGetAnotherDigitOrFinish
172
- jump_to result_of_menu.match_object, :extension => result_of_menu.new_extension
173
- throw :finish
174
- when MenuDSL::Menu::MenuGetAnotherDigitOrTimeout
175
- logger.debug "Menu timed out. Calling \"timeout\" hook and restarting."
176
- menu_instance.execute_timeout_hook
177
- menu_instance.restart!
178
- result_of_menu = nil
179
- end
180
- end
181
- when MenuDSL::Menu::MenuResultFound
182
- logger.debug "Menu received valid input (#{result_of_menu.new_extension}). Calling the matching hook."
183
- jump_to result_of_menu.match_object, :extension => result_of_menu.new_extension
184
- throw :finish
185
- end
186
- end
187
- end
188
-
189
- Result.new.tap do |result|
190
- result.response = menu_instance.result
191
- result.status = menu_instance.status
192
- result.menu = menu_instance
193
- end
116
+ menu_builder.execute output_document, self
194
117
  end
195
-
196
- # @private
197
- def play_sound_files_for_menu(menu_instance, sound_files)
198
- digit = nil
199
- if sound_files.any? && menu_instance.digit_buffer_empty?
200
- if menu_instance.interruptible
201
- digit = interruptible_play(*sound_files, renderer: menu_instance.renderer)
202
- else
203
- play(*sound_files, renderer: menu_instance.renderer)
204
- end
205
- end
206
- digit || wait_for_digit(menu_instance.timeout)
207
- end
208
-
209
- #
210
- # Waits for a single digit and returns it, or returns nil if nothing was pressed
211
- #
212
- # @param [Integer] timeout the timeout to wait before returning, in seconds. nil or -1 mean no timeout.
213
- # @return [String, nil] the pressed key, or nil if timeout was reached.
214
- #
215
- # @private
216
- #
217
- def wait_for_digit(timeout = 1)
218
- timeout = nil if timeout == -1
219
- timeout *= 1_000 if timeout
220
- input_component = execute_component_and_await_completion Punchblock::Component::Input.new :mode => :dtmf,
221
- :initial_timeout => timeout,
222
- :inter_digit_timeout => timeout,
223
- :grammar => {
224
- :value => grammar_accept.to_s
225
- }
226
-
227
- reason = input_component.complete_event.reason
228
- result = reason.respond_to?(:utterance) ? reason.utterance : nil
229
- parse_dtmf result
230
- end
231
-
232
- # @private
233
- def jump_to(match_object, overrides = nil)
234
- if match_object.block
235
- instance_exec overrides[:extension], &match_object.block
236
- else
237
- invoke match_object.match_payload, overrides
238
- end
239
- end
240
-
241
118
  end
242
119
  end
243
120
  end