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