adhearsion 2.6.4 → 3.0.0.beta1
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.
- checksums.yaml +4 -4
- data/.travis.yml +6 -9
- data/CHANGELOG.md +22 -7
- data/Gemfile +2 -0
- data/README.markdown +4 -5
- data/Rakefile +1 -2
- data/adhearsion.gemspec +19 -8
- data/features/cli_create.feature +19 -3
- data/features/step_definitions/cli_steps.rb +0 -11
- data/features/support/env.rb +3 -4
- data/lib/adhearsion.rb +48 -27
- data/lib/adhearsion/call.rb +34 -50
- data/lib/adhearsion/call_controller.rb +6 -12
- data/lib/adhearsion/call_controller/dial.rb +15 -53
- data/lib/adhearsion/call_controller/input.rb +39 -162
- data/lib/adhearsion/call_controller/input/ask_grammar_builder.rb +44 -0
- data/lib/adhearsion/call_controller/input/menu_builder.rb +136 -0
- data/lib/adhearsion/call_controller/input/prompt_builder.rb +78 -0
- data/lib/adhearsion/call_controller/input/result.rb +46 -0
- data/lib/adhearsion/call_controller/output.rb +48 -67
- data/lib/adhearsion/call_controller/output/abstract_player.rb +3 -3
- data/lib/adhearsion/call_controller/output/async_player.rb +3 -3
- data/lib/adhearsion/call_controller/output/player.rb +1 -1
- data/lib/adhearsion/call_controller/record.rb +23 -8
- data/lib/adhearsion/calls.rb +1 -1
- data/lib/adhearsion/cli_commands/ahn_command.rb +2 -65
- data/lib/adhearsion/cli_commands/thor_errors.rb +0 -6
- data/lib/adhearsion/configuration.rb +91 -39
- data/lib/adhearsion/core_ext/blather/stanza.rb +41 -0
- data/lib/adhearsion/core_ext/blather/stanza/presence.rb +13 -0
- data/lib/adhearsion/error.rb +5 -0
- data/lib/adhearsion/event.rb +21 -0
- data/lib/adhearsion/event/active_speaker.rb +11 -0
- data/lib/adhearsion/event/answered.rb +11 -0
- data/lib/adhearsion/event/asterisk.rb +10 -0
- data/lib/adhearsion/event/asterisk/ami.rb +34 -0
- data/lib/adhearsion/event/complete.rb +75 -0
- data/lib/adhearsion/event/dtmf.rb +11 -0
- data/lib/adhearsion/event/end.rb +22 -0
- data/lib/adhearsion/event/input_timers_started.rb +9 -0
- data/lib/adhearsion/event/joined.rb +17 -0
- data/lib/adhearsion/event/offer.rb +14 -0
- data/lib/adhearsion/event/ringing.rb +11 -0
- data/lib/adhearsion/event/started_speaking.rb +13 -0
- data/lib/adhearsion/event/stopped_speaking.rb +13 -0
- data/lib/adhearsion/event/unjoined.rb +17 -0
- data/lib/adhearsion/events.rb +47 -66
- data/lib/adhearsion/foundation.rb +0 -1
- data/lib/adhearsion/foundation/object.rb +0 -5
- data/lib/adhearsion/generators/app/app_generator.rb +4 -1
- data/lib/adhearsion/generators/app/templates/Gemfile.erb +2 -10
- data/lib/adhearsion/generators/app/templates/adhearsion.erb +9 -9
- data/lib/adhearsion/generators/app/templates/config.ru +7 -0
- data/lib/adhearsion/generators/app/templates/en.yml +4 -0
- data/lib/adhearsion/generators/app/templates/events.erb +2 -2
- data/lib/adhearsion/generators/app/templates/hello_world.wav +0 -0
- data/lib/adhearsion/generators/app/templates/simon_game.rb +2 -1
- data/lib/adhearsion/generators/app/templates/simon_game_spec.rb +2 -2
- data/lib/adhearsion/has_headers.rb +34 -0
- data/lib/adhearsion/http_server.rb +37 -0
- data/lib/adhearsion/initializer.rb +19 -153
- data/lib/adhearsion/logging.rb +6 -25
- data/lib/adhearsion/outbound_call.rb +5 -5
- data/lib/adhearsion/plugin.rb +1 -0
- data/lib/adhearsion/protocol_error.rb +26 -0
- data/lib/adhearsion/rayo.rb +30 -0
- data/lib/adhearsion/rayo/client.rb +62 -0
- data/lib/adhearsion/rayo/client/component_registry.rb +33 -0
- data/lib/adhearsion/rayo/command.rb +21 -0
- data/lib/adhearsion/rayo/command/accept.rb +16 -0
- data/lib/adhearsion/rayo/command/answer.rb +16 -0
- data/lib/adhearsion/rayo/command/dial.rb +57 -0
- data/lib/adhearsion/rayo/command/hangup.rb +16 -0
- data/lib/adhearsion/rayo/command/join.rb +43 -0
- data/lib/adhearsion/rayo/command/mute.rb +13 -0
- data/lib/adhearsion/rayo/command/redirect.rb +23 -0
- data/lib/adhearsion/rayo/command/reject.rb +40 -0
- data/lib/adhearsion/rayo/command/unjoin.rb +24 -0
- data/lib/adhearsion/rayo/command/unmute.rb +13 -0
- data/lib/adhearsion/rayo/command_node.rb +47 -0
- data/lib/adhearsion/rayo/component.rb +21 -0
- data/lib/adhearsion/rayo/component/asterisk.rb +13 -0
- data/lib/adhearsion/rayo/component/asterisk/agi.rb +14 -0
- data/lib/adhearsion/rayo/component/asterisk/agi/command.rb +46 -0
- data/lib/adhearsion/rayo/component/asterisk/ami.rb +14 -0
- data/lib/adhearsion/rayo/component/asterisk/ami/action.rb +61 -0
- data/lib/adhearsion/rayo/component/component_node.rb +90 -0
- data/lib/adhearsion/rayo/component/input.rb +186 -0
- data/lib/adhearsion/rayo/component/output.rb +471 -0
- data/lib/adhearsion/rayo/component/prompt.rb +53 -0
- data/lib/adhearsion/rayo/component/receive_fax.rb +26 -0
- data/lib/adhearsion/rayo/component/record.rb +165 -0
- data/lib/adhearsion/rayo/component/send_fax.rb +64 -0
- data/lib/adhearsion/rayo/component/stop.rb +11 -0
- data/lib/adhearsion/rayo/connection.rb +12 -0
- data/lib/adhearsion/rayo/connection/asterisk.rb +74 -0
- data/lib/adhearsion/rayo/connection/connected.rb +22 -0
- data/lib/adhearsion/rayo/connection/generic_connection.rb +22 -0
- data/lib/adhearsion/rayo/connection/xmpp.rb +198 -0
- data/lib/adhearsion/rayo/disconnected_error.rb +22 -0
- data/lib/adhearsion/{punchblock_plugin → rayo}/initializer.rb +19 -19
- data/lib/adhearsion/rayo/rayo_node.rb +127 -0
- data/lib/adhearsion/rayo/ref.rb +57 -0
- data/lib/adhearsion/statistics.rb +1 -1
- data/lib/adhearsion/tasks.rb +1 -2
- data/lib/adhearsion/tasks/configuration.rb +1 -1
- data/lib/adhearsion/tasks/environment.rb +0 -2
- data/lib/adhearsion/tasks/i18n.rb +49 -0
- data/lib/adhearsion/translator.rb +11 -0
- data/lib/adhearsion/translator/asterisk.rb +234 -0
- data/lib/adhearsion/translator/asterisk/agi_app.rb +17 -0
- data/lib/adhearsion/translator/asterisk/agi_command.rb +45 -0
- data/lib/adhearsion/translator/asterisk/ami_error_converter.rb +20 -0
- data/lib/adhearsion/translator/asterisk/call.rb +416 -0
- data/lib/adhearsion/translator/asterisk/channel.rb +43 -0
- data/lib/adhearsion/translator/asterisk/component.rb +88 -0
- data/lib/adhearsion/translator/asterisk/component/asterisk.rb +15 -0
- data/lib/adhearsion/translator/asterisk/component/asterisk/agi_command.rb +42 -0
- data/lib/adhearsion/translator/asterisk/component/asterisk/ami_action.rb +68 -0
- data/lib/adhearsion/translator/asterisk/component/composed_prompt.rb +76 -0
- data/lib/adhearsion/translator/asterisk/component/dtmf_recognizer.rb +137 -0
- data/lib/adhearsion/translator/asterisk/component/input.rb +34 -0
- data/lib/adhearsion/translator/asterisk/component/input_component.rb +90 -0
- data/lib/adhearsion/translator/asterisk/component/mrcp_native_prompt.rb +71 -0
- data/lib/adhearsion/translator/asterisk/component/mrcp_prompt.rb +55 -0
- data/lib/adhearsion/translator/asterisk/component/mrcp_recog_prompt.rb +165 -0
- data/lib/adhearsion/translator/asterisk/component/output.rb +233 -0
- data/lib/adhearsion/translator/asterisk/component/record.rb +101 -0
- data/lib/adhearsion/translator/asterisk/component/stop_by_redirect.rb +30 -0
- data/lib/adhearsion/translator/asterisk/unimrcp_app.rb +28 -0
- data/lib/adhearsion/uri_list.rb +21 -0
- data/lib/adhearsion/version.rb +1 -1
- data/spec/adhearsion/call_controller/dial_spec.rb +79 -1420
- data/spec/adhearsion/call_controller/input_spec.rb +1141 -237
- data/spec/adhearsion/call_controller/output/async_player_spec.rb +10 -10
- data/spec/adhearsion/call_controller/output/player_spec.rb +8 -8
- data/spec/adhearsion/call_controller/output_spec.rb +162 -215
- data/spec/adhearsion/call_controller/record_spec.rb +15 -16
- data/spec/adhearsion/call_controller_spec.rb +23 -40
- data/spec/adhearsion/call_spec.rb +123 -129
- data/spec/adhearsion/calls_spec.rb +3 -3
- data/spec/adhearsion/configuration_spec.rb +94 -108
- data/spec/adhearsion/event/answered_spec.rb +50 -0
- data/spec/adhearsion/event/asterisk/Find Results +402 -0
- data/spec/adhearsion/event/asterisk/ami_spec.rb +81 -0
- data/spec/adhearsion/event/complete_spec.rb +176 -0
- data/spec/adhearsion/event/dtmf_spec.rb +35 -0
- data/spec/adhearsion/event/end_spec.rb +85 -0
- data/spec/adhearsion/event/input_timers_started_spec.rb +19 -0
- data/spec/adhearsion/event/joined_spec.rb +53 -0
- data/spec/adhearsion/event/offer_spec.rb +106 -0
- data/spec/adhearsion/event/ringing_spec.rb +50 -0
- data/spec/adhearsion/event/started_speaking_spec.rb +37 -0
- data/spec/adhearsion/event/stopped_speaking_spec.rb +37 -0
- data/spec/adhearsion/event/unjoined_spec.rb +48 -0
- data/spec/adhearsion/event/untitled +0 -0
- data/spec/adhearsion/events_spec.rb +19 -45
- data/spec/adhearsion/initializer_spec.rb +12 -184
- data/spec/adhearsion/logging_spec.rb +5 -20
- data/spec/adhearsion/outbound_call_spec.rb +13 -13
- data/spec/adhearsion/plugin_spec.rb +3 -4
- data/spec/adhearsion/protocol_error_spec.rb +91 -0
- data/spec/adhearsion/rayo/client/component_registry_spec.rb +26 -0
- data/spec/adhearsion/rayo/client_spec.rb +134 -0
- data/spec/adhearsion/rayo/command/accept_spec.rb +63 -0
- data/spec/adhearsion/rayo/command/answer_spec.rb +73 -0
- data/spec/adhearsion/rayo/command/dial_spec.rb +156 -0
- data/spec/adhearsion/rayo/command/hangup_spec.rb +63 -0
- data/spec/adhearsion/rayo/command/join_spec.rb +158 -0
- data/spec/adhearsion/rayo/command/mute_spec.rb +32 -0
- data/spec/adhearsion/rayo/command/redirect_spec.rb +89 -0
- data/spec/adhearsion/rayo/command/reject_spec.rb +117 -0
- data/spec/adhearsion/rayo/command/unjoin_spec.rb +82 -0
- data/spec/adhearsion/rayo/command/unmute_spec.rb +32 -0
- data/spec/adhearsion/rayo/command_node_spec.rb +101 -0
- data/spec/adhearsion/rayo/component/asterisk/agi/command_spec.rb +111 -0
- data/spec/adhearsion/rayo/component/asterisk/ami/action_spec.rb +173 -0
- data/spec/adhearsion/rayo/component/component_node_spec.rb +110 -0
- data/spec/adhearsion/rayo/component/input_spec.rb +715 -0
- data/spec/adhearsion/rayo/component/output_spec.rb +1030 -0
- data/spec/adhearsion/rayo/component/prompt_spec.rb +171 -0
- data/spec/adhearsion/rayo/component/receive_fax_spec.rb +136 -0
- data/spec/adhearsion/rayo/component/record_spec.rb +497 -0
- data/spec/adhearsion/rayo/component/send_fax_spec.rb +144 -0
- data/spec/adhearsion/rayo/connection/asterisk_spec.rb +118 -0
- data/spec/adhearsion/rayo/connection/xmpp_spec.rb +449 -0
- data/spec/adhearsion/rayo/initializer_spec.rb +353 -0
- data/spec/adhearsion/rayo/ref_spec.rb +168 -0
- data/spec/adhearsion/rayo_spec.rb +7 -0
- data/spec/adhearsion/router/route_spec.rb +1 -1
- data/spec/adhearsion/statistics_spec.rb +2 -5
- data/spec/adhearsion/translator/asterisk/call_spec.rb +2047 -0
- data/spec/adhearsion/translator/asterisk/component/asterisk/agi_command_spec.rb +256 -0
- data/spec/adhearsion/translator/asterisk/component/asterisk/ami_action_spec.rb +151 -0
- data/spec/adhearsion/translator/asterisk/component/composed_prompt_spec.rb +257 -0
- data/spec/adhearsion/translator/asterisk/component/input_spec.rb +571 -0
- data/spec/adhearsion/translator/asterisk/component/mrcp_native_prompt_spec.rb +774 -0
- data/spec/adhearsion/translator/asterisk/component/mrcp_prompt_spec.rb +1244 -0
- data/spec/adhearsion/translator/asterisk/component/output_spec.rb +1850 -0
- data/spec/adhearsion/translator/asterisk/component/record_spec.rb +426 -0
- data/spec/adhearsion/translator/asterisk/component/stop_by_redirect_spec.rb +62 -0
- data/spec/adhearsion/translator/asterisk/component_spec.rb +83 -0
- data/spec/adhearsion/translator/asterisk_spec.rb +685 -0
- data/spec/adhearsion/uri_list_spec.rb +88 -0
- data/spec/adhearsion_spec.rb +89 -14
- data/spec/fixtures/locale/en.yml +13 -0
- data/spec/fixtures/locale/it.yml +13 -0
- data/spec/spec_helper.rb +18 -5
- data/spec/support/call_controller_test_helpers.rb +3 -2
- data/spec/support/initializer_stubs.rb +3 -1
- data/spec/support/punchblock_examples.rb +65 -0
- data/spec/support/punchblock_mocks.rb +12 -0
- metadata +412 -70
- data/features/cli_daemon.feature +0 -20
- data/features/cli_restart.feature +0 -52
- data/features/cli_stop.feature +0 -50
- data/lib/adhearsion/call_controller/menu_dsl.rb +0 -21
- data/lib/adhearsion/call_controller/menu_dsl/array_match_calculator.rb +0 -26
- data/lib/adhearsion/call_controller/menu_dsl/calculated_match.rb +0 -43
- data/lib/adhearsion/call_controller/menu_dsl/calculated_match_collection.rb +0 -45
- data/lib/adhearsion/call_controller/menu_dsl/fixnum_match_calculator.rb +0 -11
- data/lib/adhearsion/call_controller/menu_dsl/match_calculator.rb +0 -40
- data/lib/adhearsion/call_controller/menu_dsl/menu.rb +0 -207
- data/lib/adhearsion/call_controller/menu_dsl/menu_builder.rb +0 -92
- data/lib/adhearsion/call_controller/menu_dsl/range_match_calculator.rb +0 -60
- data/lib/adhearsion/call_controller/menu_dsl/string_match_calculator.rb +0 -25
- data/lib/adhearsion/call_controller/utility.rb +0 -77
- data/lib/adhearsion/foundation/custom_daemonizer.rb +0 -52
- data/lib/adhearsion/punchblock_plugin.rb +0 -63
- data/scripts/cloc-1.64.pl +0 -10483
- data/spec/adhearsion/call_controller/menu_dsl/array_match_calculator_spec.rb +0 -76
- data/spec/adhearsion/call_controller/menu_dsl/calculated_match_collection_spec.rb +0 -60
- data/spec/adhearsion/call_controller/menu_dsl/calculated_match_spec.rb +0 -61
- data/spec/adhearsion/call_controller/menu_dsl/fixnum_match_calculator_spec.rb +0 -39
- data/spec/adhearsion/call_controller/menu_dsl/match_calculator_spec.rb +0 -17
- data/spec/adhearsion/call_controller/menu_dsl/menu_builder_spec.rb +0 -165
- data/spec/adhearsion/call_controller/menu_dsl/menu_spec.rb +0 -420
- data/spec/adhearsion/call_controller/menu_dsl/range_match_calculator_spec.rb +0 -32
- data/spec/adhearsion/call_controller/menu_dsl/string_match_calculator_spec.rb +0 -40
- data/spec/adhearsion/call_controller/utility_spec.rb +0 -90
- data/spec/adhearsion/punchblock_plugin/initializer_spec.rb +0 -356
- data/spec/adhearsion/punchblock_plugin_spec.rb +0 -59
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'ruby_jid'
|
|
4
|
+
|
|
5
|
+
module Adhearsion
|
|
6
|
+
module Rayo
|
|
7
|
+
##
|
|
8
|
+
# A rayo Ref message. This provides the command ID in response to execution of a command.
|
|
9
|
+
#
|
|
10
|
+
class Ref < RayoNode
|
|
11
|
+
register :ref, :core
|
|
12
|
+
|
|
13
|
+
# @return [String] the command URI
|
|
14
|
+
attribute :uri
|
|
15
|
+
def uri=(other)
|
|
16
|
+
super URI(other)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def scheme
|
|
20
|
+
uri.scheme
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call_id
|
|
24
|
+
case scheme
|
|
25
|
+
when 'xmpp'
|
|
26
|
+
RubyJID.new(uri.opaque).node
|
|
27
|
+
when nil
|
|
28
|
+
uri.path
|
|
29
|
+
else
|
|
30
|
+
uri.opaque
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def domain
|
|
35
|
+
case scheme
|
|
36
|
+
when 'xmpp'
|
|
37
|
+
RubyJID.new(uri.opaque).domain
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def component_id
|
|
42
|
+
case scheme
|
|
43
|
+
when 'xmpp'
|
|
44
|
+
RubyJID.new(uri.opaque).resource
|
|
45
|
+
else
|
|
46
|
+
call_id
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def rayo_attributes
|
|
51
|
+
{}.tap do |atts|
|
|
52
|
+
atts[:uri] = uri if uri
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/adhearsion/tasks.rb
CHANGED
|
@@ -12,13 +12,12 @@ initializer.load_lib_folder
|
|
|
12
12
|
initializer.load_config_file
|
|
13
13
|
initializer.load_events_file
|
|
14
14
|
initializer.load_routes_file
|
|
15
|
-
initializer.initialize_log_paths
|
|
16
15
|
initializer.start_logging
|
|
17
16
|
initializer.init_plugins
|
|
18
17
|
|
|
19
18
|
Adhearsion::Plugin.load_tasks
|
|
20
19
|
|
|
21
|
-
puts "\nAdhearsion configured environment: #{Adhearsion.
|
|
20
|
+
puts "\nAdhearsion configured environment: #{Adhearsion.environment}\n" unless ARGV.empty?
|
|
22
21
|
|
|
23
22
|
desc "Dump useful information about this application's Adhearsion environment"
|
|
24
23
|
task :about do
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
3
|
namespace :config do
|
|
4
|
-
desc "Show configuration values; accepts a parameter: [nil|
|
|
4
|
+
desc "Show configuration values; accepts a parameter: [nil|core|<plugin-name>|all]"
|
|
5
5
|
task :show, [:name] => [:environment] do |t, args|
|
|
6
6
|
name = args.name.nil? ? :all : args.name.to_sym
|
|
7
7
|
puts "\nAdhearsion.config do |config|\n\n"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
namespace :i18n do
|
|
4
|
+
desc "Validate configured audio prompt files exist"
|
|
5
|
+
task :validate_files => :environment do
|
|
6
|
+
Adhearsion::Initializer.new.setup_i18n_load_path
|
|
7
|
+
|
|
8
|
+
config = Adhearsion.config.core.i18n
|
|
9
|
+
locale_files = Dir.glob(I18n.load_path)
|
|
10
|
+
|
|
11
|
+
locale_errors = {}
|
|
12
|
+
checked_prompts = 0
|
|
13
|
+
locale_files.each do |locale_file|
|
|
14
|
+
# We only support YAML for now
|
|
15
|
+
next unless locale_file =~ /\.ya?ml$/
|
|
16
|
+
prompts = YAML.load File.read(locale_file)
|
|
17
|
+
|
|
18
|
+
locale = prompts.keys.first
|
|
19
|
+
prompts = prompts[locale]
|
|
20
|
+
|
|
21
|
+
prompts.each_pair do |key, mapping|
|
|
22
|
+
logger.trace "Checking i18n key #{key}"
|
|
23
|
+
# Not all prompts will have audio files
|
|
24
|
+
next unless mapping['audio']
|
|
25
|
+
|
|
26
|
+
file = File.absolute_path "#{config['audio_path']}/#{locale}/#{mapping['audio']}"
|
|
27
|
+
unless File.exist?(file)
|
|
28
|
+
logger.warn "[#{locale}] Missing audio file: #{file}"
|
|
29
|
+
locale_errors[locale] ||= 0
|
|
30
|
+
locale_errors[locale] += 1
|
|
31
|
+
end
|
|
32
|
+
checked_prompts += 1
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if checked_prompts == 0
|
|
37
|
+
logger.warn "No Adhearsion i18n prompts found. No files checked."
|
|
38
|
+
else
|
|
39
|
+
if locale_errors.keys.count > 0
|
|
40
|
+
logger.error "Errors detected! Number of errors by locale:"
|
|
41
|
+
locale_errors.each_pair do |locale, err_count|
|
|
42
|
+
logger.error "[#{locale}]: #{err_count} missing prompts"
|
|
43
|
+
end
|
|
44
|
+
else
|
|
45
|
+
logger.info "All configured prompt files successfully validated."
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'celluloid'
|
|
4
|
+
require 'ruby_ami'
|
|
5
|
+
|
|
6
|
+
require 'adhearsion/translator/asterisk/agi_command'
|
|
7
|
+
require 'adhearsion/translator/asterisk/call'
|
|
8
|
+
require 'adhearsion/translator/asterisk/channel'
|
|
9
|
+
require 'adhearsion/translator/asterisk/component'
|
|
10
|
+
|
|
11
|
+
module Adhearsion
|
|
12
|
+
module Translator
|
|
13
|
+
class Asterisk
|
|
14
|
+
include Celluloid
|
|
15
|
+
|
|
16
|
+
# Indicates that a command was executed against a channel which no longer exists
|
|
17
|
+
ChannelGoneError = Class.new Error
|
|
18
|
+
|
|
19
|
+
attr_reader :ami_client, :connection, :calls, :bridges
|
|
20
|
+
|
|
21
|
+
REDIRECT_CONTEXT = 'adhearsion-redirect'
|
|
22
|
+
REDIRECT_EXTENSION = '1'
|
|
23
|
+
REDIRECT_PRIORITY = '1'
|
|
24
|
+
|
|
25
|
+
EVENTS_ALLOWED_BRIDGED = %w{AGIExec AsyncAGI}
|
|
26
|
+
|
|
27
|
+
trap_exit :actor_died
|
|
28
|
+
|
|
29
|
+
# Set the AMI event filter to be applied to incoming AMI events. A truthy return value will send the event via Rayo to the client (Adhearsion).
|
|
30
|
+
#
|
|
31
|
+
# @param [#[<RubyAMI::Event>]] filter
|
|
32
|
+
#
|
|
33
|
+
# @example A lambda
|
|
34
|
+
# Translator::Asterisk.event_filter = ->(event) { event.name == 'AsyncAGI' }
|
|
35
|
+
#
|
|
36
|
+
def self.event_filter=(filter)
|
|
37
|
+
@event_filter = filter
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.event_passes_filter?(event)
|
|
41
|
+
@event_filter ? !!@event_filter[event] : true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
event_filter = nil
|
|
45
|
+
|
|
46
|
+
def initialize(ami_client, connection)
|
|
47
|
+
@ami_client, @connection = ami_client, connection
|
|
48
|
+
@calls, @components, @channel_to_call_id, @bridges = {}, {}, {}, {}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def register_call(call)
|
|
52
|
+
@channel_to_call_id[call.channel] = call.id
|
|
53
|
+
@calls[call.id] ||= call
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def deregister_call(id, channel)
|
|
57
|
+
@channel_to_call_id.delete channel
|
|
58
|
+
@calls.delete id
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def call_with_id(call_id)
|
|
62
|
+
@calls[call_id]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def call_for_channel(channel)
|
|
66
|
+
call_with_id @channel_to_call_id[Channel.new(channel).name]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def register_component(component)
|
|
70
|
+
@components[component.id] ||= component
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def component_with_id(component_id)
|
|
74
|
+
@components[component_id]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def handle_ami_event(event)
|
|
78
|
+
return unless event.is_a? RubyAMI::Event
|
|
79
|
+
|
|
80
|
+
if event.name == 'FullyBooted'
|
|
81
|
+
handle_pb_event Adhearsion::Rayo::Connection::Connected.new
|
|
82
|
+
run_at_fully_booted
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
handle_varset_ami_event event
|
|
87
|
+
|
|
88
|
+
ami_dispatch_to_or_create_call event
|
|
89
|
+
if !ami_event_known_call?(event) && self.class.event_passes_filter?(event)
|
|
90
|
+
handle_pb_event Adhearsion::Event::Asterisk::AMI.new(name: event.name, headers: event.headers)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def handle_pb_event(event)
|
|
95
|
+
connection.handle_event event
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def send_message(call_id, domain, body, options = {})
|
|
99
|
+
call = call_with_id call_id
|
|
100
|
+
call.send_message body if call
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def execute_command(command, options = {})
|
|
104
|
+
command.request!
|
|
105
|
+
|
|
106
|
+
command.target_call_id ||= options[:call_id]
|
|
107
|
+
command.component_id ||= options[:component_id]
|
|
108
|
+
|
|
109
|
+
if command.target_call_id
|
|
110
|
+
execute_call_command command
|
|
111
|
+
elsif command.component_id
|
|
112
|
+
execute_component_command command
|
|
113
|
+
else
|
|
114
|
+
execute_global_command command
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def execute_call_command(command)
|
|
119
|
+
if call = call_with_id(command.target_call_id)
|
|
120
|
+
begin
|
|
121
|
+
call.execute_command command
|
|
122
|
+
rescue => e
|
|
123
|
+
deregister_call call.id, call.channel
|
|
124
|
+
end
|
|
125
|
+
else
|
|
126
|
+
command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a call with ID #{command.target_call_id}", command.target_call_id
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def execute_component_command(command)
|
|
131
|
+
if (component = component_with_id(command.component_id))
|
|
132
|
+
component.execute_command command
|
|
133
|
+
else
|
|
134
|
+
command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a component with ID #{command.component_id}", command.target_call_id, command.component_id
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def execute_global_command(command)
|
|
139
|
+
case command
|
|
140
|
+
when Adhearsion::Rayo::Component::Asterisk::AMI::Action
|
|
141
|
+
component = Component::Asterisk::AMIAction.new command, current_actor, ami_client
|
|
142
|
+
register_component component
|
|
143
|
+
component.execute
|
|
144
|
+
when Adhearsion::Rayo::Command::Dial
|
|
145
|
+
if call = call_with_id(command.uri)
|
|
146
|
+
command.response = Adhearsion::ProtocolError.new.setup(:conflict, 'Call ID already in use')
|
|
147
|
+
else
|
|
148
|
+
call = Call.new command.to, current_actor, ami_client, connection, nil, command.uri
|
|
149
|
+
register_call call
|
|
150
|
+
call.dial command
|
|
151
|
+
end
|
|
152
|
+
else
|
|
153
|
+
command.response = Adhearsion::ProtocolError.new.setup 'command-not-acceptable', "Did not understand command"
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def run_at_fully_booted
|
|
158
|
+
send_ami_action 'Command', 'Command' => "dialplan add extension #{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY},AGI,agi:async into #{REDIRECT_CONTEXT}"
|
|
159
|
+
|
|
160
|
+
result = send_ami_action 'Command', 'Command' => "dialplan show #{REDIRECT_CONTEXT}"
|
|
161
|
+
if result.text_body =~ /failed/
|
|
162
|
+
logger.error "Adhearsion failed to add the #{REDIRECT_EXTENSION} extension to the #{REDIRECT_CONTEXT} context. Please add a [#{REDIRECT_CONTEXT}] entry to your dialplan."
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
check_recording_directory
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def check_recording_directory
|
|
169
|
+
logger.warn "Recordings directory #{Component::Record::RECORDING_BASE_PATH} does not exist. Recording might not work. This warning can be ignored if Adhearsion is running on a separate machine than Asterisk. See http://adhearsion.com/docs/call-controllers#recording" unless File.exists?(Component::Record::RECORDING_BASE_PATH)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def actor_died(actor, reason)
|
|
173
|
+
return unless reason
|
|
174
|
+
if id = @calls.key(actor)
|
|
175
|
+
@calls.delete id
|
|
176
|
+
end_event = Adhearsion::Event::End.new :target_call_id => id,
|
|
177
|
+
:reason => :error
|
|
178
|
+
handle_pb_event end_event
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
private
|
|
183
|
+
|
|
184
|
+
def send_ami_action(name, headers = {})
|
|
185
|
+
ami_client.send_action name, headers
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def handle_varset_ami_event(event)
|
|
189
|
+
return unless event.name == 'VarSet' && event['Variable'] == 'adhearsion_call_id' && (call = call_with_id event['Value'])
|
|
190
|
+
|
|
191
|
+
@channel_to_call_id.delete call.channel
|
|
192
|
+
call.channel = event['Channel']
|
|
193
|
+
register_call call
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def ami_dispatch_to_or_create_call(event)
|
|
197
|
+
calls_for_event = channels_for_ami_event(event).inject({}) do |h, channel|
|
|
198
|
+
call = call_for_channel channel
|
|
199
|
+
h[channel] = call if call
|
|
200
|
+
h
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
if !calls_for_event.empty?
|
|
204
|
+
calls_for_event.each_pair do |channel, call|
|
|
205
|
+
next if channel.bridged? && !EVENTS_ALLOWED_BRIDGED.include?(event.name)
|
|
206
|
+
call.process_ami_event event
|
|
207
|
+
end
|
|
208
|
+
elsif event.name == "AsyncAGIStart" || (event.name == "AsyncAGI" && event['SubEvent'] == "Start")
|
|
209
|
+
handle_async_agi_start_event event
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def channels_for_ami_event(event)
|
|
214
|
+
[event['Channel'], event['Channel1'], event['Channel2']].compact.map { |channel| Channel.new(channel) }
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def ami_event_known_call?(event)
|
|
218
|
+
(event['Channel'] && call_for_channel(event['Channel'])) ||
|
|
219
|
+
(event['Channel1'] && call_for_channel(event['Channel1'])) ||
|
|
220
|
+
(event['Channel2'] && call_for_channel(event['Channel2']))
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def handle_async_agi_start_event(event)
|
|
224
|
+
env = RubyAMI::AsyncAGIEnvironmentParser.new(event['Env']).to_hash
|
|
225
|
+
|
|
226
|
+
return if env[:agi_extension] == 'h' || env[:agi_type] == 'Kill'
|
|
227
|
+
|
|
228
|
+
call = Call.new event['Channel'], current_actor, ami_client, connection, env
|
|
229
|
+
register_call call
|
|
230
|
+
call.send_offer
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Adhearsion
|
|
4
|
+
module Translator
|
|
5
|
+
class Asterisk
|
|
6
|
+
class AGIApp
|
|
7
|
+
def initialize(app, *args)
|
|
8
|
+
@app, @args = app, args
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def execute(call)
|
|
12
|
+
call.execute_agi_command "EXEC #{@app}", @args.join(',')
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'active_support/core_ext/string/filters'
|
|
4
|
+
require 'adhearsion/translator/asterisk/ami_error_converter'
|
|
5
|
+
|
|
6
|
+
module Adhearsion
|
|
7
|
+
module Translator
|
|
8
|
+
class Asterisk
|
|
9
|
+
class AGICommand
|
|
10
|
+
ARG_QUOTER = /["\\]/.freeze
|
|
11
|
+
|
|
12
|
+
attr_reader :id
|
|
13
|
+
|
|
14
|
+
def initialize(id, channel, command, *params)
|
|
15
|
+
@id, @channel, @command, @params = id, channel, command, params
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @raises RubyAMI::Error, ChannelGoneError
|
|
19
|
+
def execute(ami_client)
|
|
20
|
+
AMIErrorConverter.convert { ami_client.send_action 'AGI', 'Channel' => @channel, 'Command' => agi_command, 'CommandID' => id }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def parse_result(event)
|
|
24
|
+
parser = RubyAMI::AGIResultParser.new event['Result']
|
|
25
|
+
{code: parser.code, result: parser.result, data: parser.data}
|
|
26
|
+
rescue ArgumentError => e
|
|
27
|
+
logger.warn "Illegal message received from Asterisk: #{e.message}"
|
|
28
|
+
{code: -1, result: nil, data: nil}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def agi_command
|
|
34
|
+
"#{@command} #{@params.map { |arg| quote_arg(arg) }.join(' ')}".squish
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Arguments surrounded by quotes; quotes backslash-escaped.
|
|
38
|
+
# See parse_args in asterisk/res/res_agi.c (Asterisk 1.4.21.1)
|
|
39
|
+
def quote_arg(arg)
|
|
40
|
+
'"' + arg.to_s.gsub(ARG_QUOTER) { |m| "\\#{m}" } + '"'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|