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
@@ -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