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,20 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Adhearsion
|
|
4
|
+
module Translator
|
|
5
|
+
class Asterisk
|
|
6
|
+
module AMIErrorConverter
|
|
7
|
+
def self.convert(result = ->(e) { raise ChannelGoneError, e.message } )
|
|
8
|
+
yield
|
|
9
|
+
rescue RubyAMI::Error => e
|
|
10
|
+
case e.message
|
|
11
|
+
when 'No such channel', /Channel (\S+) does not exist./
|
|
12
|
+
result.call e if result
|
|
13
|
+
else
|
|
14
|
+
raise e
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'has_guarded_handlers'
|
|
4
|
+
require 'adhearsion/translator/asterisk/ami_error_converter'
|
|
5
|
+
|
|
6
|
+
module Adhearsion
|
|
7
|
+
module Translator
|
|
8
|
+
class Asterisk
|
|
9
|
+
class Call
|
|
10
|
+
include HasGuardedHandlers
|
|
11
|
+
|
|
12
|
+
InvalidCommandError = Class.new Error
|
|
13
|
+
|
|
14
|
+
OUTBOUND_CHANNEL_MATCH = /.* <(?<channel>.*)>/.freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :id, :channel, :translator, :agi_env, :direction, :pending_joins
|
|
17
|
+
|
|
18
|
+
HANGUP_CAUSE_TO_END_REASON = Hash.new { :error }
|
|
19
|
+
HANGUP_CAUSE_TO_END_REASON[0] = :hungup
|
|
20
|
+
HANGUP_CAUSE_TO_END_REASON[16] = :hungup
|
|
21
|
+
HANGUP_CAUSE_TO_END_REASON[17] = :busy
|
|
22
|
+
HANGUP_CAUSE_TO_END_REASON[18] = :timeout
|
|
23
|
+
HANGUP_CAUSE_TO_END_REASON[19] = :reject
|
|
24
|
+
HANGUP_CAUSE_TO_END_REASON[21] = :reject
|
|
25
|
+
HANGUP_CAUSE_TO_END_REASON[22] = :reject
|
|
26
|
+
HANGUP_CAUSE_TO_END_REASON[102] = :timeout
|
|
27
|
+
|
|
28
|
+
def initialize(channel, translator, ami_client, connection, agi_env = nil, id = nil)
|
|
29
|
+
@channel, @translator, @ami_client, @connection = channel, translator, ami_client, connection
|
|
30
|
+
@agi_env = agi_env || {}
|
|
31
|
+
@id = id || Adhearsion.new_uuid
|
|
32
|
+
@components = {}
|
|
33
|
+
@answered = false
|
|
34
|
+
@pending_joins = {}
|
|
35
|
+
@progress_sent = false
|
|
36
|
+
@block_commands = false
|
|
37
|
+
@channel_variables = {}
|
|
38
|
+
@hangup_cause = nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def register_component(component)
|
|
42
|
+
@components[component.id] ||= component
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def deregister_component(id)
|
|
46
|
+
@components.delete id
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def component_with_id(component_id)
|
|
50
|
+
@components[component_id]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def send_offer
|
|
54
|
+
@direction = :inbound
|
|
55
|
+
send_pb_event offer_event
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def channel_var(variable)
|
|
59
|
+
@channel_variables[variable] || fetch_channel_var(variable)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_s
|
|
63
|
+
"#<#{self.class}:#{id} Channel: #{channel.inspect}>"
|
|
64
|
+
end
|
|
65
|
+
alias :inspect :to_s
|
|
66
|
+
|
|
67
|
+
def dial(dial_command)
|
|
68
|
+
@direction = :outbound
|
|
69
|
+
channel = dial_command.to || ''
|
|
70
|
+
channel.match(OUTBOUND_CHANNEL_MATCH) { |m| channel = m[:channel] }
|
|
71
|
+
params = { :async => true,
|
|
72
|
+
:context => REDIRECT_CONTEXT,
|
|
73
|
+
:exten => REDIRECT_EXTENSION,
|
|
74
|
+
:priority => REDIRECT_PRIORITY,
|
|
75
|
+
:channel => channel,
|
|
76
|
+
:callerid => dial_command.from
|
|
77
|
+
}
|
|
78
|
+
params[:variable] = variable_for_headers dial_command.headers
|
|
79
|
+
params[:timeout] = dial_command.timeout unless dial_command.timeout.nil?
|
|
80
|
+
|
|
81
|
+
originate_action = Adhearsion::Rayo::Component::Asterisk::AMI::Action.new :name => 'Originate',
|
|
82
|
+
:params => params
|
|
83
|
+
originate_action.request!
|
|
84
|
+
translator.async.execute_global_command originate_action
|
|
85
|
+
dial_command.response = Adhearsion::Rayo::Ref.new uri: id
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def outbound?
|
|
89
|
+
direction == :outbound
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def inbound?
|
|
93
|
+
direction == :inbound
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def answered?
|
|
97
|
+
@answered
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def send_progress
|
|
101
|
+
return if answered? || outbound? || @progress_sent
|
|
102
|
+
@progress_sent = true
|
|
103
|
+
execute_agi_command "EXEC Progress"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def channel=(other)
|
|
107
|
+
@channel = other
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def process_ami_event(ami_event)
|
|
111
|
+
if Asterisk.event_passes_filter?(ami_event)
|
|
112
|
+
send_pb_event Adhearsion::Event::Asterisk::AMI.new(name: ami_event.name, headers: ami_event.headers)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
case ami_event.name
|
|
116
|
+
when 'Hangup'
|
|
117
|
+
handle_hangup_event ami_event['Cause'].to_i, ami_event.best_time
|
|
118
|
+
when 'AsyncAGI'
|
|
119
|
+
component_for_command_id_handle ami_event
|
|
120
|
+
|
|
121
|
+
if @answered == false && ami_event['SubEvent'] == 'Start'
|
|
122
|
+
@answered = true
|
|
123
|
+
send_pb_event Adhearsion::Event::Answered.new(timestamp: ami_event.best_time)
|
|
124
|
+
end
|
|
125
|
+
when 'AsyncAGIStart'
|
|
126
|
+
component_for_command_id_handle ami_event
|
|
127
|
+
|
|
128
|
+
if @answered == false
|
|
129
|
+
@answered = true
|
|
130
|
+
send_pb_event Adhearsion::Event::Answered.new(timestamp: ami_event.best_time)
|
|
131
|
+
end
|
|
132
|
+
when 'AsyncAGIExec', 'AsyncAGIEnd'
|
|
133
|
+
component_for_command_id_handle ami_event
|
|
134
|
+
when 'Newstate'
|
|
135
|
+
case ami_event['ChannelState']
|
|
136
|
+
when '5'
|
|
137
|
+
send_pb_event Adhearsion::Event::Ringing.new(timestamp: ami_event.best_time)
|
|
138
|
+
end
|
|
139
|
+
when 'BridgeEnter'
|
|
140
|
+
if other_call_channel = translator.bridges.delete(ami_event['BridgeUniqueid'])
|
|
141
|
+
if other_call = translator.call_for_channel(other_call_channel)
|
|
142
|
+
join_command = other_call.pending_joins.delete channel
|
|
143
|
+
join_command.response = true if join_command
|
|
144
|
+
|
|
145
|
+
event = Adhearsion::Event::Joined.new call_uri: other_call.id, timestamp: ami_event.best_time
|
|
146
|
+
send_pb_event event
|
|
147
|
+
|
|
148
|
+
other_call_event = Adhearsion::Event::Joined.new call_uri: id, timestamp: ami_event.best_time
|
|
149
|
+
other_call_event.target_call_id = other_call.id
|
|
150
|
+
translator.handle_pb_event other_call_event
|
|
151
|
+
end
|
|
152
|
+
else
|
|
153
|
+
translator.bridges[ami_event['BridgeUniqueid']] = ami_event['Channel']
|
|
154
|
+
end
|
|
155
|
+
when 'BridgeLeave'
|
|
156
|
+
if other_call_channel = translator.bridges.delete(ami_event['BridgeUniqueid'] + '_leave')
|
|
157
|
+
if other_call = translator.call_for_channel(other_call_channel)
|
|
158
|
+
event = Adhearsion::Event::Unjoined.new call_uri: other_call.id, timestamp: ami_event.best_time
|
|
159
|
+
send_pb_event event
|
|
160
|
+
|
|
161
|
+
other_call_event = Adhearsion::Event::Unjoined.new call_uri: id, timestamp: ami_event.best_time
|
|
162
|
+
other_call_event.target_call_id = other_call.id
|
|
163
|
+
translator.handle_pb_event other_call_event
|
|
164
|
+
end
|
|
165
|
+
else
|
|
166
|
+
translator.bridges[ami_event['BridgeUniqueid'] + '_leave'] = ami_event['Channel']
|
|
167
|
+
end
|
|
168
|
+
when 'OriginateResponse'
|
|
169
|
+
if ami_event['Response'] == 'Failure' && ami_event['Uniqueid'] == '<null>'
|
|
170
|
+
send_end_event :error, nil, ami_event.best_time
|
|
171
|
+
end
|
|
172
|
+
when 'BridgeExec'
|
|
173
|
+
join_command = @pending_joins.delete ami_event['Channel1']
|
|
174
|
+
join_command ||= @pending_joins.delete ami_event['Channel2']
|
|
175
|
+
join_command.response = true if join_command
|
|
176
|
+
when 'Bridge'
|
|
177
|
+
other_call_channel = ([ami_event['Channel1'], ami_event['Channel2']] - [channel]).first
|
|
178
|
+
if other_call = translator.call_for_channel(other_call_channel)
|
|
179
|
+
event = case ami_event['Bridgestate']
|
|
180
|
+
when 'Link'
|
|
181
|
+
Adhearsion::Event::Joined.new call_uri: other_call.id, timestamp: ami_event.best_time
|
|
182
|
+
when 'Unlink'
|
|
183
|
+
Adhearsion::Event::Unjoined.new call_uri: other_call.id, timestamp: ami_event.best_time
|
|
184
|
+
end
|
|
185
|
+
send_pb_event event
|
|
186
|
+
end
|
|
187
|
+
when 'Unlink'
|
|
188
|
+
other_call_channel = ([ami_event['Channel1'], ami_event['Channel2']] - [channel]).first
|
|
189
|
+
if other_call = translator.call_for_channel(other_call_channel)
|
|
190
|
+
send_pb_event Adhearsion::Event::Unjoined.new(call_uri: other_call.id, timestamp: ami_event.best_time)
|
|
191
|
+
end
|
|
192
|
+
when 'VarSet'
|
|
193
|
+
@channel_variables[ami_event['Variable']] = ami_event['Value']
|
|
194
|
+
end
|
|
195
|
+
trigger_handler :ami, ami_event
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def send_message(body)
|
|
199
|
+
execute_agi_command 'EXEC SendText', body
|
|
200
|
+
rescue
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def execute_command(command)
|
|
204
|
+
if @block_commands
|
|
205
|
+
command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a call with ID #{id}", id
|
|
206
|
+
return
|
|
207
|
+
end
|
|
208
|
+
if command.component_id
|
|
209
|
+
if component = component_with_id(command.component_id)
|
|
210
|
+
component.execute_command command
|
|
211
|
+
else
|
|
212
|
+
command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a component with ID #{command.component_id} for call #{id}", id, command.component_id
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
case command
|
|
216
|
+
when Adhearsion::Rayo::Command::Accept
|
|
217
|
+
if outbound?
|
|
218
|
+
command.response = true
|
|
219
|
+
else
|
|
220
|
+
execute_agi_command 'EXEC RINGING'
|
|
221
|
+
command.response = true
|
|
222
|
+
end
|
|
223
|
+
when Adhearsion::Rayo::Command::Answer
|
|
224
|
+
execute_agi_command 'ANSWER'
|
|
225
|
+
@answered = true
|
|
226
|
+
command.response = true
|
|
227
|
+
when Adhearsion::Rayo::Command::Hangup
|
|
228
|
+
send_hangup_command
|
|
229
|
+
@hangup_cause = :hangup_command
|
|
230
|
+
command.response = true
|
|
231
|
+
when Adhearsion::Rayo::Command::Join
|
|
232
|
+
other_call = translator.call_with_id command.call_uri
|
|
233
|
+
if other_call
|
|
234
|
+
@pending_joins[other_call.channel] = command
|
|
235
|
+
execute_agi_command 'EXEC Bridge', "#{other_call.channel},F(#{REDIRECT_CONTEXT},#{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY})"
|
|
236
|
+
else
|
|
237
|
+
command.response = Adhearsion::ProtocolError.new.setup :service_unavailable, "Could not find join party with address #{command.call_uri}", id
|
|
238
|
+
end
|
|
239
|
+
when Adhearsion::Rayo::Command::Unjoin
|
|
240
|
+
other_call = translator.call_with_id command.call_uri
|
|
241
|
+
redirect_back other_call
|
|
242
|
+
command.response = true
|
|
243
|
+
when Adhearsion::Rayo::Command::Reject
|
|
244
|
+
case command.reason
|
|
245
|
+
when :busy
|
|
246
|
+
execute_agi_command 'EXEC Busy'
|
|
247
|
+
when :decline
|
|
248
|
+
send_hangup_command 21
|
|
249
|
+
when :error
|
|
250
|
+
execute_agi_command 'EXEC Congestion'
|
|
251
|
+
else
|
|
252
|
+
execute_agi_command 'EXEC Congestion'
|
|
253
|
+
end
|
|
254
|
+
command.response = true
|
|
255
|
+
when Adhearsion::Rayo::Command::Redirect
|
|
256
|
+
execute_agi_command 'EXEC Transfer', command.to
|
|
257
|
+
status = channel_var 'TRANSFERSTATUS'
|
|
258
|
+
command.response = case status
|
|
259
|
+
when 'SUCCESS'
|
|
260
|
+
true
|
|
261
|
+
else
|
|
262
|
+
Adhearsion::ProtocolError.new.setup 'error', "TRANSFERSTATUS was #{status}", id
|
|
263
|
+
end
|
|
264
|
+
when Adhearsion::Rayo::Component::Asterisk::AGI::Command
|
|
265
|
+
execute_component Component::Asterisk::AGICommand, command
|
|
266
|
+
when Adhearsion::Rayo::Component::Output
|
|
267
|
+
execute_component Component::Output, command
|
|
268
|
+
when Adhearsion::Rayo::Component::Input
|
|
269
|
+
execute_component Component::Input, command
|
|
270
|
+
when Adhearsion::Rayo::Component::Prompt
|
|
271
|
+
component_class = case command.input.recognizer
|
|
272
|
+
when 'unimrcp'
|
|
273
|
+
case command.output.renderer
|
|
274
|
+
when 'unimrcp'
|
|
275
|
+
Component::MRCPPrompt
|
|
276
|
+
when 'asterisk'
|
|
277
|
+
Component::MRCPNativePrompt
|
|
278
|
+
else
|
|
279
|
+
raise InvalidCommandError, 'Invalid recognizer/renderer combination'
|
|
280
|
+
end
|
|
281
|
+
else
|
|
282
|
+
Component::ComposedPrompt
|
|
283
|
+
end
|
|
284
|
+
execute_component component_class, command
|
|
285
|
+
when Adhearsion::Rayo::Component::Record
|
|
286
|
+
execute_component Component::Record, command
|
|
287
|
+
else
|
|
288
|
+
command.response = Adhearsion::ProtocolError.new.setup 'command-not-acceptable', "Did not understand command for call #{id}", id
|
|
289
|
+
end
|
|
290
|
+
rescue InvalidCommandError => e
|
|
291
|
+
command.response = Adhearsion::ProtocolError.new.setup :invalid_command, e.message, id
|
|
292
|
+
rescue ChannelGoneError
|
|
293
|
+
command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a call with ID #{id}", id
|
|
294
|
+
rescue RubyAMI::Error => e
|
|
295
|
+
command.response = Adhearsion::ProtocolError.new.setup 'error', e.message, id
|
|
296
|
+
rescue Celluloid::DeadActorError
|
|
297
|
+
command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a component with ID #{command.component_id} for call #{id}", id, command.component_id
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
#
|
|
301
|
+
# @return [Hash] AGI result
|
|
302
|
+
#
|
|
303
|
+
# @raises RubyAMI::Error, ChannelGoneError
|
|
304
|
+
def execute_agi_command(command, *params)
|
|
305
|
+
agi = AGICommand.new Adhearsion.new_uuid, channel, command, *params
|
|
306
|
+
response = Celluloid::Future.new
|
|
307
|
+
register_tmp_handler :ami, [{name: 'AsyncAGI', [:[], 'SubEvent'] => 'Exec'}, {name: 'AsyncAGIExec'}], [{[:[], 'CommandID'] => agi.id}, {[:[], 'CommandId'] => agi.id}] do |event|
|
|
308
|
+
response.signal Celluloid::SuccessResponse.new(nil, event)
|
|
309
|
+
end
|
|
310
|
+
agi.execute @ami_client
|
|
311
|
+
event = response.value
|
|
312
|
+
return unless event
|
|
313
|
+
agi.parse_result event
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def logger_id
|
|
317
|
+
"#{self.class}: #{id}"
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def redirect_back(other_call = nil)
|
|
321
|
+
redirect_options = {
|
|
322
|
+
'Channel' => channel,
|
|
323
|
+
'Exten' => Asterisk::REDIRECT_EXTENSION,
|
|
324
|
+
'Priority' => Asterisk::REDIRECT_PRIORITY,
|
|
325
|
+
'Context' => Asterisk::REDIRECT_CONTEXT
|
|
326
|
+
}
|
|
327
|
+
redirect_options.merge!({
|
|
328
|
+
'ExtraChannel' => other_call.channel,
|
|
329
|
+
'ExtraExten' => Asterisk::REDIRECT_EXTENSION,
|
|
330
|
+
'ExtraPriority' => Asterisk::REDIRECT_PRIORITY,
|
|
331
|
+
'ExtraContext' => Asterisk::REDIRECT_CONTEXT
|
|
332
|
+
}) if other_call
|
|
333
|
+
send_ami_action 'Redirect', redirect_options
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def handle_hangup_event(code = nil, timestamp = nil)
|
|
337
|
+
code ||= 16
|
|
338
|
+
reason = @hangup_cause || HANGUP_CAUSE_TO_END_REASON[code]
|
|
339
|
+
@block_commands = true
|
|
340
|
+
@components.each_pair do |id, component|
|
|
341
|
+
component.call_ended
|
|
342
|
+
end
|
|
343
|
+
send_end_event reason, code, timestamp
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def after(*args, &block)
|
|
347
|
+
translator.after(*args, &block)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
private
|
|
351
|
+
|
|
352
|
+
def fetch_channel_var(variable)
|
|
353
|
+
result = @ami_client.send_action 'GetVar', 'Channel' => channel, 'Variable' => variable
|
|
354
|
+
result['Value'] == '(null)' ? nil : result['Value']
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def send_hangup_command(cause_code = 16)
|
|
358
|
+
send_ami_action 'Hangup', 'Channel' => channel, 'Cause' => cause_code
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def send_ami_action(name, headers = {})
|
|
362
|
+
AMIErrorConverter.convert { @ami_client.send_action name, headers }
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def send_end_event(reason, code = nil, timestamp = nil)
|
|
366
|
+
end_event = Adhearsion::Event::End.new(reason: reason, platform_code: code, timestamp: timestamp)
|
|
367
|
+
send_pb_event end_event
|
|
368
|
+
translator.deregister_call id, channel
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def execute_component(type, command, options = {})
|
|
372
|
+
type.new(command, self).tap do |component|
|
|
373
|
+
register_component component
|
|
374
|
+
component.execute
|
|
375
|
+
end
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def send_pb_event(event)
|
|
379
|
+
event.target_call_id = id
|
|
380
|
+
translator.handle_pb_event event
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def offer_event
|
|
384
|
+
Adhearsion::Event::Offer.new :to => agi_env.values_at(:agi_dnid, :agi_extension).detect { |e| e && e != 'unknown' },
|
|
385
|
+
:from => "#{agi_env[:agi_calleridname]} <#{[agi_env[:agi_type], agi_env[:agi_callerid]].join('/')}>",
|
|
386
|
+
:headers => sip_headers
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def sip_headers
|
|
390
|
+
agi_env.to_a.inject({}) do |accumulator, element|
|
|
391
|
+
accumulator['X-' + element[0].to_s] = element[1] || ''
|
|
392
|
+
accumulator
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def component_for_command_id_handle(ami_event)
|
|
397
|
+
if component = component_with_id(ami_event['CommandID'] || ami_event['CommandId'])
|
|
398
|
+
component.handle_ami_event ami_event
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def variable_for_headers(headers)
|
|
403
|
+
variables = { :adhearsion_call_id => id }
|
|
404
|
+
header_counter = 51
|
|
405
|
+
headers.each do |name, value|
|
|
406
|
+
variables["SIPADDHEADER#{header_counter}"] = "\"#{name}: #{value}\""
|
|
407
|
+
header_counter += 1
|
|
408
|
+
end
|
|
409
|
+
variables.inject([]) do |a, (k, v)|
|
|
410
|
+
a << "#{k}=#{v}"
|
|
411
|
+
end.join(',')
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'delegate'
|
|
4
|
+
|
|
5
|
+
module Adhearsion
|
|
6
|
+
module Translator
|
|
7
|
+
class Asterisk
|
|
8
|
+
class Channel < SimpleDelegator
|
|
9
|
+
NORMALIZATION_REGEXP = /^(?<prefix>Bridge\/)*(?<name>[^<>]*)(?<suffix><.*>)*$/.freeze
|
|
10
|
+
|
|
11
|
+
def self.new(other)
|
|
12
|
+
other.is_a?(self) ? other : super
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def name
|
|
16
|
+
matchdata[:name]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def prefix
|
|
20
|
+
matchdata[:prefix]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def suffix
|
|
24
|
+
matchdata[:suffix]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def bridged?
|
|
28
|
+
@bridged ||= (prefix || suffix)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_s
|
|
32
|
+
__getobj__
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def matchdata
|
|
38
|
+
@matchdata ||= __getobj__.match(NORMALIZATION_REGEXP)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|