adhearsion 1.2.6 → 2.0.0.alpha1

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 (236) hide show
  1. data/.gitignore +17 -10
  2. data/CHANGELOG.md +273 -0
  3. data/Gemfile +1 -1
  4. data/Guardfile +17 -0
  5. data/README.markdown +61 -9
  6. data/Rakefile +16 -48
  7. data/adhearsion.gemspec +21 -7
  8. data/bin/ahn +3 -1
  9. data/cucumber.yml +4 -0
  10. data/features/app_generator.feature +42 -0
  11. data/features/cli.feature +108 -0
  12. data/features/step_definitions/app_generator_steps.rb +6 -0
  13. data/features/step_definitions/cli_steps.rb +74 -0
  14. data/features/support/aruba_helper.rb +22 -0
  15. data/features/support/env.rb +37 -0
  16. data/features/support/utils.rb +8 -0
  17. data/lib/adhearsion.rb +85 -41
  18. data/lib/adhearsion/call.rb +176 -0
  19. data/lib/adhearsion/call_controller.rb +134 -0
  20. data/lib/adhearsion/call_controller/dial.rb +70 -0
  21. data/lib/adhearsion/call_controller/input.rb +173 -0
  22. data/lib/adhearsion/call_controller/menu.rb +124 -0
  23. data/lib/adhearsion/call_controller/output.rb +267 -0
  24. data/lib/adhearsion/call_controller/record.rb +42 -0
  25. data/lib/adhearsion/call_controller/utility.rb +60 -0
  26. data/lib/adhearsion/calls.rb +81 -0
  27. data/lib/adhearsion/cli.rb +1 -3
  28. data/lib/adhearsion/cli_commands.rb +142 -0
  29. data/lib/adhearsion/configuration.rb +149 -0
  30. data/lib/adhearsion/console.rb +19 -8
  31. data/lib/adhearsion/dialplan_controller.rb +9 -0
  32. data/lib/adhearsion/events.rb +84 -0
  33. data/lib/adhearsion/foundation/all.rb +0 -7
  34. data/lib/adhearsion/foundation/custom_daemonizer.rb +4 -6
  35. data/lib/adhearsion/foundation/exception_handler.rb +9 -0
  36. data/lib/adhearsion/foundation/object.rb +26 -8
  37. data/lib/adhearsion/foundation/synchronized_hash.rb +3 -6
  38. data/lib/adhearsion/foundation/thread_safety.rb +17 -1
  39. data/lib/adhearsion/generators/app/app_generator.rb +4 -13
  40. data/lib/adhearsion/generators/app/templates/Gemfile +10 -5
  41. data/lib/adhearsion/generators/app/templates/Procfile +1 -0
  42. data/lib/adhearsion/generators/app/templates/README.md +28 -0
  43. data/lib/adhearsion/generators/app/templates/config/adhearsion.rb +41 -0
  44. data/lib/adhearsion/generators/app/templates/{components/simon_game → lib}/simon_game.rb +6 -18
  45. data/lib/adhearsion/generators/app/templates/script/ahn +2 -2
  46. data/lib/adhearsion/initializer.rb +151 -293
  47. data/lib/adhearsion/initializer/logging.rb +33 -0
  48. data/lib/adhearsion/logging.rb +65 -69
  49. data/lib/adhearsion/menu_dsl.rb +15 -0
  50. data/lib/adhearsion/menu_dsl/calculated_match.rb +39 -0
  51. data/lib/adhearsion/menu_dsl/calculated_match_collection.rb +41 -0
  52. data/lib/adhearsion/menu_dsl/fixnum_match_calculator.rb +18 -0
  53. data/lib/adhearsion/menu_dsl/match_calculator.rb +36 -0
  54. data/lib/adhearsion/{voip/menu_state_machine/menu_class.rb → menu_dsl/menu.rb} +38 -40
  55. data/lib/adhearsion/menu_dsl/menu_builder.rb +69 -0
  56. data/lib/adhearsion/menu_dsl/range_match_calculator.rb +55 -0
  57. data/lib/adhearsion/menu_dsl/string_match_calculator.rb +21 -0
  58. data/lib/adhearsion/outbound_call.rb +64 -0
  59. data/lib/adhearsion/plugin.rb +319 -0
  60. data/lib/adhearsion/plugin/collection.rb +19 -0
  61. data/lib/adhearsion/plugin/initializer.rb +37 -0
  62. data/lib/adhearsion/plugin/methods_container.rb +6 -0
  63. data/lib/adhearsion/process.rb +94 -0
  64. data/lib/adhearsion/punchblock_plugin.rb +29 -0
  65. data/lib/adhearsion/punchblock_plugin/initializer.rb +137 -0
  66. data/lib/adhearsion/router.rb +30 -0
  67. data/lib/adhearsion/router/route.rb +42 -0
  68. data/lib/adhearsion/script_ahn_loader.rb +2 -2
  69. data/lib/adhearsion/tasks.rb +14 -9
  70. data/lib/adhearsion/tasks/configuration.rb +26 -0
  71. data/lib/adhearsion/tasks/plugins.rb +17 -0
  72. data/lib/adhearsion/version.rb +8 -14
  73. data/spec/adhearsion/call_controller/dial_spec.rb +138 -0
  74. data/spec/adhearsion/call_controller/input_spec.rb +278 -0
  75. data/spec/adhearsion/call_controller/menu_spec.rb +120 -0
  76. data/spec/adhearsion/call_controller/output_spec.rb +466 -0
  77. data/spec/adhearsion/call_controller/record_spec.rb +125 -0
  78. data/spec/adhearsion/call_controller_spec.rb +395 -0
  79. data/spec/adhearsion/call_spec.rb +438 -0
  80. data/spec/adhearsion/calls_spec.rb +47 -0
  81. data/spec/adhearsion/configuration_spec.rb +308 -0
  82. data/spec/adhearsion/dialplan_controller_spec.rb +26 -0
  83. data/spec/adhearsion/events_spec.rb +112 -0
  84. data/spec/adhearsion/initializer/logging_spec.rb +58 -0
  85. data/spec/adhearsion/initializer_spec.rb +209 -122
  86. data/spec/adhearsion/logging_spec.rb +58 -47
  87. data/spec/adhearsion/menu_dsl/calculated_match_collection_spec.rb +56 -0
  88. data/spec/adhearsion/menu_dsl/calculated_match_spec.rb +57 -0
  89. data/spec/adhearsion/menu_dsl/fixnum_match_calculator_spec.rb +33 -0
  90. data/spec/adhearsion/menu_dsl/match_calculator_spec.rb +13 -0
  91. data/spec/adhearsion/menu_dsl/menu_builder_spec.rb +118 -0
  92. data/spec/adhearsion/menu_dsl/menu_spec.rb +210 -0
  93. data/spec/adhearsion/menu_dsl/range_match_calculator_spec.rb +28 -0
  94. data/spec/adhearsion/menu_dsl/string_match_calculator_spec.rb +36 -0
  95. data/spec/adhearsion/menu_dsl_spec.rb +12 -0
  96. data/spec/adhearsion/outbound_call_spec.rb +174 -0
  97. data/spec/adhearsion/plugin_spec.rb +489 -0
  98. data/spec/adhearsion/process_spec.rb +34 -0
  99. data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +294 -0
  100. data/spec/adhearsion/router/route_spec.rb +99 -0
  101. data/spec/adhearsion/router_spec.rb +106 -0
  102. data/spec/adhearsion_spec.rb +46 -0
  103. data/spec/spec_helper.rb +14 -14
  104. data/spec/support/call_controller_test_helpers.rb +48 -0
  105. data/spec/support/initializer_stubs.rb +8 -13
  106. data/spec/support/punchblock_mocks.rb +6 -0
  107. metadata +255 -253
  108. data/CHANGELOG +0 -174
  109. data/bin/ahnctl +0 -68
  110. data/bin/jahn +0 -43
  111. data/examples/asterisk_manager_interface/standalone.rb +0 -51
  112. data/lib/adhearsion/commands.rb +0 -302
  113. data/lib/adhearsion/component_manager.rb +0 -278
  114. data/lib/adhearsion/component_manager/component_tester.rb +0 -54
  115. data/lib/adhearsion/component_manager/spec_framework.rb +0 -18
  116. data/lib/adhearsion/events_support.rb +0 -65
  117. data/lib/adhearsion/foundation/blank_slate.rb +0 -3
  118. data/lib/adhearsion/foundation/event_socket.rb +0 -205
  119. data/lib/adhearsion/foundation/future_resource.rb +0 -36
  120. data/lib/adhearsion/foundation/metaprogramming.rb +0 -17
  121. data/lib/adhearsion/foundation/numeric.rb +0 -13
  122. data/lib/adhearsion/foundation/pseudo_guid.rb +0 -10
  123. data/lib/adhearsion/foundation/relationship_properties.rb +0 -42
  124. data/lib/adhearsion/foundation/string.rb +0 -26
  125. data/lib/adhearsion/generators/app/templates/.ahnrc +0 -34
  126. data/lib/adhearsion/generators/app/templates/README +0 -8
  127. data/lib/adhearsion/generators/app/templates/components/ami_remote/ami_remote.rb +0 -15
  128. data/lib/adhearsion/generators/app/templates/components/disabled/HOW_TO_ENABLE +0 -7
  129. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/README.markdown +0 -47
  130. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/stomp_gateway.rb +0 -34
  131. data/lib/adhearsion/generators/app/templates/components/disabled/stomp_gateway/stomp_gateway.yml +0 -12
  132. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/README.markdown +0 -3
  133. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +0 -11
  134. data/lib/adhearsion/generators/app/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
  135. data/lib/adhearsion/generators/app/templates/config/startup.rb +0 -81
  136. data/lib/adhearsion/generators/app/templates/dialplan.rb +0 -3
  137. data/lib/adhearsion/generators/app/templates/events.rb +0 -33
  138. data/lib/adhearsion/host_definitions.rb +0 -67
  139. data/lib/adhearsion/initializer/asterisk.rb +0 -86
  140. data/lib/adhearsion/initializer/configuration.rb +0 -324
  141. data/lib/adhearsion/initializer/database.rb +0 -60
  142. data/lib/adhearsion/initializer/drb.rb +0 -31
  143. data/lib/adhearsion/initializer/freeswitch.rb +0 -22
  144. data/lib/adhearsion/initializer/ldap.rb +0 -57
  145. data/lib/adhearsion/initializer/rails.rb +0 -41
  146. data/lib/adhearsion/initializer/xmpp.rb +0 -42
  147. data/lib/adhearsion/tasks/components.rb +0 -32
  148. data/lib/adhearsion/tasks/database.rb +0 -5
  149. data/lib/adhearsion/tasks/deprecations.rb +0 -59
  150. data/lib/adhearsion/tasks/generating.rb +0 -20
  151. data/lib/adhearsion/tasks/lint.rb +0 -4
  152. data/lib/adhearsion/voip/asterisk.rb +0 -4
  153. data/lib/adhearsion/voip/asterisk/agi_server.rb +0 -121
  154. data/lib/adhearsion/voip/asterisk/commands.rb +0 -1966
  155. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +0 -140
  156. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +0 -102
  157. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +0 -250
  158. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +0 -240
  159. data/lib/adhearsion/voip/asterisk/config_manager.rb +0 -64
  160. data/lib/adhearsion/voip/asterisk/manager_interface.rb +0 -697
  161. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +0 -1681
  162. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +0 -341
  163. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +0 -78
  164. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +0 -87
  165. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +0 -80
  166. data/lib/adhearsion/voip/call.rb +0 -521
  167. data/lib/adhearsion/voip/call_routing.rb +0 -64
  168. data/lib/adhearsion/voip/commands.rb +0 -17
  169. data/lib/adhearsion/voip/constants.rb +0 -39
  170. data/lib/adhearsion/voip/conveniences.rb +0 -18
  171. data/lib/adhearsion/voip/dial_plan.rb +0 -252
  172. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +0 -151
  173. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +0 -37
  174. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +0 -27
  175. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +0 -124
  176. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +0 -69
  177. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +0 -16
  178. data/lib/adhearsion/voip/dsl/numerical_string.rb +0 -128
  179. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +0 -48
  180. data/lib/adhearsion/voip/freeswitch/event_handler.rb +0 -58
  181. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +0 -129
  182. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +0 -38
  183. data/lib/adhearsion/voip/freeswitch/oes_server.rb +0 -195
  184. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +0 -80
  185. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +0 -123
  186. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +0 -57
  187. data/lib/adhearsion/xmpp/connection.rb +0 -61
  188. data/lib/theatre.rb +0 -147
  189. data/lib/theatre/README.markdown +0 -64
  190. data/lib/theatre/callback_definition_loader.rb +0 -86
  191. data/lib/theatre/guid.rb +0 -23
  192. data/lib/theatre/invocation.rb +0 -131
  193. data/lib/theatre/namespace_manager.rb +0 -153
  194. data/lib/theatre/version.rb +0 -2
  195. data/spec/adhearsion/cli_spec.rb +0 -306
  196. data/spec/adhearsion/component_manager_spec.rb +0 -292
  197. data/spec/adhearsion/constants_spec.rb +0 -8
  198. data/spec/adhearsion/drb_spec.rb +0 -65
  199. data/spec/adhearsion/fixtures/dialplan.rb +0 -3
  200. data/spec/adhearsion/foundation/event_socket_spec.rb +0 -168
  201. data/spec/adhearsion/host_definitions_spec.rb +0 -79
  202. data/spec/adhearsion/initializer/configuration_spec.rb +0 -291
  203. data/spec/adhearsion/initializer/loading_spec.rb +0 -154
  204. data/spec/adhearsion/initializer/paths_spec.rb +0 -74
  205. data/spec/adhearsion/relationship_properties_spec.rb +0 -54
  206. data/spec/adhearsion/voip/asterisk/agi_server_spec.rb +0 -473
  207. data/spec/adhearsion/voip/asterisk/ami/ami_spec.rb +0 -550
  208. data/spec/adhearsion/voip/asterisk/ami/lexer/ami_fixtures.yml +0 -30
  209. data/spec/adhearsion/voip/asterisk/ami/lexer/lexer_story +0 -291
  210. data/spec/adhearsion/voip/asterisk/ami/lexer/lexer_story.rb +0 -241
  211. data/spec/adhearsion/voip/asterisk/ami/lexer/story_helper.rb +0 -124
  212. data/spec/adhearsion/voip/asterisk/commands_spec.rb +0 -3241
  213. data/spec/adhearsion/voip/asterisk/config_file_generators/agents_spec.rb +0 -251
  214. data/spec/adhearsion/voip/asterisk/config_file_generators/queues_spec.rb +0 -323
  215. data/spec/adhearsion/voip/asterisk/config_file_generators/voicemail_spec.rb +0 -306
  216. data/spec/adhearsion/voip/asterisk/config_manager_spec.rb +0 -127
  217. data/spec/adhearsion/voip/asterisk/menu_command/calculated_match_spec.rb +0 -109
  218. data/spec/adhearsion/voip/asterisk/menu_command/matchers_spec.rb +0 -97
  219. data/spec/adhearsion/voip/call_routing_spec.rb +0 -125
  220. data/spec/adhearsion/voip/dialplan_manager_spec.rb +0 -468
  221. data/spec/adhearsion/voip/dsl/dialing_dsl_spec.rb +0 -270
  222. data/spec/adhearsion/voip/dsl/dispatcher_spec.rb +0 -82
  223. data/spec/adhearsion/voip/dsl/dispatcher_spec_helper.rb +0 -45
  224. data/spec/adhearsion/voip/dsl/parser_spec.rb +0 -69
  225. data/spec/adhearsion/voip/freeswitch/basic_connection_manager_spec.rb +0 -39
  226. data/spec/adhearsion/voip/freeswitch/inbound_connection_manager_spec.rb +0 -39
  227. data/spec/adhearsion/voip/freeswitch/oes_server_spec.rb +0 -9
  228. data/spec/adhearsion/voip/numerical_string_spec.rb +0 -61
  229. data/spec/adhearsion/voip/phone_number_spec.rb +0 -45
  230. data/spec/support/the_following_code.rb +0 -3
  231. data/spec/theatre/dsl_examples/simple_before_call.rb +0 -7
  232. data/spec/theatre/dsl_spec.rb +0 -69
  233. data/spec/theatre/invocation_spec.rb +0 -182
  234. data/spec/theatre/namespace_spec.rb +0 -125
  235. data/spec/theatre/spec_helper_spec.rb +0 -28
  236. data/spec/theatre/theatre_class_spec.rb +0 -148
