Class: Adhearsion::Translator::Asterisk

Inherits:
Object
  • Object
show all
Includes:
Celluloid
Defined in:
lib/adhearsion/translator/asterisk.rb,
lib/adhearsion/translator/asterisk/call.rb,
lib/adhearsion/translator/asterisk/agi_app.rb,
lib/adhearsion/translator/asterisk/channel.rb,
lib/adhearsion/translator/asterisk/component.rb,
lib/adhearsion/translator/asterisk/agi_command.rb,
lib/adhearsion/translator/asterisk/unimrcp_app.rb,
lib/adhearsion/translator/asterisk/component/input.rb,
lib/adhearsion/translator/asterisk/component/output.rb,
lib/adhearsion/translator/asterisk/component/record.rb,
lib/adhearsion/translator/asterisk/component/asterisk.rb,
lib/adhearsion/translator/asterisk/ami_error_converter.rb,
lib/adhearsion/translator/asterisk/component/mrcp_prompt.rb,
lib/adhearsion/translator/asterisk/component/composed_prompt.rb,
lib/adhearsion/translator/asterisk/component/dtmf_recognizer.rb,
lib/adhearsion/translator/asterisk/component/input_component.rb,
lib/adhearsion/translator/asterisk/component/stop_by_redirect.rb,
lib/adhearsion/translator/asterisk/component/mrcp_recog_prompt.rb,
lib/adhearsion/translator/asterisk/component/mrcp_native_prompt.rb,
lib/adhearsion/translator/asterisk/component/asterisk/ami_action.rb,
lib/adhearsion/translator/asterisk/component/asterisk/agi_command.rb

Defined Under Namespace

Modules: AMIErrorConverter, Component Classes: AGIApp, AGICommand, Call, Channel, UniMRCPApp

Constant Summary collapse

ChannelGoneError =

Indicates that a command was executed against a channel which no longer exists

Class.new Error
REDIRECT_CONTEXT =
'adhearsion-redirect'
REDIRECT_EXTENSION =
'1'
REDIRECT_PRIORITY =
'1'
EVENTS_ALLOWED_BRIDGED =
%w{AGIExec AsyncAGI}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Celluloid

logger

Constructor Details

#initialize(ami_client, connection) ⇒ Asterisk

Returns a new instance of Asterisk.



46
47
48
49
# File 'lib/adhearsion/translator/asterisk.rb', line 46

def initialize(ami_client, connection)
  @ami_client, @connection = ami_client, connection
  @calls, @components, @channel_to_call_id, @bridges = {}, {}, {}, {}
end

Instance Attribute Details

#ami_clientObject (readonly)

Returns the value of attribute ami_client.



19
20
21
# File 'lib/adhearsion/translator/asterisk.rb', line 19

def ami_client
  @ami_client
end

#bridgesObject (readonly)

Returns the value of attribute bridges.



19
20
21
# File 'lib/adhearsion/translator/asterisk.rb', line 19

def bridges
  @bridges
end

#callsObject (readonly)

Returns the value of attribute calls.



19
20
21
# File 'lib/adhearsion/translator/asterisk.rb', line 19

def calls
  @calls
end

#connectionObject (readonly)

Returns the value of attribute connection.



19
20
21
# File 'lib/adhearsion/translator/asterisk.rb', line 19

def connection
  @connection
end

Class Method Details

.event_filter=(filter) ⇒ Object

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).

Examples:

A lambda

Translator::Asterisk.event_filter = ->(event) { event.name == 'AsyncAGI' }

Parameters:

  • ] (#<<RubyAMI::Event>)

    filter



36
37
38
# File 'lib/adhearsion/translator/asterisk.rb', line 36

def self.event_filter=(filter)
  @event_filter = filter
end

.event_passes_filter?(event) ⇒ Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/adhearsion/translator/asterisk.rb', line 40

def self.event_passes_filter?(event)
  @event_filter ? !!@event_filter[event] : true
end

Instance Method Details

#actor_died(actor, reason) ⇒ Object



178
179
180
181
182
183
184
185
186
# File 'lib/adhearsion/translator/asterisk.rb', line 178

def actor_died(actor, reason)
  return unless reason
  if id = @calls.key(actor)
    @calls.delete id
    end_event = Adhearsion::Event::End.new :target_call_id  => id,
                                           :reason          => :error
    handle_pb_event end_event
  end
end

#call_for_channel(channel) ⇒ Object



65
66
67
# File 'lib/adhearsion/translator/asterisk.rb', line 65

def call_for_channel(channel)
  call_with_id @channel_to_call_id[Channel.new(channel).name]
end

#call_with_id(call_id) ⇒ Object



61
62
63
# File 'lib/adhearsion/translator/asterisk.rb', line 61

def call_with_id(call_id)
  @calls[call_id]
end

#check_recording_directoryObject



174
175
176
# File 'lib/adhearsion/translator/asterisk.rb', line 174

def check_recording_directory
  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)
