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
@@ -0,0 +1,176 @@
1
+ require 'thread'
2
+
3
+ module Adhearsion
4
+ ##
5
+ # Encapsulates call-related data and behavior.
6
+ #
7
+ class Call
8
+
9
+ include HasGuardedHandlers
10
+
11
+ attr_accessor :offer, :client, :end_reason, :commands
12
+
13
+ def initialize(offer = nil)
14
+ if offer
15
+ @offer = offer
16
+ @client = offer.client
17
+ end
18
+
19
+ @tag_mutex = Mutex.new
20
+ @tags = []
21
+ @end_reason_mutex = Mutex.new
22
+ end_reason = nil
23
+ @commands = CommandRegistry.new
24
+
25
+ register_initial_handlers
26
+ end
27
+
28
+ def id
29
+ @offer.call_id
30
+ end
31
+
32
+ def tags
33
+ @tag_mutex.synchronize { @tags.clone }
34
+ end
35
+
36
+ # This may still be a symbol, but no longer requires the tag to be a symbol although beware
37
+ # that using a symbol would create a memory leak if used improperly
38
+ # @param [String, Symbol] label String or Symbol with which to tag this call
39
+ def tag(label)
40
+ raise ArgumentError, "Tag must be a String or Symbol" unless [String, Symbol].include?(label.class)
41
+ @tag_mutex.synchronize { @tags << label }
42
+ end
43
+
44
+ def remove_tag(symbol)
45
+ @tag_mutex.synchronize do
46
+ @tags.reject! { |tag| tag == symbol }
47
+ end
48
+ end
49
+
50
+ def tagged_with?(symbol)
51
+ @tag_mutex.synchronize { @tags.include? symbol }
52
+ end
53
+
54
+ def register_event_handler(*guards, &block)
55
+ register_handler :event, *guards, &block
56
+ end
57
+
58
+ def deliver_message(message)
59
+ trigger_handler :event, message
60
+ end
61
+ alias << deliver_message
62
+
63
+ def register_initial_handlers
64
+ on_end do |event|
65
+ hangup
66
+ @end_reason_mutex.synchronize { @end_reason = event.reason }
67
+ commands.terminate
68
+ end
69
+ end
70
+
71
+ def on_end(&block)
72
+ register_event_handler :class => Punchblock::Event::End do |event|
73
+ block.call event
74
+ throw :pass
75
+ end
76
+ end
77
+
78
+ def active?
79
+ @end_reason_mutex.synchronize { !end_reason }
80
+ end
81
+
82
+ def accept(headers = nil)
83
+ write_and_await_response Punchblock::Command::Accept.new(:headers => headers)
84
+ end
85
+
86
+ def answer(headers = nil)
87
+ write_and_await_response Punchblock::Command::Answer.new(:headers => headers)
88
+ end
89
+
90
+ def reject(reason = :busy, headers = nil)
91
+ write_and_await_response Punchblock::Command::Reject.new(:reason => reason, :headers => headers)
92
+ end
93
+
94
+ def hangup!(headers = nil)
95
+ return false unless active?
96
+ @end_reason_mutex.synchronize { @end_reason = true }
97
+ write_and_await_response Punchblock::Command::Hangup.new(:headers => headers)
98
+ end
99
+
100
+ def hangup
101
+ Adhearsion.active_calls.remove_inactive_call self
102
+ end
103
+
104
+ def join(other_call_id)
105
+ write_and_await_response Punchblock::Command::Join.new :other_call_id => other_call_id
106
+ end
107
+
108
+ # Lock the socket for a command. Can be used to allow the console to take
109
+ # control of the thread in between AGI commands coming from the dialplan.
110
+ def with_command_lock
111
+ @command_monitor ||= Monitor.new
112
+ @command_monitor.synchronize { yield }
113
+ end
114
+
115
+ def write_and_await_response(command, timeout = 60)
116
+ logger.trace "Executing command #{command.inspect}"
117
+ commands << command
118
+ write_command command
119
+ response = command.response timeout
120
+ raise response if response.is_a? Exception
121
+ command
122
+ end
123
+
124
+ def write_command(command)
125
+ raise Hangup unless active? || command.is_a?(Punchblock::Command::Hangup)
126
+ client.execute_command command, :call_id => id
127
+ end
128
+
129
+ # Sanitize the offer id
130
+ def logger_id
131
+ "#{self.class}: #{id}"
132
+ end
133
+
134
+ def variables
135
+ offer ? offer.headers_hash : nil or {}
136
+ end
137
+
138
+ def execute_controller(controller, latch = nil)
139
+ Adhearsion::Process.important_threads << Thread.new do
140
+ catching_standard_errors do
141
+ begin
142
+ CallController.exec controller
143
+ ensure
144
+ hangup!
145
+ end
146
+ latch.countdown! if latch
147
+ end
148
+ end
149
+ end
150
+
151
+ class CommandRegistry < ThreadSafeArray
152
+ def terminate
153
+ hangup = Hangup.new
154
+ each { |command| command.response = hangup if command.requested? }
155
+ end
156
+ end
157
+
158
+ class Registry
159
+ @registry = Hash.new
160
+ @mutex = Mutex.new
161
+
162
+ def self.[](k)
163
+ @mutex.synchronize do
164
+ @registry[k]
165
+ end
166
+ end
167
+
168
+ def self.[]=(k, value)
169
+ @mutex.synchronize do
170
+ @registry[k] = value
171
+ end
172
+ end
173
+ end#Registry
174
+
175
+ end#Call
176
+ end#Adhearsion
@@ -0,0 +1,134 @@
1
+ module Adhearsion
2
+ class CallController
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Dial
6
+ autoload :Input
7
+ autoload :Output
8
+ autoload :Record
9
+ autoload :Menu
10
+ autoload :Utility
11
+
12
+ include Dial
13
+ include Input
14
+ include Output
15
+ include Record
16
+ include Menu
17
+ include Utility
18
+
19
+ class_attribute :callbacks
20
+
21
+ self.callbacks = {:before_call => [], :after_call => []}
22
+
23
+ self.callbacks.keys.each do |name|
24
+ class_eval <<-STOP
25
+ def self.#{name}(method_name = nil, &block)
26
+ callback = if method_name
27
+ lambda { send method_name }
28
+ elsif block
29
+ block
30
+ end
31
+ self.callbacks = self.callbacks.dup.tap { |cb| cb[:#{name}] += Array(callback) }
32
+ end
33
+ STOP
34
+ end
35
+
36
+ def self.exec(controller, fresh_call = true)
37
+ return unless controller
38
+
39
+ new_controller = catch :pass_controller do
40
+ controller.skip_accept! unless fresh_call
41
+ controller.execute!
42
+ nil
43
+ end
44
+
45
+ exec new_controller, false
46
+ end
47
+
48
+ attr_reader :call, :metadata
49
+
50
+ delegate :[], :[]=, :to => :@metadata
51
+ delegate :variables, :logger, :to => :call
52
+ delegate :write_and_await_response, :accept, :answer, :reject, :to => :call
53
+
54
+ def initialize(call, metadata = nil)
55
+ @call, @metadata = call, metadata || {}
56
+ setup
57
+ end
58
+
59
+ def setup
60
+ Plugin.add_dialplan_methods self if Plugin
61
+ end
62
+
63
+ def execute!(*options)
64
+ execute_callbacks :before_call
65
+ accept if auto_accept?
66
+ run
67
+ rescue Hangup
68
+ logger.info "Call was hung up"
69
+ rescue SyntaxError, StandardError => e
70
+ Events.trigger :exception, e
71
+ ensure
72
+ after_call
73
+ end
74
+
75
+ def run
76
+ end
77
+
78
+ def invoke(controller_class, metadata = nil)
79
+ controller = controller_class.new call, metadata
80
+ controller.run
81
+ end
82
+
83
+ def pass(controller_class, metadata = nil)
84
+ throw :pass_controller, controller_class.new(call, metadata)
85
+ end
86
+
87
+ def execute_callbacks(type)
88
+ self.class.callbacks[type].each do |callback|
89
+ catching_standard_errors do
90
+ instance_exec &callback
91
+ end
92
+ end
93
+ end
94
+
95
+ def skip_accept!
96
+ @skip_accept = true
97
+ end
98
+
99
+ def skip_accept?
100
+ @skip_accept || false
101
+ end
102
+
103
+ def auto_accept?
104
+ Adhearsion.config.platform.automatically_accept_incoming_calls && !skip_accept?
105
+ end
106
+
107
+ def after_call
108
+ @after_call ||= execute_callbacks :after_call
109
+ end
110
+
111
+ def hangup(headers = nil)
112
+ hangup_response = call.hangup! headers
113
+ after_call unless hangup_response == false
114
+ end
115
+
116
+ def mute
117
+ write_and_await_response ::Punchblock::Command::Mute.new
118
+ end
119
+
120
+ def unmute
121
+ write_and_await_response ::Punchblock::Command::Unmute.new
122
+ end
123
+
124
+ def execute_component_and_await_completion(component)
125
+ write_and_await_response component
126
+
127
+ yield component if block_given?
128
+
129
+ complete_event = component.complete_event
130
+ raise StandardError, complete_event.reason.details if complete_event.reason.is_a? Punchblock::Event::Complete::Error
131
+ component
132
+ end
133
+ end#class
134
+ end
@@ -0,0 +1,70 @@
1
+ module Adhearsion
2
+ class CallController
3
+ module Dial
4
+ #
5
+ # Dial a third party and join to this call
6
+ #
7
+ # @param [String|Array<String>] number represents the extension or "number" that asterisk should dial.
8
+ # Be careful to not just specify a number like 5001, 9095551001
9
+ # You must specify a properly formatted string as Asterisk would expect to use in order to understand
10
+ # whether the call should be dialed using SIP, IAX, or some other means.
11
+ # You can also specify an array of destinations: each will be called with the same options simultaneously.
12
+ # The first call answered is joined, the others are hung up.
13
+ #
14
+ # @param [Hash] options
15
+ #
16
+ # +:caller_id+ - the caller id number to be used when the call is placed. It is advised you properly adhere to the
17
+ # policy of VoIP termination providers with respect to caller id values.
18
+ #
19
+ # +:name+ - this is the name which should be passed with the caller ID information
20
+ # if :name=>"John Doe" and :caller_id => "444-333-1000" then the compelete CID and name would be "John Doe" <4443331000>
21
+ # support for caller id information varies from country to country and from one VoIP termination provider to another.
22
+ #
23
+ # +:for+ - this option can be thought of best as a timeout. i.e. timeout after :for if no one answers the call
24
+ # For example, dial("SIP/jay-desk-650&SIP/jay-desk-601&SIP/jay-desk-601-2", :for => 15.seconds, :caller_id => callerid)
25
+ # this call will timeout after 15 seconds if 1 of the 3 extensions being dialed do not pick prior to the 15 second time limit
26
+ #
27
+ # +:options+ - This is a string of options like "Tr" which are supported by the asterisk DIAL application.
28
+ # for a complete list of these options and their usage please check the link below.
29
+ #
30
+ # +:confirm+ - ?
31
+ #
32
+ # @example Make a call to the PSTN using my SIP provider for VoIP termination
33
+ # dial "SIP/19095551001@my.sip.voip.terminator.us"
34
+ #
35
+ # @example Make 3 Simulataneous calls to the SIP extensions, try for 15 seconds and use the callerid
36
+ # for this call specified by the variable my_callerid
37
+ # dial ["SIP/jay-desk-650", "SIP/jay-desk-601", "SIP/jay-desk-601-2"], :for => 15.seconds, :caller_id => my_callerid
38
+ #
39
+ # @example Make a call using the IAX provider to the PSTN
40
+ # dial "IAX2/my.id@voipjet/19095551234", :name => "John Doe", :caller_id => "9095551234"
41
+ #
42
+ def dial(to, options = {})
43
+ latch = CountDownLatch.new 1
44
+ calls = Array(to).map do |target|
45
+ new_call = OutboundCall.new options
46
+
47
+ new_call.on_answer do |event|
48
+ calls.each do |call_to_hangup, target|
49
+ call_to_hangup.hangup! unless call_to_hangup.id == new_call.id
50
+ end
51
+ new_call.join call.id
52
+ end
53
+
54
+ new_call.on_end do |event|
55
+ latch.countdown!
56
+ end
57
+
58
+ [new_call, target]
59
+ end
60
+
61
+ calls.each do |call, target|
62
+ call.dial target, options
63
+ end
64
+
65
+ latch.wait
66
+ end
67
+
68
+ end#module Dial
69
+ end
70
+ end
@@ -0,0 +1,173 @@
1
+ module Adhearsion
2
+ class CallController
3
+ module Input
4
+ #
5
+ # Waits for a single digit and returns it, or returns nil if nothing was pressed
6
+ #
7
+ # @param [Integer] the timeout to wait before returning, in seconds. nil or -1 mean no timeout.
8
+ # @return [String|nil] the pressed key, or nil if timeout was reached.
9
+ #
10
+ def wait_for_digit(timeout = 1)
11
+ timeout = nil if timeout == -1
12
+ timeout *= 1_000 if timeout
13
+ input_component = execute_component_and_await_completion ::Punchblock::Component::Input.new :mode => :dtmf,
14
+ :initial_timeout => timeout,
15
+ :inter_digit_timeout => timeout,
16
+ :grammar => {
17
+ :value => grammar_accept.to_s
18
+ }
19
+
20
+ reason = input_component.complete_event.reason
21
+ result = reason.respond_to?(:interpretation) ? reason.interpretation : nil
22
+ parse_single_dtmf result
23
+ end
24
+
25
+ # Used to receive keypad input from the user. Digits are collected
26
+ # via DTMF (keypad) input until one of three things happens:
27
+ #
28
+ # 1. The number of digits you specify as the first argument is collected
29
+ # 2. The timeout you specify with the :timeout option elapses, in seconds.
30
+ # 3. The "#" key (or the key you specify with :accept_key) is pressed
31
+ #
32
+ # Usage examples
33
+ #
34
+ # input # Receives digits until the caller presses the "#" key
35
+ # input 3 # Receives three digits. Can be 0-9, * or #
36
+ # input 5, :accept_key => "*" # Receive at most 5 digits, stopping if '*' is pressed
37
+ # input 1, :timeout => 60000 # Receive a single digit, returning an empty
38
+ # string if the timeout is encountered
39
+ # input 9, :timeout => 7000, :accept_key => "0" # Receives nine digits, returning
40
+ # # when the timeout is encountered
41
+ # # or when the "0" key is pressed.
42
+ # input 3, :play => "you-sound-cute"
43
+ # input :play => ["if-this-is-correct-press", 1, "otherwise-press", 2]
44
+ # input :interruptible => false, :play => ["you-cannot-interrupt-this-message"] # Disallow DTMF (keypad) interruption
45
+ # # until after all files are played.
46
+ #
47
+ # When specifying outputs to play, the playback of the sequence of files will stop
48
+ # immediately when the user presses the first digit.
49
+ #
50
+ # Accepted output types are:
51
+ # 1. Any object supported by detect_type (@see detect_type)
52
+ # 2. Any valid SSML document
53
+ # 3. An Hash with at least the :value key set to a supported object type, and other keys as options to the specific output
54
+ #
55
+ # :play usage examples
56
+ # input 1, :play => RubySpeech::SSML.draw { string "hello there" } # 1 digit, SSML document
57
+ # input 2, :play => "hello there" # 2 digits, string
58
+ # input 2, :play => {:value => Time.now, :strftime => "%H:%M"} # 2 digits, Hash with :value
59
+ # input :play => [ "the time is", {:value => Time.now, :strftime => "%H:%M"} ] # no digit limit, two mixed outputs
60
+ #
61
+ # The :timeout option works like a digit timeout, therefore each digit pressed
62
+ # causes the timer to reset. This is a much more user-friendly approach than an
63
+ # absolute timeout.
64
+ #
65
+ # Note that when the digit limit is not specified the :accept_key becomes "#".
66
+ # Otherwise there would be no way to end the collection of digits. You can
67
+ # obviously override this by passing in a new key with :accept_key.
68
+ #
69
+ # @return [String] The keypad input received. An empty string is returned in the
70
+ # absense of input. If the :accept_key argument was pressed, it
71
+ # will not appear in the output.
72
+ def input(*args, &block)
73
+ begin
74
+ input! *args, &block
75
+ rescue PlaybackError => e
76
+ logger.warn { e }
77
+ retry # If sound playback fails, play the remaining sound files and wait for digits
78
+ end
79
+ end
80
+
81
+ # Same as {#input}, but immediately raises an exception if sound playback fails
82
+ #
83
+ # @return (see #input)
84
+ # @raise [Adhearsion::PlaybackError] If a sound file cannot be played
85
+ def input!(*args, &block)
86
+ options = args.last.kind_of?(Hash) ? args.pop : {}
87
+ number_of_digits = args.shift
88
+
89
+ options[:play] = Array(case options[:play]
90
+ when String
91
+ options[:play]
92
+ when Array
93
+ options[:play].compact
94
+ when NilClass
95
+ []
96
+ else
97
+ [options[:play]]
98
+ end)
99
+
100
+ play_command = if options.has_key?(:interruptible) && options[:interruptible] == false
101
+ :play!
102
+ else
103
+ options[:interruptible] = true
104
+ :interruptible_play!
105
+ end
106
+
107
+ if options.has_key? :speak
108
+ raise ArgumentError, ':speak must be a Hash' unless options[:speak].is_a? Hash
109
+ raise ArgumentError, 'Must include a text string when requesting TTS fallback' unless options[:speak].has_key?(:text)
110
+ if options.has_key?(:speak) && options.has_key?(:play) && options[:play].size > 0
111
+ raise ArgumentError, 'Must specify only one of :play or :speak'
112
+ end
113
+ end
114
+
115
+ timeout = options[:timeout]
116
+ terminator = options[:terminator]
117
+
118
+ terminator = if terminator
119
+ terminator.to_s
120
+ elsif number_of_digits.nil? && !terminator.equal?(false)
121
+ '#'
122
+ end
123
+
124
+ if number_of_digits && number_of_digits < 0
125
+ logger.warn "Giving -1 to #input is now deprecated. Do not specify a first " +
126
+ "argument to allow unlimited digits." if number_of_digits == -1
127
+ raise ArgumentError, "The number of digits must be positive!"
128
+ end
129
+
130
+ buffer = ''
131
+ if options[:play].any?
132
+ # Consume the sound files one at a time. In the event of playback
133
+ # failure, this tells us which files remain unplayed.
134
+ while output = options[:play].shift
135
+ if output.class == Hash
136
+ argument = output.delete(:value)
137
+ raise ArgumentError, ':value has to be specified for each :play argument that is a Hash' if argument.nil?
138
+ output = [argument, output]
139
+ end
140
+ key = send play_command, output
141
+ key = nil if play_command == :play!
142
+ break if key
143
+ end
144
+ key ||= ''
145
+ # instead use a normal play command, :speak is basically an alias
146
+ elsif options[:speak]
147
+ speak_output = options[:speak].delete(:text)
148
+ key = send play_command, speak_output, options[:speak]
149
+ key = nil if play_command == :play!
150
+ else
151
+ key = wait_for_digit timeout
152
+ end
153
+
154
+ loop do
155
+ return buffer if key.nil?
156
+ if terminator
157
+ if key == terminator
158
+ return buffer
159
+ else
160
+ buffer << key
161
+ return buffer if number_of_digits && number_of_digits == buffer.length
162
+ end
163
+ else
164
+ buffer << key
165
+ return buffer if number_of_digits && number_of_digits == buffer.length
166
+ end
167
+ return buffer if block_given? && yield(buffer)
168
+ key = wait_for_digit timeout
169
+ end
170
+ end # #input!
171
+ end # Input
172
+ end
173
+ end