adhearsion 1.2.6 → 2.0.0.alpha1

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