end

#component_with_id(component_id) ⇒ Object



77
78
79
# File 'lib/adhearsion/translator/asterisk.rb', line 77

def component_with_id(component_id)
  @components[component_id]
end

#deregister_call(id, channel) ⇒ Object



56
57
58
59
# File 'lib/adhearsion/translator/asterisk.rb', line 56

def deregister_call(id, channel)
  @channel_to_call_id.delete channel
  @calls.delete id
end

#deregister_component(id) ⇒ Object



73
74
75
# File 'lib/adhearsion/translator/asterisk.rb', line 73

def deregister_component(id)
  @components.delete id
end

#execute_call_command(command) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/adhearsion/translator/asterisk.rb', line 122

def execute_call_command(command)
  if call = call_with_id(command.target_call_id)
    begin
      call.execute_command command
    rescue => e
      Adhearsion::Events.trigger :exception, [e, logger]
      deregister_call call.id, call.channel
      command.response = Adhearsion::ProtocolError.new.setup :error, "Unknown error executing command on call #{command.target_call_id}", command.target_call_id
    end
  else
    command.response = Adhearsion::ProtocolError.new.setup :item_not_found, "Could not find a call with ID #{command.target_call_id}", command.target_call_id
  end
end

#execute_command(command, options = {}) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/adhearsion/translator/asterisk.rb', line 107

def execute_command(command, options = {})
  command.request!

  command.target_call_id ||= options[:call_id]
  command.component_id ||= options[:component_id]

  if command.target_call_id
    execute_call_command command
  elsif command.component_id
    execute_component_command command
  else
    execute_global_command command
  end
end

#execute_component_command(command) ⇒ Object



136
137
138
139
140
141
142
# File 'lib/adhearsion/translator/asterisk.rb', line 136

def execute_component_command(command)
  if (component = component_with_id(command.component_id))
    component.execute_command command
  else
    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
  end
end

#execute_global_command(command) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/adhearsion/translator/asterisk.rb', line 144

def execute_global_command(command)
  case command
  when Adhearsion::Rayo::Component::Asterisk::AMI::Action
    component = Component::Asterisk::AMIAction.new command, current_actor, ami_client
    register_component component
    component.execute
  when Adhearsion::Rayo::Command::Dial
    if call = call_with_id(command.uri)
      command.response = Adhearsion::ProtocolError.new.setup(:conflict, 'Call ID already in use')
    else
      call = Call.new command.to, self, ami_client, connection, nil, command.uri
      register_call call
      call.dial command
    end
  else
    command.response = Adhearsion::ProtocolError.new.setup 'command-not-acceptable', "Did not understand command"
  end
end

#handle_ami_event(event) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/adhearsion/translator/asterisk.rb', line 81

def handle_ami_event(event)
  return unless event.is_a? RubyAMI::Event

  if event.name == 'FullyBooted'
    handle_pb_event Adhearsion::Rayo::Connection::Connected.new
    run_at_fully_booted
    return
  end

  handle_varset_ami_event event

  ami_dispatch_to_or_create_call event
  if !ami_event_known_call?(event) && self.class.event_passes_filter?(event)
    handle_pb_event Adhearsion::Event::Asterisk::AMI.new(name: event.name, headers: event.headers)
  end
end

#handle_pb_event(event) ⇒ Object



98
99
100
# File 'lib/adhearsion/translator/asterisk.rb', line 98

def handle_pb_event(event)
  connection.handle_event event
end

#register_call(call) ⇒ Object



51
52
53
54
# File 'lib/adhearsion/translator/asterisk.rb', line 51

def register_call(call)
  @channel_to_call_id[call.channel] = call.id
  @calls[call.id] ||= call
end

#register_component(component) ⇒ Object



69
70
71
# File 'lib/adhearsion/translator/asterisk.rb', line 69

def register_component(component)
  @components[component.id] ||= component
end

#run_at_fully_bootedObject



163
164
165
166
167
168
169
170
171
172
# File 'lib/adhearsion/translator/asterisk.rb', line 163

def run_at_fully_booted
  send_ami_action 'Command', 'Command' => "dialplan add extension #{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY},AGI,agi:async into #{REDIRECT_CONTEXT}"

  result = send_ami_action 'Command', 'Command' => "dialplan show #{REDIRECT_CONTEXT}"
  if result.text_body =~ /failed/
    logger.error "Adhearsion failed to add the #{REDIRECT_EXTENSION} extension to the #{REDIRECT_CONTEXT} context. Please add a [#{REDIRECT_CONTEXT}] entry to your dialplan."
  end

  check_recording_directory
end

#send_message(call_id, domain, body, options = {}) ⇒ Object



102
103
104
105
# File 'lib/adhearsion/translator/asterisk.rb', line 102

def send_message(call_id, domain, body, options = {})
  call = call_with_id call_id
  call.send_message body if call
end