@@ -1,80 +0,0 @@
1
- module Adhearsion
2
- class DialPlan
3
- class ConfirmationManager
4
-
5
- class << self
6
-
7
- def encode_hash_for_dial_macro_argument(options)
8
- options = options.clone
9
- macro_name = options.delete :macro
10
- options[:play] &&= options[:play].kind_of?(Array) ? options[:play].join('++') : options[:play]
11
- encoded_options = URI.escape options.map { |key,value| "#{key}:#{value}" }.join('!')
12
- "M(#{macro_name}^#{encoded_options})".tap do |str|
13
- if str.rindex('^') != str.index('^')
14
- raise ArgumentError, "You seem to have supplied a :confirm option with a caret (^) in it!" +
15
- " Please remove it. This will blow Asterisk up."
16
- end
17
- end
18
- end
19
-
20
- def handle(call)
21
- new(call).handle
22
- end
23
-
24
- def confirmation_call?(call)
25
- call.variables.has_key?(:network_script) && call.variables[:network_script].starts_with?('confirm!')
26
- end
27
-
28
- def decode_hash(encoded_hash)
29
- encoded_hash = encoded_hash =~ /^M\((.+)\)$/ ? $1 : encoded_hash
30
- encoded_hash = encoded_hash =~ /^([^:]+\^)?(.+)$/ ? $2 : encoded_hash # Remove the macro name if it's there
31
- unencoded = URI.unescape(encoded_hash).split('!')
32
- unencoded.shift unless unencoded.first.include?(':')
33
- unencoded = unencoded.map { |pair| key, value = pair.split(':'); [key.to_sym ,value] }.flatten
34
- Hash[*unencoded].tap do |hash|
35
- hash[:timeout] &&= hash[:timeout].to_i
36
- hash[:play] &&= hash[:play].split('++')
37
- end
38
- end
39
-
40
- end
41
-
42
- attr_reader :call
43
- def initialize(call)
44
- @call = call
45
- extend Adhearsion::VoIP::Commands.for(call.originating_voip_platform)
46
- end
47
-
48
- def handle
49
- variables = self.class.decode_hash call.variables[:network_script]
50
-
51
- answer
52
- loop do
53
- response = interruptible_play(*variables[:play])
54
- if response && response.to_s == variables[:key].to_s
55
- # Don't set a variable to pass through to dial()
56
- break
57
- elsif response && response.to_s != variables[:key].to_s
58
- next
59
- else
60
- response = wait_for_digit variables[:timeout]
61
- if response
62
- if response.to_s == variables[:key].to_s
63
- # Don't set a variable to pass through to dial()
64
- break
65
- else
66
- next
67
- end
68
- else
69
- # By setting MACRO_RESULT to CONTINUE, we cancel the dial.
70
- variable 'MACRO_RESULT' => "CONTINUE"
71
- break
72
- end
73
- end
74
- end
75
-
76
- end
77
-
78
- end
79
- end
80
- end
@@ -1,521 +0,0 @@
1
- require 'uri'
2
- require 'thread'
3
- #TODO Some of this is asterisk-specific
4
- module Adhearsion
5
- class << self
6
- def active_calls
7
- @calls ||= Calls.new
8
- end
9
-
10
- def receive_call_from(io, &block)
11
- active_calls << (call = Call.receive_from(io, &block))
12
- call
13
- end
14
-
15
- def remove_inactive_call(call)
16
- active_calls.remove_inactive_call(call)
17
- end
18
- end
19
-
20
- class Hangup < StandardError
21
- # At the moment, we'll just use this to end a call-handling Thread.
22
- end
23
-
24
- ##
25
- # This manages the list of calls the Adhearsion service receives
26
- class Calls
27
- attr_reader :semaphore, :calls
28
-
29
- def initialize
30
- @semaphore = Monitor.new
31
- @calls = {}
32
- end
33
-
34
- def <<(call)
35
- atomically do
36
- calls[call.unique_identifier] = call
37
- end
38
- end
39
-
40
- def any?
41
- atomically do
42
- !calls.empty?
43
- end
44
- end
45
-
46
- def size
47
- atomically do
48
- calls.size
49
- end
50
- end
51
-
52
- def remove_inactive_call(call)
53
- atomically do
54
- calls.delete call.unique_identifier
55
- end
56
- end
57
-
58
- # Searches all active calls by their unique_identifier. See Call#unique_identifier.
59
- # Is this actually by channel?
60
- def find(id)
61
- atomically do
62
- return calls[id]
63
- end
64
- end
65
- alias :[] :find
66
-
67
- def clear!
68
- atomically do
69
- calls.clear
70
- end
71
- end
72
-
73
- def with_tag(tag)
74
- atomically do
75
- calls.inject(Array.new) do |calls_with_tag,(key,call)|
76
- call.tagged_with?(tag) ? calls_with_tag << call : calls_with_tag
77
- end
78
- end
79
- end
80
-
81
- def each
82
- calls.each_pair{|id, call| yield id, call }
83
- end
84
-
85
- def to_a
86
- calls.values
87
- end
88
-
89
- def to_h
90
- calls
91
- end
92
-
93
- def method_missing(m, *args)
94
- atomically do
95
- calls.send(m.to_sym, *args)
96
- end
97
- end
98
-
99
- private
100
-
101
- def atomically(&block)
102
- semaphore.synchronize(&block)
103
- end
104
-
105
- end
106
-
107
- class UselessCallException < StandardError; end
108
-
109
- class MetaAgiCallException < StandardError
110
- attr_reader :call
111
- def initialize(call)
112
- super()
113
- @call = call
114
- end
115
- end
116
-
117
- class FailedExtensionCallException < MetaAgiCallException; end
118
-
119
- class HungupExtensionCallException < MetaAgiCallException; end
120
-
121
- ##
122
- # Encapsulates call-related data and behavior.
123
- # For example, variables passed in on call initiation are
124
- # accessible here as attributes
125
- class Call
126
-
127
- ##
128
- # Wraps the Queue object (subclasses it) so we can handle runaway threads,
129
- # namely those originating from using with_next_message in commands.rb - this
130
- # overrides << to check for the :cancel symbol and trigger the automessaging_tainted
131
- # instance variable.
132
- class CallMessageQueue < Queue
133
-
134
- class InboxClosedException < StandardError
135
- # this gets raised when the :cancel message is delivered to the queue and with_next_message (or similar auto-message-iteration)
136
- # features are called.
137
- end
138
-
139
- attr_reader :open
140
-
141
- def initialize
142
- @open = true
143
- super
144
- end
145
-
146
- def <<(queue_obj)
147
- @open = false if queue_obj == :cancel
148
- super(queue_obj)
149
- end
150
-
151
- def pop
152
- raise Adhearsion::Call::CallMessageQueue::InboxClosedException, "The message queue for this call has aleady been disabled." if !@open
153
- super
154
- end
155
- end
156
-
157
-
158
- # This is basically a translation of ast_channel_reason2str() from main/channel.c and
159
- # ast_control_frame_type in include/asterisk/frame.h in the Asterisk source code. When
160
- # Asterisk jumps to the 'failed' extension, it sets a REASON channel variable to a number.
161
- # The indexes of these symbols represent the possible numbers REASON could be.
162
- ASTERISK_FRAME_STATES = [
163
- :failure, # "Call Failure (not BUSY, and not NO_ANSWER, maybe Circuit busy or down?)"
164
- :hangup, # Other end has hungup
165
- :ring, # Local ring
166
- :ringing, # Remote end is ringing
167
- :answer, # Remote end has answered
168
- :busy, # Remote end is busy
169
- :takeoffhook, # Make it go off hook
170
- :offhook, # Line is off hook
171
- :congestion, # Congestion (circuits busy)
172
- :flash, # Flash hook
173
- :wink, # Wink
174
- :option, # Set a low-level option
175
- :radio_key, # Key Radio
176
- :radio_unkey, # Un-Key Radio
177
- :progress, # Indicate PROGRESS
178
- :proceeding, # Indicate CALL PROCEEDING
179
- :hold, # Indicate call is placed on hold
180
- :unhold, # Indicate call is left from hold
181
- :vidupdate # Indicate video frame update
182
- ]
183
-
184
-
185
- class << self
186
- ##
187
- # The primary public interface for creating a Call instance.
188
- # Given an IO (probably a socket accepted from an Asterisk service),
189
- # creates a Call instance which encapsulates everything we know about that call.
190
- def receive_from(io, &block)
191
- new(io, variable_parser_for(io).variables).tap do |call|
192
- block.call(call) if block
193
- end
194
- end
195
-
196
- private
197
- def variable_parser_for(io)
198
- Variables::Parser.parse(io)
199
- end
200
-
201
- end
202
-
203
- attr_accessor :io, :type, :variables, :originating_voip_platform, :inbox
204
- def initialize(io, variables)
205
- @io, @variables = io, variables.symbolize_keys
206
- check_if_valid_call
207
- define_variable_accessors
208
- set_originating_voip_platform!
209
- @tag_mutex = Mutex.new
210
- @tags = []
211
- end
212
-
213
- def tags
214
- @tag_mutex.synchronize do
215
- return @tags.clone
216
- end
217
- end
218
-
219
- # This may still be a symbol, but no longer requires the tag to be a symbol although beware
220
- # that using a symbol would create a memory leak if used improperly
221
- # @param [String, Symbol] label String or Symbol with which to tag this call
222
- def tag(label)
223
- if ![String, Symbol].include?(label.class)
224
- raise ArgumentError, "Tag must be a String or Symbol"
225
- end
226
- @tag_mutex.synchronize do
227
- @tags << label
228
- end
229
- end
230
-
231
- def remove_tag(symbol)
232
- @tag_mutex.synchronize do
233
- @tags.reject! { |tag| tag == symbol }
234
- end
235
- end
236
-
237
- def tagged_with?(symbol)
238
- @tag_mutex.synchronize do
239
- @tags.include? symbol
240
- end
241
- end
242
-
243
- def deliver_message(message)
244
- inbox << message
245
- end
246
- alias << deliver_message
247
-
248
- def can_use_messaging?
249
- return inbox.open == true
250
- end
251
-
252
- def inbox
253
- @inbox ||= CallMessageQueue.new
254
- end
255
-
256
- def hangup!
257
- io.close
258
- Adhearsion.remove_inactive_call self
259
- end
260
-
261
- def closed?
262
- io.closed?
263
- end
264
-
265
- # Asterisk sometimes uses the "failed" extension to indicate a failed dial attempt.
266
- # Since it may be important to handle these, this flag helps the dialplan Manager
267
- # figure that out.
268
- def failed_call?
269
- @failed_call
270
- end
271
-
272
- def hungup_call?
273
- @hungup_call
274
- end
275
-
276
- # Lock the socket for a command. Can be used to allow the console to take
277
- # control of the thread in between AGI commands coming from the dialplan.
278
- def with_command_lock
279
- @command_monitor ||= Monitor.new
280
- @command_monitor.synchronize do
281
- yield
282
- end
283
- end
284
-
285
- # Adhearsion indexes calls by this identifier so they may later be found and manipulated. For calls from Asterisk, this
286
- # method uses the following properties for uniqueness, falling back to the next if one is for some reason unavailable:
287
- #
288
- # Asterisk channel ID -> unique ID -> Call#object_id
289
- # (e.g. SIP/mytrunk-jb12c88a) -> (e.g. 1215039989.47033) -> (e.g. 2792080)
290
- #
291
- # Note: channel is used over unique ID because channel may be used to bridge two channels together.
292
- def unique_identifier
293
- case originating_voip_platform
294
- when :asterisk
295
- variables[:channel] || variables[:uniqueid] || object_id
296
- else
297
- raise NotImplementedError
298
- end
299
- end
300
-
301
- def define_variable_accessors(recipient=self)
302
- variables.each do |key, value|
303
- define_singleton_accessor_with_pair(key, value, recipient)
304
- end
305
- end
306
-
307
- def extract_failed_reason_from(environment)
308
- if originating_voip_platform == :asterisk
309
- failed_reason = environment.variable 'REASON'
310
- failed_reason &&= ASTERISK_FRAME_STATES[failed_reason.to_i]
311
- define_singleton_accessor_with_pair(:failed_reason, failed_reason, environment)
312
- end
313
- end
314
-
315
- private
316
-
317
- def define_singleton_accessor_with_pair(key, value, recipient=self)
318
- recipient.metaclass.send :attr_accessor, key unless recipient.class.respond_to?("#{key}=")
319
- recipient.metaclass.send :public, key, "#{key}=".to_sym
320
- recipient.send "#{key}=", value
321
- end
322
-
323
- def check_if_valid_call
324
- extension = variables[:extension]
325
- @failed_call = true if extension == 'failed'
326
- @hungup_call = true if extension == 'h'
327
- raise UselessCallException if extension == 't' # TODO: Move this whole method to Manager
328
- end
329
-
330
- def set_originating_voip_platform!
331
- # TODO: we can make this determination programatically at some point,
332
- # but it will probably involve a bit more engineering than just a case statement (like
333
- # subclasses of Call for the various platforms), so we'll be totally cheap for now.
334
- self.originating_voip_platform = :asterisk
335
- end
336
-
337
- module Variables
338
-
339
- module Coercions
340
-
341
- COERCION_ORDER = %w{
342
- remove_agi_prefixes_from_keys_and_strip_whitespace
343
- coerce_keys_into_symbols
344
- coerce_extension_into_phone_number_object
345
- coerce_numerical_values_to_numerics
346
- replace_unknown_values_with_nil
347
- replace_yes_no_answers_with_booleans
348
- coerce_request_into_uri_object
349
- decompose_uri_query_into_hash
350
- override_variables_with_query_params
351
- remove_dashes_from_context_name
352
- coerce_type_of_number_into_symbol
353
- }
354
-
355
- class << self
356
-
357
- def remove_agi_prefixes_from_keys_and_strip_whitespace(variables)
358
- variables.inject({}) do |new_variables,(key,value)|
359
- new_variables.tap do
360
- stripped_name = key.kind_of?(String) ? key[/^(agi_)?(.+)$/,2] : key
361
- new_variables[stripped_name] = value.kind_of?(String) ? value.strip : value
362
- end
363
- end
364
- end
365
-
366
- def coerce_keys_into_symbols(variables)
367
- variables.inject({}) do |new_variables,(key,value)|
368
- new_variables.tap do
369
- new_variables[key.to_sym] = value
370
- end
371
- end
372
- end
373
-
374
- def coerce_extension_into_phone_number_object(variables)
375
- variables.tap do
376
- variables[:extension] = Adhearsion::VoIP::DSL::PhoneNumber.new(variables[:extension])
377
- end
378
- end
379
-
380
- def coerce_numerical_values_to_numerics(variables)
381
- variables.inject({}) do |vars,(key,value)|
382
- vars.tap do
383
- is_numeric = value =~ /^-?\d+(?:(\.)\d+)?$/
384
- is_float = $1
385
- vars[key] = if is_numeric
386
- if Adhearsion::VoIP::DSL::NumericalString.starts_with_leading_zero?(value)
387
- Adhearsion::VoIP::DSL::NumericalString.new(value)
388
- else
389
- if is_float
390
- if key == :uniqueid
391
- value
392
- else
393
- value.to_f
394
- end
395
- else
396
- value.to_i
397
- end
398
- end
399
- else
400
- value
401
- end
402
- end
403
- end
404
- end
405
-
406
- def replace_unknown_values_with_nil(variables)
407
- variables.each do |key,value|
408
- variables[key] = nil if value == 'unknown'
409
- end
410
- end
411
-
412
- def replace_yes_no_answers_with_booleans(variables)
413
- variables.each do |key,value|
414
- case value
415
- when 'yes' then variables[key] = true
416
- when 'no' then variables[key] = false
417
- end
418
- end
419
- end
420
-
421
- def coerce_request_into_uri_object(variables)
422
- if variables[:request]
423
- variables[:request] = URI.parse(variables[:request]) unless variables[:request].kind_of? URI
424
- end
425
- variables
426
- end
427
-
428
- def coerce_type_of_number_into_symbol(variables)
429
- variables.tap do
430
- variables[:type_of_calling_number] = Adhearsion::VoIP::Constants::Q931_TYPE_OF_NUMBER[variables.delete(:callington).to_i]
431
- end
432
- end
433
-
434
- def decompose_uri_query_into_hash(variables)
435
- variables.tap do
436
- if variables[:request] && variables[:request].query
437
- variables[:query] = variables[:request].query.split('&').inject({}) do |query_string_parameters, key_value_pair|
438
- parameter_name, parameter_value = *key_value_pair.match(/(.+)=(.*)/).captures
439
- query_string_parameters[parameter_name] = parameter_value
440
- query_string_parameters
441
- end
442
- else
443
- variables[:query] = {}
444
- end
445
- end
446
- end
447
-
448
- def override_variables_with_query_params(variables)
449
- variables.tap do
450
- if variables[:query]
451
- variables[:query].each do |key, value|
452
- variables[key.to_sym] = value
453
- end
454
- end
455
- end
456
- end
457
-
458
- def remove_dashes_from_context_name(variables)
459
- variables.tap do
460
- variables[:context].gsub!('-', '_')
461
- end
462
- end
463
-
464
- end
465
- end
466
-
467
- class Parser
468
-
469
- class << self
470
- def parse(*args, &block)
471
- new(*args, &block).tap do |parser|
472
- parser.parse
473
- end
474
- end
475
-
476
- def coerce_variables(variables)
477
- Coercions::COERCION_ORDER.inject(variables) do |tmp_variables, coercing_method_name|
478
- Coercions.send(coercing_method_name, tmp_variables)
479
- end
480
- end
481
-
482
- def separate_line_into_key_value_pair(line)
483
- line.match(/^([^:]+):(?:\s?(.+)|$)/).captures
484
- end
485
- end
486
-
487
- attr_reader :io, :variables, :lines
488
- def initialize(io)
489
- @io = io
490
- @lines = []
491
- end
492
-
493
- def parse
494
- extract_variable_lines_from_io
495
- initialize_variables_as_hash_from_lines
496
- @variables = self.class.coerce_variables(variables)
497
- end
498
-
499
- private
500
-
501
- def initialize_variables_as_hash_from_lines
502
- @variables = lines.inject({}) do |new_variables,line|
503
- new_variables.tap do
504
- key, value = self.class.separate_line_into_key_value_pair line
505
- new_variables[key] = value
506
- end
507
- end
508
- end
509
-
510
- def extract_variable_lines_from_io
511
- while line = io.readline.chomp
512
- break if line.empty?
513
- @lines << line
514
- end
515
- end
516
-
517
- end
518
-
519
- end
520
- end
521
- end