adhearsion 2.6.4 → 3.0.0.beta1

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 (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
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+
3
+ require 'ruby_jid'
4
+
5
+ module Adhearsion
6
+ module Rayo
7
+ ##
8
+ # A rayo Ref message. This provides the command ID in response to execution of a command.
9
+ #
10
+ class Ref < RayoNode
11
+ register :ref, :core
12
+
13
+ # @return [String] the command URI
14
+ attribute :uri
15
+ def uri=(other)
16
+ super URI(other)
17
+ end
18
+
19
+ def scheme
20
+ uri.scheme
21
+ end
22
+
23
+ def call_id
24
+ case scheme
25
+ when 'xmpp'
26
+ RubyJID.new(uri.opaque).node
27
+ when nil
28
+ uri.path
29
+ else
30
+ uri.opaque
31
+ end
32
+ end
33
+
34
+ def domain
35
+ case scheme
36
+ when 'xmpp'
37
+ RubyJID.new(uri.opaque).domain
38
+ end
39
+ end
40
+
41
+ def component_id
42
+ case scheme
43
+ when 'xmpp'
44
+ RubyJID.new(uri.opaque).resource
45
+ else
46
+ call_id
47
+ end
48
+ end
49
+
50
+ def rayo_attributes
51
+ {}.tap do |atts|
52
+ atts[:uri] = uri if uri
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -8,7 +8,7 @@ module Adhearsion
8
8
 
9
9
  # @private
10
10
  def self.setup_event_handlers
11
- Events.punchblock(Punchblock::Event::Offer) do
11
+ Events.rayo(Adhearsion::Event::Offer) do
12
12
  begin
13
13
  Celluloid::Actor[:statistics].register_call_offered
14
14
  ensure
@@ -12,13 +12,12 @@ initializer.load_lib_folder
12
12
  initializer.load_config_file
13
13
  initializer.load_events_file
14
14
  initializer.load_routes_file
15
- initializer.initialize_log_paths
16
15
  initializer.start_logging
17
16
  initializer.init_plugins
18
17
 
19
18
  Adhearsion::Plugin.load_tasks
20
19
 
21
- puts "\nAdhearsion configured environment: #{Adhearsion.config.platform.environment}\n" unless ARGV.empty?
20
+ puts "\nAdhearsion configured environment: #{Adhearsion.environment}\n" unless ARGV.empty?
22
21
 
23
22
  desc "Dump useful information about this application's Adhearsion environment"
24
23
  task :about do
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  namespace :config do
4
- desc "Show configuration values; accepts a parameter: [nil|platform|<plugin-name>|all]"
4
+ desc "Show configuration values; accepts a parameter: [nil|core|<plugin-name>|all]"
5
5
  task :show, [:name] => [:environment] do |t, args|
6
6
  name = args.name.nil? ? :all : args.name.to_sym
7
7
  puts "\nAdhearsion.config do |config|\n\n"
@@ -1,8 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  task :environment do
4
- require 'adhearsion/punchblock_plugin'
5
-
6
4
  begin
7
5
  Adhearsion.config # load default config vlaues
8
6
  initializer = Adhearsion::Initializer.new
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ namespace :i18n do
4
+ desc "Validate configured audio prompt files exist"
5
+ task :validate_files => :environment do
6
+ Adhearsion::Initializer.new.setup_i18n_load_path
7
+
8
+ config = Adhearsion.config.core.i18n
9
+ locale_files = Dir.glob(I18n.load_path)
10
+
11
+ locale_errors = {}
12
+ checked_prompts = 0
13
+ locale_files.each do |locale_file|
14
+ # We only support YAML for now
15
+ next unless locale_file =~ /\.ya?ml$/
16
+ prompts = YAML.load File.read(locale_file)
17
+
18
+ locale = prompts.keys.first
19
+ prompts = prompts[locale]
20
+
21
+ prompts.each_pair do |key, mapping|
22
+ logger.trace "Checking i18n key #{key}"
23
+ # Not all prompts will have audio files
24
+ next unless mapping['audio']
25
+
26
+ file = File.absolute_path "#{config['audio_path']}/#{locale}/#{mapping['audio']}"
27
+ unless File.exist?(file)
28
+ logger.warn "[#{locale}] Missing audio file: #{file}"
29
+ locale_errors[locale] ||= 0
30
+ locale_errors[locale] += 1
31
+ end
32
+ checked_prompts += 1
33
+ end
34
+ end
35
+
36
+ if checked_prompts == 0
37
+ logger.warn "No Adhearsion i18n prompts found. No files checked."
38
+ else
39
+ if locale_errors.keys.count > 0
40
+ logger.error "Errors detected! Number of errors by locale:"
41
+ locale_errors.each_pair do |locale, err_count|
42
+ logger.error "[#{locale}]: #{err_count} missing prompts"
43
+ end
44
+ else
45
+ logger.info "All configured prompt files successfully validated."
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ require 'adhearsion/error'
4
+
5
+ module Adhearsion
6
+ module Translator
7
+ OptionError = Class.new Adhearsion::Error
8
+ end
9
+ end
10
+
11
+ require 'adhearsion/translator/asterisk'
@@ -0,0 +1,234 @@
1
+ # encoding: utf-8
2
+
3
+ require 'celluloid'
4
+ require 'ruby_ami'
5
+
6
+ require 'adhearsion/translator/asterisk/agi_command'
7
+ require 'adhearsion/translator/asterisk/call'
8
+ require 'adhearsion/translator/asterisk/channel'
9
+ require 'adhearsion/translator/asterisk/component'
10
+
11
+ module Adhearsion
12
+ module Translator
13
+ class Asterisk
14
+ include Celluloid
15
+
16
+ # Indicates that a command was executed against a channel which no longer exists
17
+ ChannelGoneError = Class.new Error
18
+
19
+ attr_reader :ami_client, :connection, :calls, :bridges
20
+
21
+ REDIRECT_CONTEXT = 'adhearsion-redirect'
22
+ REDIRECT_EXTENSION = '1'
23
+ REDIRECT_PRIORITY = '1'
24
+
25
+ EVENTS_ALLOWED_BRIDGED = %w{AGIExec AsyncAGI}
26
+
27
+ trap_exit :actor_died
28
+
29
+ # Set the AMI event filter to be applied to incoming AMI events. A truthy return value will send the event via Rayo to the client (Adhearsion).
30
+ #
31
+ # @param [#[<RubyAMI::Event>]] filter
32
+ #
33
+ # @example A lambda
34
+ # Translator::Asterisk.event_filter = ->(event) { event.name == 'AsyncAGI' }
35
+ #
36
+ def self.event_filter=(filter)
37
+ @event_filter = filter
38
+ end
39
+
40
+ def self.event_passes_filter?(event)
41
+ @event_filter ? !!@event_filter[event] : true
42
+ end
43
+
44
+ event_filter = nil
45
+
46
+ def initialize(ami_client, connection)
47
+ @ami_client, @connection = ami_client, connection
48
+ @calls, @components, @channel_to_call_id, @bridges = {}, {}, {}, {}
49
+ end
50
+
51
+ def register_call(call)
52
+ @channel_to_call_id[call.channel] = call.id
53
+ @calls[call.id] ||= call
54
+ end
55
+
56
+ def deregister_call(id, channel)
57
+ @channel_to_call_id.delete channel
58
+ @calls.delete id
59
+ end
60
+
61
+ def call_with_id(call_id)
62
+ @calls[call_id]
63
+ end
64
+
65
+ def call_for_channel(channel)
66
+ call_with_id @channel_to_call_id[Channel.new(channel).name]
67
+ end
68
+
69
+ def register_component(component)
70
+ @components[component.id] ||= component
71
+ end
72
+
73
+ def component_with_id(component_id)
74
+ @components[component_id]
75
+ end
76
+
77
+ def handle_ami_event(event)
78
+ return unless event.is_a? RubyAMI::Event
79
+
80
+ if event.name == 'FullyBooted'
81
+ handle_pb_event Adhearsion::Rayo::Connection::Connected.new
82
+ run_at_fully_booted
83
+ return
84
+ end
85
+
86
+ handle_varset_ami_event event
87
+
88
+ ami_dispatch_to_or_create_call event
89
+ if !ami_event_known_call?(event) && self.class.event_passes_filter?(event)
90
+ handle_pb_event Adhearsion::Event::Asterisk::AMI.new(name: event.name, headers: event.headers)
91
+ end
92
+ end
93
+
94
+ def handle_pb_event(event)
95
+ connection.handle_event event
96
+ end
97
+
98
+ def send_message(call_id, domain, body, options = {})
99
+ call = call_with_id call_id
100
+ call.send_message body if call
101
+ end
102
+
103
+ def execute_command(command, options = {})
104
+ command.request!
105
+
106
+ command.target_call_id ||= options[:call_id]
107
+ command.component_id ||= options[:component_id]
108
+
109
+ if command.target_call_id
110
+ execute_call_command command
111
+ elsif command.component_id
112
+ execute_component_command command
113
+ else
114
+ execute_global_command command
115
+ end
116
+ end
117
+
118
+ def execute_call_command(command)
119
+ if call = call_with_id(command.target_call_id)
120
+ begin
121
+ call.execute_command command
122
+ rescue => e
123
+ deregister_call call.id, call.channel
124
+ end
125
+ else
126
+ command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a call with ID #{command.target_call_id}", command.target_call_id
127
+ end
128
+ end
129
+
130
+ def execute_component_command(command)
131
+ if (component = component_with_id(command.component_id))
132
+ component.execute_command command
133
+ else
134
+ command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a component with ID #{command.component_id}", command.target_call_id, command.component_id
135
+ end
136
+ end
137
+
138
+ def execute_global_command(command)
139
+ case command
140
+ when Adhearsion::Rayo::Component::Asterisk::AMI::Action
141
+ component = Component::Asterisk::AMIAction.new command, current_actor, ami_client
142
+ register_component component
143
+ component.execute
144
+ when Adhearsion::Rayo::Command::Dial
145
+ if call = call_with_id(command.uri)
146
+ command.response = Adhearsion::ProtocolError.new.setup(:conflict, 'Call ID already in use')
147
+ else
148
+ call = Call.new command.to, current_actor, ami_client, connection, nil, command.uri
149
+ register_call call
150
+ call.dial command
151
+ end
152
+ else
153
+ command.response = Adhearsion::ProtocolError.new.setup 'command-not-acceptable', "Did not understand command"
154
+ end
155
+ end
156
+
157
+ def run_at_fully_booted
158
+ send_ami_action 'Command', 'Command' => "dialplan add extension #{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY},AGI,agi:async into #{REDIRECT_CONTEXT}"
159
+
160
+ result = send_ami_action 'Command', 'Command' => "dialplan show #{REDIRECT_CONTEXT}"
161
+ if result.text_body =~ /failed/
162
+ logger.error "Adhearsion failed to add the #{REDIRECT_EXTENSION} extension to the #{REDIRECT_CONTEXT} context. Please add a [#{REDIRECT_CONTEXT}] entry to your dialplan."
163
+ end
164
+
165
+ check_recording_directory
166
+ end
167
+
168
+ def check_recording_directory
169
+ logger.warn "Recordings directory #{Component::Record::RECORDING_BASE_PATH} does not exist. Recording might not work. This warning can be ignored if Adhearsion is running on a separate machine than Asterisk. See http://adhearsion.com/docs/call-controllers#recording" unless File.exists?(Component::Record::RECORDING_BASE_PATH)
170
+ end
171
+
172
+ def actor_died(actor, reason)
173
+ return unless reason
174
+ if id = @calls.key(actor)
175
+ @calls.delete id
176
+ end_event = Adhearsion::Event::End.new :target_call_id => id,
177
+ :reason => :error
178
+ handle_pb_event end_event
179
+ end
180
+ end
181
+
182
+ private
183
+
184
+ def send_ami_action(name, headers = {})
185
+ ami_client.send_action name, headers
186
+ end
187
+
188
+ def handle_varset_ami_event(event)
189
+ return unless event.name == 'VarSet' && event['Variable'] == 'adhearsion_call_id' && (call = call_with_id event['Value'])
190
+
191
+ @channel_to_call_id.delete call.channel
192
+ call.channel = event['Channel']
193
+ register_call call
194
+ end
195
+
196
+ def ami_dispatch_to_or_create_call(event)
197
+ calls_for_event = channels_for_ami_event(event).inject({}) do |h, channel|
198
+ call = call_for_channel channel
199
+ h[channel] = call if call
200
+ h
201
+ end
202
+
203
+ if !calls_for_event.empty?
204
+ calls_for_event.each_pair do |channel, call|
205
+ next if channel.bridged? && !EVENTS_ALLOWED_BRIDGED.include?(event.name)
206
+ call.process_ami_event event
207
+ end
208
+ elsif event.name == "AsyncAGIStart" || (event.name == "AsyncAGI" && event['SubEvent'] == "Start")
209
+ handle_async_agi_start_event event
210
+ end
211
+ end
212
+
213
+ def channels_for_ami_event(event)
214
+ [event['Channel'], event['Channel1'], event['Channel2']].compact.map { |channel| Channel.new(channel) }
215
+ end
216
+
217
+ def ami_event_known_call?(event)
218
+ (event['Channel'] && call_for_channel(event['Channel'])) ||
219
+ (event['Channel1'] && call_for_channel(event['Channel1'])) ||
220
+ (event['Channel2'] && call_for_channel(event['Channel2']))
221
+ end
222
+
223
+ def handle_async_agi_start_event(event)
224
+ env = RubyAMI::AsyncAGIEnvironmentParser.new(event['Env']).to_hash
225
+
226
+ return if env[:agi_extension] == 'h' || env[:agi_type] == 'Kill'
227
+
228
+ call = Call.new event['Channel'], current_actor, ami_client, connection, env
229
+ register_call call
230
+ call.send_offer
231
+ end
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ module Adhearsion
4
+ module Translator
5
+ class Asterisk
6
+ class AGIApp
7
+ def initialize(app, *args)
8
+ @app, @args = app, args
9
+ end
10
+
11
+ def execute(call)
12
+ call.execute_agi_command "EXEC #{@app}", @args.join(',')
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'active_support/core_ext/string/filters'
4
+ require 'adhearsion/translator/asterisk/ami_error_converter'
5
+
6
+ module Adhearsion
7
+ module Translator
8
+ class Asterisk
9
+ class AGICommand
10
+ ARG_QUOTER = /["\\]/.freeze
11
+
12
+ attr_reader :id
13
+
14
+ def initialize(id, channel, command, *params)
15
+ @id, @channel, @command, @params = id, channel, command, params
16
+ end
17
+
18
+ # @raises RubyAMI::Error, ChannelGoneError
19
+ def execute(ami_client)
20
+ AMIErrorConverter.convert { ami_client.send_action 'AGI', 'Channel' => @channel, 'Command' => agi_command, 'CommandID' => id }
21
+ end
22
+
23
+ def parse_result(event)
24
+ parser = RubyAMI::AGIResultParser.new event['Result']
25
+ {code: parser.code, result: parser.result, data: parser.data}
26
+ rescue ArgumentError => e
27
+ logger.warn "Illegal message received from Asterisk: #{e.message}"
28
+ {code: -1, result: nil, data: nil}
29
+ end
30
+
31
+ private
32
+
33
+ def agi_command
34
+ "#{@command} #{@params.map { |arg| quote_arg(arg) }.join(' ')}".squish
35
+ end
36
+
37
+ # Arguments surrounded by quotes; quotes backslash-escaped.
38
+ # See parse_args in asterisk/res/res_agi.c (Asterisk 1.4.21.1)
39
+ def quote_arg(arg)
40
+ '"' + arg.to_s.gsub(ARG_QUOTER) { |m| "\\#{m}" } + '"'
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end