sevenscale-adhearsion 0.7.1003 → 0.8.0
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.
- data/CHANGELOG +8 -2
- data/EVENTS +11 -0
- data/Rakefile +92 -26
- data/adhearsion.gemspec +131 -23
- data/app_generators/ahn/ahn_generator.rb +21 -7
- data/app_generators/ahn/templates/.ahnrc +10 -4
- data/app_generators/ahn/templates/Rakefile +7 -2
- data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
- data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml +12 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
- data/app_generators/ahn/templates/components/simon_game/{lib/simon_game.rb → simon_game.rb} +14 -19
- data/app_generators/ahn/templates/config/startup.rb +3 -6
- data/app_generators/ahn/templates/dialplan.rb +2 -3
- data/app_generators/ahn/templates/events.rb +32 -6
- data/bin/jahn +10 -0
- data/examples/asterisk_manager_interface/standalone.rb +51 -0
- data/lib/adhearsion/cli.rb +140 -23
- data/lib/adhearsion/component_manager/component_tester.rb +55 -0
- data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
- data/lib/adhearsion/component_manager.rb +169 -238
- data/lib/adhearsion/events_support.rb +59 -237
- data/lib/adhearsion/{core_extensions → foundation}/all.rb +0 -0
- data/lib/adhearsion/{blank_slate.rb → foundation/blank_slate.rb} +0 -0
- data/lib/adhearsion/{core_extensions → foundation}/custom_daemonizer.rb +0 -0
- data/lib/adhearsion/foundation/event_socket.rb +203 -0
- data/lib/adhearsion/foundation/future_resource.rb +36 -0
- data/lib/adhearsion/{core_extensions → foundation}/global.rb +0 -0
- data/lib/adhearsion/{core_extensions → foundation}/metaprogramming.rb +0 -0
- data/lib/adhearsion/foundation/numeric.rb +13 -0
- data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
- data/lib/adhearsion/{core_extensions → foundation}/relationship_properties.rb +2 -0
- data/lib/adhearsion/foundation/string.rb +26 -0
- data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
- data/lib/adhearsion/{core_extensions → foundation}/thread_safety.rb +0 -0
- data/lib/adhearsion/host_definitions.rb +5 -1
- data/lib/adhearsion/initializer/asterisk.rb +33 -11
- data/lib/adhearsion/initializer/configuration.rb +58 -6
- data/lib/adhearsion/initializer/database.rb +3 -46
- data/lib/adhearsion/initializer/drb.rb +9 -3
- data/lib/adhearsion/initializer/freeswitch.rb +3 -3
- data/lib/adhearsion/initializer/rails.rb +1 -1
- data/lib/adhearsion/initializer.rb +213 -87
- data/lib/adhearsion/tasks/deprecations.rb +59 -0
- data/lib/adhearsion/tasks.rb +2 -1
- data/lib/adhearsion/version.rb +3 -3
- data/lib/adhearsion/voip/asterisk/agi_server.rb +6 -6
- data/lib/adhearsion/voip/asterisk/commands.rb +100 -2
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1754 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
- data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
- data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
- data/lib/adhearsion/voip/asterisk.rb +1 -8
- data/lib/adhearsion/voip/call.rb +5 -1
- data/lib/adhearsion/voip/dial_plan.rb +74 -61
- data/lib/adhearsion/voip/dsl/dialing_dsl.rb +1 -1
- data/lib/adhearsion/voip/dsl/dialplan/parser.rb +2 -6
- data/lib/adhearsion/voip/dsl/numerical_string.rb +2 -2
- data/lib/adhearsion/voip/freeswitch/oes_server.rb +2 -2
- data/lib/adhearsion.rb +16 -11
- data/lib/theatre/README.markdown +64 -0
- data/lib/theatre/callback_definition_loader.rb +84 -0
- data/lib/theatre/guid.rb +23 -0
- data/lib/theatre/invocation.rb +121 -0
- data/lib/theatre/namespace_manager.rb +153 -0
- data/lib/theatre/version.rb +2 -0
- data/lib/theatre.rb +151 -0
- metadata +60 -147
- data/Manifest.txt +0 -151
- data/README.txt +0 -5
- data/ahn_generators/component/USAGE +0 -5
- data/ahn_generators/component/component_generator.rb +0 -57
- data/ahn_generators/component/templates/configuration.rb +0 -0
- data/ahn_generators/component/templates/lib/lib.rb.erb +0 -3
- data/ahn_generators/component/templates/test/test.rb.erb +0 -12
- data/ahn_generators/component/templates/test/test_helper.rb +0 -14
- data/app_generators/ahn/templates/components/simon_game/configuration.rb +0 -0
- data/app_generators/ahn/templates/components/simon_game/test/test_helper.rb +0 -14
- data/app_generators/ahn/templates/components/simon_game/test/test_simon_game.rb +0 -31
- data/lib/adhearsion/core_extensions/array.rb +0 -0
- data/lib/adhearsion/core_extensions/guid.rb +0 -5
- data/lib/adhearsion/core_extensions/hash.rb +0 -0
- data/lib/adhearsion/core_extensions/numeric.rb +0 -4
- data/lib/adhearsion/core_extensions/proc.rb +0 -0
- data/lib/adhearsion/core_extensions/pseudo_uuid.rb +0 -11
- data/lib/adhearsion/core_extensions/publishable.rb +0 -73
- data/lib/adhearsion/core_extensions/string.rb +0 -26
- data/lib/adhearsion/core_extensions/thread.rb +0 -13
- data/lib/adhearsion/core_extensions/time.rb +0 -0
- data/lib/adhearsion/distributed/gateways/dbus_gateway.rb +0 -0
- data/lib/adhearsion/distributed/gateways/osa_gateway.rb +0 -0
- data/lib/adhearsion/distributed/gateways/rest_gateway.rb +0 -9
- data/lib/adhearsion/distributed/gateways/soap_gateway.rb +0 -9
- data/lib/adhearsion/distributed/gateways/xmlrpc_gateway.rb +0 -9
- data/lib/adhearsion/distributed/peer_finder.rb +0 -0
- data/lib/adhearsion/distributed/remote_cli.rb +0 -0
- data/lib/adhearsion/hooks.rb +0 -57
- data/lib/adhearsion/initializer/paths.rb +0 -55
- data/lib/adhearsion/voip/asterisk/ami/actions.rb +0 -238
- data/lib/adhearsion/voip/asterisk/ami/machine.rb +0 -871
- data/lib/adhearsion/voip/asterisk/ami/machine.rl +0 -109
- data/lib/adhearsion/voip/asterisk/ami/parser.rb +0 -262
- data/lib/adhearsion/voip/asterisk/ami.rb +0 -147
- data/spec/fixtures/dialplan.rb +0 -3
- data/spec/initializer/test_configuration.rb +0 -267
- data/spec/initializer/test_loading.rb +0 -162
- data/spec/initializer/test_paths.rb +0 -43
- data/spec/sample.rb +0 -9
- data/spec/silence.rb +0 -10
- data/spec/test_ahn_command.rb +0 -149
- data/spec/test_code_quality.rb +0 -87
- data/spec/test_component_manager.rb +0 -97
- data/spec/test_constants.rb +0 -8
- data/spec/test_drb.rb +0 -104
- data/spec/test_events.rb +0 -136
- data/spec/test_helper.rb +0 -106
- data/spec/test_hooks.rb +0 -15
- data/spec/test_host_definitions.rb +0 -79
- data/spec/test_initialization.rb +0 -124
- data/spec/test_logging.rb +0 -80
- data/spec/test_relationship_properties.rb +0 -54
- data/spec/voip/asterisk/ami_response_definitions.rb +0 -23
- data/spec/voip/asterisk/config_file_generators/test_agents.rb +0 -253
- data/spec/voip/asterisk/config_file_generators/test_queues.rb +0 -325
- data/spec/voip/asterisk/config_file_generators/test_voicemail.rb +0 -306
- data/spec/voip/asterisk/menu_command/test_calculated_match.rb +0 -111
- data/spec/voip/asterisk/menu_command/test_matchers.rb +0 -98
- data/spec/voip/asterisk/mock_ami_server.rb +0 -176
- data/spec/voip/asterisk/test_agi_server.rb +0 -453
- data/spec/voip/asterisk/test_ami.rb +0 -227
- data/spec/voip/asterisk/test_commands.rb +0 -2006
- data/spec/voip/asterisk/test_config_manager.rb +0 -129
- data/spec/voip/dsl/dispatcher_spec_helper.rb +0 -45
- data/spec/voip/dsl/test_dialing_dsl.rb +0 -268
- data/spec/voip/dsl/test_dispatcher.rb +0 -82
- data/spec/voip/dsl/test_parser.rb +0 -87
- data/spec/voip/freeswitch/test_basic_connection_manager.rb +0 -39
- data/spec/voip/freeswitch/test_inbound_connection_manager.rb +0 -39
- data/spec/voip/freeswitch/test_oes_server.rb +0 -9
- data/spec/voip/test_call_routing.rb +0 -127
- data/spec/voip/test_dialplan_manager.rb +0 -442
- data/spec/voip/test_numerical_string.rb +0 -48
- data/spec/voip/test_phone_number.rb +0 -36
- data/test/test_ahn_generator.rb +0 -59
- data/test/test_component_generator.rb +0 -52
- data/test/test_generator_helper.rb +0 -20
@@ -1,6 +1,45 @@
|
|
1
1
|
module Adhearsion
|
2
2
|
|
3
|
+
class << self
|
4
|
+
|
5
|
+
##
|
6
|
+
# Shuts down the framework.
|
7
|
+
#
|
8
|
+
def self.shutdown!
|
9
|
+
ahn_log "Shutting down gracefully at #{Time.now}."
|
10
|
+
Events.stop!
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
3
15
|
class PathString < String
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
##
|
20
|
+
# Will return a PathString for the application root folder to which the specified arbitrarily nested subfolder belongs.
|
21
|
+
# It works by traversing parent directories looking for the .ahnrc file. If no .ahnrc is found, nil is returned.
|
22
|
+
#
|
23
|
+
# @param [String] folder The path to the directory which should be a
|
24
|
+
# @return [nil] if the subdirectory does not belong to a parent Adhearsion app directory
|
25
|
+
# @return [PathString] if a directory is found
|
26
|
+
#
|
27
|
+
def from_application_subdirectory(folder)
|
28
|
+
folder = File.expand_path folder
|
29
|
+
ahn_rc = nil
|
30
|
+
|
31
|
+
until ahn_rc || folder == "/"
|
32
|
+
possible_ahn_rc = File.join(folder, ".ahnrc")
|
33
|
+
if File.exists?(possible_ahn_rc)
|
34
|
+
ahn_rc = possible_ahn_rc
|
35
|
+
else
|
36
|
+
folder = File.expand_path(folder + "/..")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
ahn_rc ? new(folder) : nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
4
43
|
attr_accessor :component_path, :dialplan_path, :log_path
|
5
44
|
|
6
45
|
def initialize(path)
|
@@ -27,10 +66,6 @@ module Adhearsion
|
|
27
66
|
self.base_path = original_path
|
28
67
|
end
|
29
68
|
|
30
|
-
def dial_plan_named(name)
|
31
|
-
File.join(dialplan_path, name)
|
32
|
-
end
|
33
|
-
|
34
69
|
private
|
35
70
|
def build_path_for(path)
|
36
71
|
File.join(to_s, path)
|
@@ -66,8 +101,6 @@ module Adhearsion
|
|
66
101
|
|
67
102
|
attr_reader :path, :daemon, :pid_file, :log_file, :ahn_app_log_directory
|
68
103
|
|
69
|
-
DEFAULT_FRAMEWORK_EVENT_CALLBACK_NAMES = [:after_initialized, :shutdown]
|
70
|
-
|
71
104
|
# Creation of pid_files
|
72
105
|
#
|
73
106
|
# - You may want to have Adhearsion create a process identification
|
@@ -89,48 +122,41 @@ module Adhearsion
|
|
89
122
|
|
90
123
|
def start
|
91
124
|
self.class.ahn_root = path
|
125
|
+
|
92
126
|
resolve_pid_file_path
|
93
127
|
resolve_log_file_path
|
94
128
|
switch_to_root_directory
|
95
129
|
catch_termination_signal
|
96
130
|
bootstrap_rc
|
97
|
-
load_all_init_files
|
98
|
-
init_modules
|
99
131
|
daemonize! if should_daemonize?
|
100
132
|
initialize_log_file
|
133
|
+
load_all_init_files
|
134
|
+
init_components_subsystem
|
135
|
+
init_modules
|
136
|
+
init_events_subsystem
|
101
137
|
create_pid_file if pid_file
|
102
138
|
load_components
|
103
|
-
|
139
|
+
init_events_file
|
140
|
+
|
104
141
|
ahn_log "Adhearsion initialized!"
|
105
142
|
|
106
143
|
trigger_after_initialized_hooks
|
107
|
-
|
144
|
+
join_important_threads
|
108
145
|
|
109
146
|
self
|
110
147
|
end
|
111
148
|
|
112
|
-
def
|
113
|
-
|
114
|
-
framework = Events.register_namespace_path(:framework)
|
115
|
-
DEFAULT_FRAMEWORK_EVENT_CALLBACK_NAMES.each do |framework_event_callback_name|
|
116
|
-
framework.register_callback_name framework_event_callback_name
|
117
|
-
end
|
118
|
-
Events.load_definitions_from_files *all_events
|
119
|
-
else
|
120
|
-
ahn_log.events.warn 'No "events" section in .ahnrc. Skipping its initialization.'
|
121
|
-
end
|
149
|
+
def default_pid_path
|
150
|
+
File.join AHN_ROOT, 'adhearsion.pid'
|
122
151
|
end
|
123
152
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
else
|
131
|
-
Logging::AdhearsionLogger.outputters << file_logger
|
153
|
+
def resolve_pid_file_path
|
154
|
+
@pid_file = if pid_file.equal?(true) then default_pid_path
|
155
|
+
elsif pid_file then pid_file
|
156
|
+
elsif pid_file.equal?(false) then nil
|
157
|
+
# FIXME @pid_file = @daemon? Assignment or equality? I'm assuming equality.
|
158
|
+
else @pid_file = @daemon ? default_pid_path : nil
|
132
159
|
end
|
133
|
-
Logging::DefaultAdhearsionLogger.redefine_outputters
|
134
160
|
end
|
135
161
|
|
136
162
|
def resolve_log_file_path
|
@@ -138,18 +164,101 @@ module Adhearsion
|
|
138
164
|
@log_file = File.expand_path(ahn_app_log_directory + "/adhearsion.log")
|
139
165
|
end
|
140
166
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
144
|
-
|
167
|
+
def switch_to_root_directory
|
168
|
+
Dir.chdir AHN_ROOT
|
169
|
+
end
|
170
|
+
|
171
|
+
def catch_termination_signal
|
172
|
+
%w'INT TERM'.each do |process_signal|
|
173
|
+
trap process_signal do
|
174
|
+
ahn_log "Shutting down gracefully at #{Time.now}."
|
175
|
+
Events.trigger :shutdown
|
176
|
+
exit
|
145
177
|
end
|
146
|
-
|
147
|
-
|
148
|
-
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
##
|
182
|
+
# This step in the initialization process loads the .ahnrc in the given app folder. With the information in .ahnrc, we
|
183
|
+
# can continue the initialization knowing where certain files are specifically.
|
184
|
+
#
|
185
|
+
def bootstrap_rc
|
186
|
+
rules = self.class.get_rules_from AHN_ROOT
|
187
|
+
|
188
|
+
AHN_CONFIG.ahnrc = rules
|
189
|
+
|
190
|
+
# DEPRECATION: Check if the old paths format is being used. If so, abort and notify.
|
191
|
+
if rules.has_key?("paths") && rules["paths"].kind_of?(Hash)
|
192
|
+
paths = rules["paths"].each_pair do |key,value|
|
193
|
+
if value.kind_of?(Hash)
|
194
|
+
if value.has_key?("directory") || value.has_key?("pattern")
|
195
|
+
puts
|
196
|
+
puts *caller
|
197
|
+
puts
|
198
|
+
|
199
|
+
abort <<-WARNING
|
200
|
+
Deprecation Warning
|
201
|
+
-------------------
|
202
|
+
The (hidden) .ahnrc file in this app is of an older format and needs to be fixed.
|
203
|
+
|
204
|
+
There is a rake task to automatically fix it or you can do it manually. Note: it's
|
205
|
+
best if you do it manually so you can retain the YAML comments in your .ahnrc file.
|
206
|
+
|
207
|
+
The rake task is called "deprecations:fix_ahnrc_path_format".
|
208
|
+
|
209
|
+
To do it manually, find all entries in the "paths" section of your ".ahnrc" file
|
210
|
+
which look like the following:
|
211
|
+
|
212
|
+
paths:
|
213
|
+
key_name_could_be_anything:
|
214
|
+
directory: some_folder
|
215
|
+
pattern: *.rb
|
216
|
+
|
217
|
+
Note: the "models" section had this syntax before:
|
218
|
+
|
219
|
+
models:
|
220
|
+
directory: models
|
221
|
+
pattern: "*.rb"
|
222
|
+
|
223
|
+
The NEW syntax is as follows (using models as an example):
|
224
|
+
|
225
|
+
models: models/*.rb
|
226
|
+
|
227
|
+
This new format is much cleaner.
|
228
|
+
|
229
|
+
Adhearsion will abort until you fix this. Sorry for the incovenience.
|
230
|
+
WARNING
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
gems = rules['gems']
|
237
|
+
if gems.kind_of?(Hash) && gems.any? && respond_to?(:gem)
|
238
|
+
gems.each_pair do |gem_name,properties_hash|
|
239
|
+
if properties_hash && properties_hash["version"]
|
240
|
+
gem gem_name, properties_hash["version"]
|
241
|
+
else
|
242
|
+
gem gem_name
|
243
|
+
end
|
244
|
+
if properties_hash
|
245
|
+
case properties_hash["require"]
|
246
|
+
when Array
|
247
|
+
properties_hash["require"].each { |lib| require lib }
|
248
|
+
when String
|
249
|
+
require properties_hash["require"]
|
250
|
+
end
|
251
|
+
end
|
149
252
|
end
|
150
253
|
end
|
151
254
|
end
|
152
255
|
|
256
|
+
def load_all_init_files
|
257
|
+
init_files_from_rc = AHN_CONFIG.files_from_setting("paths", "init").map { |file| File.expand_path(file) }
|
258
|
+
already_loaded_init_files = Array(@loaded_init_files).map { |file| File.expand_path(file) }
|
259
|
+
(init_files_from_rc - already_loaded_init_files).each { |init| load init }
|
260
|
+
end
|
261
|
+
|
153
262
|
def init_modules
|
154
263
|
require 'adhearsion/initializer/database.rb'
|
155
264
|
require 'adhearsion/initializer/asterisk.rb'
|
@@ -164,28 +273,23 @@ module Adhearsion
|
|
164
273
|
# FreeswitchInitializer.start if AHN_CONFIG.freeswitch_enabled?
|
165
274
|
end
|
166
275
|
|
167
|
-
def
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
276
|
+
def init_events_subsystem
|
277
|
+
application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
|
278
|
+
if application_events_files.any?
|
279
|
+
Events.register_callback(:shutdown) do
|
280
|
+
ahn_log.events "Performing a graceful stop of events subsystem"
|
281
|
+
Events.framework_theatre.graceful_stop!
|
282
|
+
end
|
283
|
+
Events.framework_theatre.start!
|
284
|
+
else
|
285
|
+
ahn_log.events.warn 'No entries in the "events" section of .ahnrc. Skipping its initialization.'
|
173
286
|
end
|
174
287
|
end
|
175
288
|
|
176
|
-
def
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
def catch_termination_signal
|
181
|
-
Hooks::TearDown.catch_termination_signals
|
182
|
-
end
|
183
|
-
|
184
|
-
def load_all_init_files
|
185
|
-
if Paths.manager_for? "init"
|
186
|
-
init_files_from_rc = all_inits.map { |file| File.expand_path(file) }
|
187
|
-
already_loaded_init_files = Array(@loaded_init_files).map { |file| File.expand_path(file) }
|
188
|
-
(init_files_from_rc - already_loaded_init_files).each { |init| load init }
|
289
|
+
def init_events_file
|
290
|
+
application_events_files = AHN_CONFIG.files_from_setting("paths", "events")
|
291
|
+
application_events_files.each do |file|
|
292
|
+
Events.framework_theatre.load_events_file file
|
189
293
|
end
|
190
294
|
end
|
191
295
|
|
@@ -199,47 +303,69 @@ module Adhearsion
|
|
199
303
|
daemonize log_file
|
200
304
|
end
|
201
305
|
|
202
|
-
def
|
203
|
-
|
204
|
-
|
306
|
+
def initialize_log_file
|
307
|
+
Dir.mkdir(ahn_app_log_directory) unless File.directory? ahn_app_log_directory
|
308
|
+
file_logger = Log4r::FileOutputter.new("Main Adhearsion log file", :filename => log_file, :trunc => false)
|
309
|
+
|
310
|
+
if should_daemonize?
|
311
|
+
Logging::AdhearsionLogger.outputters = file_logger
|
312
|
+
else
|
313
|
+
Logging::AdhearsionLogger.outputters << file_logger
|
314
|
+
end
|
315
|
+
Logging::DefaultAdhearsionLogger.redefine_outputters
|
205
316
|
end
|
206
317
|
|
207
|
-
def
|
208
|
-
|
318
|
+
def create_pid_file(file = pid_file)
|
319
|
+
if file
|
320
|
+
File.open pid_file, 'w' do |file|
|
321
|
+
file.puts Process.pid
|
322
|
+
end
|
323
|
+
|
324
|
+
Events.register_callback :shutdown do
|
325
|
+
File.delete(pid_file) if File.exists?(pid_file)
|
326
|
+
end
|
327
|
+
end
|
209
328
|
end
|
210
329
|
|
211
|
-
def
|
212
|
-
|
330
|
+
def init_components_subsystem
|
331
|
+
@components_directory = File.expand_path "components"
|
332
|
+
if File.directory? @components_directory
|
333
|
+
Components.component_manager = Components::ComponentManager.new @components_directory
|
334
|
+
Kernel.send(:const_set, :COMPONENTS, Components.component_manager.lazy_config_loader)
|
335
|
+
Components.component_manager.globalize_global_scope!
|
336
|
+
Components.component_manager.extend_object_with(Theatre::CallbackDefinitionLoader, :events)
|
337
|
+
else
|
338
|
+
ahn_log.warn "No components directory found. Not initializing any components."
|
339
|
+
end
|
213
340
|
end
|
214
341
|
|
215
|
-
def
|
216
|
-
|
217
|
-
|
218
|
-
paths = rules['paths']
|
219
|
-
paths.each_pair do |path_name, pattern_or_ruleset|
|
220
|
-
if pattern_or_ruleset.kind_of? Hash
|
221
|
-
directory, pattern = pattern_or_ruleset['directory'] || '.', pattern_or_ruleset['pattern'] || '*'
|
222
|
-
Paths.manager_for path_name, :pattern => File.join(directory, pattern)
|
223
|
-
else
|
224
|
-
directory, pattern = '.', pattern_or_ruleset
|
225
|
-
Paths.manager_for path_name, :pattern => File.join(directory,pattern)
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
|
-
gems = rules['gems']
|
230
|
-
if gems.kind_of?(Hash) && gems.any? && respond_to?(:gem)
|
231
|
-
gems.each_pair do |gem_name,properties_hash|
|
232
|
-
if properties_hash && properties_hash["version"]
|
233
|
-
gem gem_name, properties_hash["version"]
|
234
|
-
else
|
235
|
-
gem gem_name
|
236
|
-
end
|
237
|
-
end
|
342
|
+
def load_components
|
343
|
+
if Components.component_manager
|
344
|
+
Components.component_manager.load_components
|
238
345
|
end
|
239
346
|
end
|
240
347
|
|
241
|
-
def
|
242
|
-
|
348
|
+
def trigger_after_initialized_hooks
|
349
|
+
Events.trigger_immediately :after_initialized
|
350
|
+
end
|
351
|
+
|
352
|
+
##
|
353
|
+
# This method will block Thread.main() until calling join() has returned for all Threads in IMPORTANT_THREADS.
|
354
|
+
# Note: IMPORTANT_THREADS won't always contain Thread instances. It simply requires the objects respond to join().
|
355
|
+
#
|
356
|
+
def join_important_threads
|
357
|
+
# Note: we're using this ugly accumulator to ensure that all threads have ended since IMPORTANT_THREADS will almost
|
358
|
+
# certainly change sizes after this method is called.
|
359
|
+
index = 0
|
360
|
+
until index == IMPORTANT_THREADS.size
|
361
|
+
begin
|
362
|
+
IMPORTANT_THREADS[index].join
|
363
|
+
rescue => e
|
364
|
+
ahn_log.error "Error after join()ing Thread #{thread.inspect}. #{e.message}"
|
365
|
+
ensure
|
366
|
+
index = index + 1
|
367
|
+
end
|
368
|
+
end
|
243
369
|
end
|
244
370
|
|
245
371
|
class InitializationFailedError < Exception; end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
namespace :deprecations do
|
2
|
+
desc <<-DESC
|
3
|
+
Older versions of Adhearsion had an .ahnrc "paths" section similar to the following...
|
4
|
+
|
5
|
+
paths:
|
6
|
+
models:
|
7
|
+
directory: models
|
8
|
+
pattern: *.rb
|
9
|
+
|
10
|
+
This has been deprecated. The new format is this:
|
11
|
+
|
12
|
+
paths:
|
13
|
+
models: {models,gui/app/models}/*.rb
|
14
|
+
|
15
|
+
This Rake task will fix your .ahnrc if you have
|
16
|
+
DESC
|
17
|
+
task :fix_ahnrc_path_format do
|
18
|
+
puts "\nThis will remove all comments from your .ahnrc file. A backup will be created as .ahnrc.backup."
|
19
|
+
puts "If you wish to do this manually to preserve your comments, simply overwrite .ahnrc with .ahnrc.backup"
|
20
|
+
puts "and apply the change manually."
|
21
|
+
puts
|
22
|
+
|
23
|
+
require 'fileutils'
|
24
|
+
require 'yaml'
|
25
|
+
|
26
|
+
ahnrc_file = File.expand_path(".ahnrc")
|
27
|
+
|
28
|
+
FileUtils.cp ahnrc_file, ahnrc_file + ".backup"
|
29
|
+
ahnrc_contents = YAML.load_file ahnrc_file
|
30
|
+
|
31
|
+
abort '.ahnrc does not have a "paths" section!' unless ahnrc_contents.has_key? "paths"
|
32
|
+
|
33
|
+
paths = ahnrc_contents["paths"]
|
34
|
+
paths.clone.each_pair do |key,value|
|
35
|
+
if value.kind_of?(Hash)
|
36
|
+
if value.has_key?("directory") || value.has_key?("pattern")
|
37
|
+
directory, pattern = value.values_at "directory", "pattern"
|
38
|
+
new_path = "#{directory}/#{pattern}"
|
39
|
+
|
40
|
+
puts "!!! CHANGING KEY #{key.inspect}!"
|
41
|
+
puts "!!! NEW: #{new_path.inspect}"
|
42
|
+
puts "!!! OLD:\n#{{key => value}.to_yaml.sub("---", "")}\n\n"
|
43
|
+
|
44
|
+
paths[key] = new_path
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
ahnrc_contents["paths"] = paths
|
50
|
+
new_yaml = ahnrc_contents.to_yaml.gsub("--- \n", "")
|
51
|
+
|
52
|
+
puts "New .ahnrc file:\n" + ("#" * 25) + "\n"
|
53
|
+
puts new_yaml
|
54
|
+
puts '#' * 25
|
55
|
+
|
56
|
+
File.open(ahnrc_file, "w") { |file| file.puts new_yaml }
|
57
|
+
puts "Wrote to .ahnrc. Done!"
|
58
|
+
end
|
59
|
+
end
|
data/lib/adhearsion/tasks.rb
CHANGED
@@ -4,6 +4,7 @@ require 'adhearsion/tasks/database'
|
|
4
4
|
require 'adhearsion/tasks/testing'
|
5
5
|
require 'adhearsion/tasks/generating'
|
6
6
|
require 'adhearsion/tasks/lint'
|
7
|
+
require 'adhearsion/tasks/deprecations'
|
7
8
|
|
8
9
|
namespace :adhearsion do
|
9
10
|
desc "Dump useful information about this application's adhearsion environment"
|
@@ -12,4 +13,4 @@ namespace :adhearsion do
|
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
15
|
-
task :default => "adhearsion:about"
|
16
|
+
task :default => "adhearsion:about"
|
data/lib/adhearsion/version.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Adhearsion #:nodoc:
|
2
2
|
module VERSION #:nodoc:
|
3
|
-
MAJOR = 0
|
4
|
-
MINOR =
|
5
|
-
TINY =
|
3
|
+
MAJOR = 0 unless defined? MAJOR
|
4
|
+
MINOR = 8 unless defined? MINOR
|
5
|
+
TINY = 0 unless defined? TINY
|
6
6
|
|
7
7
|
STRING = [MAJOR, MINOR, TINY].join('.') unless defined? STRING
|
8
8
|
end
|
@@ -12,8 +12,8 @@ module Adhearsion
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def serve(io)
|
15
|
-
|
16
|
-
|
15
|
+
call = Adhearsion.receive_call_from(io)
|
16
|
+
Events.trigger_immediately([:asterisk, :before_call], call)
|
17
17
|
ahn_log.agi "Handling call with variables #{call.variables.inspect}"
|
18
18
|
|
19
19
|
return DialPlan::ConfirmationManager.handle(call) if DialPlan::ConfirmationManager.confirmation_call?(call)
|
@@ -26,16 +26,16 @@ module Adhearsion
|
|
26
26
|
call.hangup!
|
27
27
|
rescue FailedExtensionCallException => failed_call
|
28
28
|
begin
|
29
|
-
ahn_log.agi "Received \"failed\" meta-call with :failed_reason => #{failed_call.call.failed_reason.inspect}. Executing
|
30
|
-
|
29
|
+
ahn_log.agi "Received \"failed\" meta-call with :failed_reason => #{failed_call.call.failed_reason.inspect}. Executing Executing /asterisk/failed_call event callbacks."
|
30
|
+
Events.trigger [:asterisk, :failed_call], failed_call.call
|
31
31
|
call.hangup!
|
32
32
|
rescue => e
|
33
33
|
ahn_log.agi.error e
|
34
34
|
end
|
35
35
|
rescue HungupExtensionCallException => hungup_call
|
36
36
|
begin
|
37
|
-
ahn_log.agi "Received \"h\" meta-call. Executing
|
38
|
-
|
37
|
+
ahn_log.agi "Received \"h\" meta-call. Executing /asterisk/call_hangup event callbacks."
|
38
|
+
Events.trigger [:asterisk, :call_hangup], hungup_call.call
|
39
39
|
call.hangup!
|
40
40
|
rescue => e
|
41
41
|
ahn_log.agi.error e
|
@@ -7,6 +7,22 @@ module Adhearsion
|
|
7
7
|
module Commands
|
8
8
|
|
9
9
|
RESPONSE_PREFIX = "200 result=" unless defined? RESPONSE_PREFIX
|
10
|
+
|
11
|
+
# These are the status messages that asterisk will issue after a dial command is executed.
|
12
|
+
# More information here: http://www.voip-info.org/wiki/index.php?page=Asterisk+variable+DIALSTATUS
|
13
|
+
# Here is a current list of dial status messages which are not all necessarily supported by adhearsion:
|
14
|
+
#
|
15
|
+
# ANSWER: Call is answered. A successful dial. The caller reached the callee.
|
16
|
+
# BUSY: Busy signal. The dial command reached its number but the number is busy.
|
17
|
+
# NOANSWER: No answer. The dial command reached its number, the number rang for too long, then the dial timed out.
|
18
|
+
# CANCEL: Call is cancelled. The dial command reached its number but the caller hung up before the callee picked up.
|
19
|
+
# CONGESTION: Congestion. This status is usually a sign that the dialled number is not recognised.
|
20
|
+
# CHANUNAVAIL: Channel unavailable. On SIP, peer may not be registered.
|
21
|
+
# DONTCALL: Privacy mode, callee rejected the call
|
22
|
+
# TORTURE: Privacy mode, callee chose to send caller to torture menu
|
23
|
+
# INVALIDARGS: Error parsing Dial command arguments (added for Asterisk 1.4.1, SVN r53135-53136)
|
24
|
+
#
|
25
|
+
#
|
10
26
|
DIAL_STATUSES = Hash.new(:unknown).merge(:answer => :answered,
|
11
27
|
:congestion => :congested,
|
12
28
|
:busy => :busy,
|
@@ -36,17 +52,47 @@ module Adhearsion
|
|
36
52
|
end
|
37
53
|
end
|
38
54
|
|
55
|
+
# This method is the underlying method executed by nearly all the command methods in this module.
|
56
|
+
# It is used to send the plaintext commands in the proper AGI format over TCP/IP back to an Asterisk server via the
|
57
|
+
# FAGI protocol.
|
58
|
+
# It is not recommended that you call this method directly unless you plan to write a new command method
|
59
|
+
# in which case use this method you to communicate directly with an Asterisk server via the FAGI protocol.
|
60
|
+
# For more information about FAGI visit: http://www.voip-info.org/wiki/view/Asterisk+FastAGI
|
39
61
|
def raw_response(message = nil)
|
40
62
|
ahn_log.agi.debug ">>> #{message}"
|
41
63
|
write message if message
|
42
64
|
read
|
43
65
|
end
|
44
66
|
|
67
|
+
# The answer command must be called first before any other commands can be issued.
|
68
|
+
# In typical adhearsion applications the answer command is called by default as soon
|
69
|
+
# as a call is transfered to a valid context in dialplan.rb.
|
70
|
+
# If you do not want your adhearsion application to automatically issue an answer command,
|
71
|
+
# then you must edit your startup.rb file and configure this setting.
|
72
|
+
# Keep in mind that you should not need to issue another answer command after
|
73
|
+
# an answer command has already been issued either explicitly by your code or implicitly
|
74
|
+
# by the standard adhearsion configuration.
|
45
75
|
def answer
|
46
76
|
raw_response "ANSWER"
|
47
77
|
true
|
48
78
|
end
|
49
79
|
|
80
|
+
# This asterisk dialplan command allows you to instruct Asterisk to start applications
|
81
|
+
# which are typically run from extensions.conf. For a complete list of these commands
|
82
|
+
# please visit: http://www.voip-info.org/wiki/view/Asterisk+-+documentation+of+application+commands
|
83
|
+
#
|
84
|
+
# The most common commands are already made available through the FAGI interface provided
|
85
|
+
# by this code base. For commands that do not fall into this category, then exec is what you
|
86
|
+
# should use.
|
87
|
+
#
|
88
|
+
# For example, if there are specific asterisk modules you have loaded that will not
|
89
|
+
# available through the standard commands provided through FAGI - then you can used EXEC.
|
90
|
+
#
|
91
|
+
# Example:
|
92
|
+
# execute 'SIPAddHeader', '"Call-Info: answer-after=0"
|
93
|
+
#
|
94
|
+
# Using execute in this way will add a header to an existing SIP call.
|
95
|
+
#
|
50
96
|
def execute(application, *arguments)
|
51
97
|
result = raw_response("EXEC #{application} #{arguments * '|'}")
|
52
98
|
return false if error?(result)
|
@@ -54,6 +100,10 @@ module Adhearsion
|
|
54
100
|
end
|
55
101
|
|
56
102
|
# Hangs up the current channel.
|
103
|
+
# After this command is issued, your application will stop executing.
|
104
|
+
# This should be used in the same way you would call the ruby exit() method to exit an application.
|
105
|
+
# If it is necessary to do some additional cleanup tasks before returning control back to asterisk, then
|
106
|
+
# make sure you have setup a begin...ensure block in the context of your adhearsion application dialplan.
|
57
107
|
def hangup
|
58
108
|
raw_response 'HANGUP'
|
59
109
|
end
|
@@ -116,10 +166,12 @@ module Adhearsion
|
|
116
166
|
block.call(next_message)
|
117
167
|
end
|
118
168
|
|
169
|
+
# This command shouled be used to advance to the next message in the Asterisk Comedian Voicemail application
|
119
170
|
def next_message
|
120
171
|
@call.inbox.pop
|
121
172
|
end
|
122
173
|
|
174
|
+
# This command should be used to check if a message is waiting on the Asterisk Comedian Voicemail application.
|
123
175
|
def messages_waiting?
|
124
176
|
not @call.inbox.empty?
|
125
177
|
end
|
@@ -482,6 +534,10 @@ module Adhearsion
|
|
482
534
|
execute "MeetMe", conference_id, command_flags, options[:pin]
|
483
535
|
end
|
484
536
|
|
537
|
+
# Issue this command to access a channel variable that exists in the asterisk dialplan (i.e. extensions.conf)
|
538
|
+
# A complete description is available here: http://www.voip-info.org/wiki/view/get+variable
|
539
|
+
# Use get_variable to pass information from other modules or high level configurations from the asterisk dialplan
|
540
|
+
# to the adhearsion dialplan.
|
485
541
|
def get_variable(variable_name)
|
486
542
|
result = raw_response("GET VARIABLE #{variable_name}")
|
487
543
|
case result
|
@@ -492,6 +548,12 @@ module Adhearsion
|
|
492
548
|
end
|
493
549
|
end
|
494
550
|
|
551
|
+
# Use set_variable to pass information back to the asterisk dial plan.
|
552
|
+
# A complete decription is available here: http://www.voip-info.org/wiki/view/set+variable
|
553
|
+
# Keep in mind that the variables are not global variables. These variables only exist for the channel
|
554
|
+
# related to the call that is being serviced by the particular instance of your adhearsion application.
|
555
|
+
# You will not be able to pass information back to the asterisk dialplan for other instances of your adhearsion
|
556
|
+
# application to share. Once the channel is "hungup" then the variables are cleared and their information is gone.
|
495
557
|
def set_variable(variable_name, value)
|
496
558
|
raw_response("SET VARIABLE %s %p" % [variable_name.to_s, value.to_s]) == "200 result=1"
|
497
559
|
end
|
@@ -580,6 +642,42 @@ module Adhearsion
|
|
580
642
|
voicemail_main
|
581
643
|
end
|
582
644
|
|
645
|
+
# Use this command to dial an extension i.e. "phone number" in asterisk
|
646
|
+
# This command maps to the Asterisk DIAL command in the asterisk dialplan: http://www.voip-info.org/wiki-Asterisk+cmd+Dial
|
647
|
+
#
|
648
|
+
# The first parameter, number, must be a string that represents the extension or "number" that asterisk should dial.
|
649
|
+
# Be careful to not just specify a number like 5001, 9095551001
|
650
|
+
# You must specify a properly formatted string as Asterisk would expect to use in order to understand
|
651
|
+
# whether the call should be dialed using SIP, IAX, or some other means.
|
652
|
+
# Examples:
|
653
|
+
#
|
654
|
+
# Make a call to the PSTN using my SIP provider for VoIP termination:
|
655
|
+
# dial("SIP/19095551001@my.sip.voip.terminator.us")
|
656
|
+
#
|
657
|
+
# Make 3 Simulataneous calls to the SIP extensions separated by & symbols, try for 15 seconds and use the callerid
|
658
|
+
# for this call specified by the variable my_callerid
|
659
|
+
# dial "SIP/jay-desk-650&SIP/jay-desk-601&SIP/jay-desk-601-2", :for => 15.seconds, :caller_id => my_callerid
|
660
|
+
#
|
661
|
+
# Make a call using the IAX provider to the PSTN
|
662
|
+
# dial("IAX2/my.id@voipjet/19095551234", :name=>"John Doe", :caller_id=>"9095551234")
|
663
|
+
#
|
664
|
+
# Options Parameter:
|
665
|
+
# :caller_id - the caller id number to be used when the call is placed. It is advised you properly adhere to the
|
666
|
+
# policy of VoIP termination providers with respect to caller id values.
|
667
|
+
#
|
668
|
+
# :name - this is the name which should be passed with the caller ID information
|
669
|
+
# if :name=>"John Doe" and :caller_id => "444-333-1000" then the compelete CID and name would be "John Doe" <4443331000>
|
670
|
+
# support for caller id information varies from country to country and from one VoIP termination provider to another.
|
671
|
+
#
|
672
|
+
# :for - this option can be thought of best as a timeout. i.e. timeout after :for if no one answers the call
|
673
|
+
# For example, dial("SIP/jay-desk-650&SIP/jay-desk-601&SIP/jay-desk-601-2", :for => 15.seconds, :caller_id => callerid)
|
674
|
+
# 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
|
675
|
+
#
|
676
|
+
# :options - This is a string of options like "Tr" which are supported by the asterisk DIAL application.
|
677
|
+
# for a complete list of these options and their usage please visit: http://www.voip-info.org/wiki-Asterisk+cmd+Dial
|
678
|
+
#
|
679
|
+
# :confirm - ?
|
680
|
+
#
|
583
681
|
def dial(number, options={})
|
584
682
|
*recognized_options = :caller_id, :name, :for, :options, :confirm
|
585
683
|
|
@@ -645,13 +743,13 @@ module Adhearsion
|
|
645
743
|
end
|
646
744
|
nil
|
647
745
|
end
|
648
|
-
|
746
|
+
|
649
747
|
def set_caller_id_number(caller_id)
|
650
748
|
return unless caller_id
|
651
749
|
raise ArgumentError, "Caller ID must be numerical" if caller_id.to_s !~ /^\d+$/
|
652
750
|
raw_response %(SET CALLERID %p) % caller_id
|
653
751
|
end
|
654
|
-
|
752
|
+
|
655
753
|
def set_caller_id_name(caller_id_name)
|
656
754
|
return unless caller_id_name
|
657
755
|
variable "CALLERID(name)" => caller_id_name
|