class Punchblock::Translator::Asterisk::Call
Constants
- HANGUP_CAUSE_TO_END_REASON
- InvalidCommandError
- OUTBOUND_CHANNEL_MATCH
Attributes
agi_env[R]
channel[R]
direction[R]
id[R]
pending_joins[R]
translator[R]
Public Class Methods
new(channel, translator, ami_client, connection, agi_env = nil, id = nil)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 28 def initialize(channel, translator, ami_client, connection, agi_env = nil, id = nil) @channel, @translator, @ami_client, @connection = channel, translator, ami_client, connection @agi_env = agi_env || {} @id = id || Punchblock.new_uuid @components = {} @answered = false @pending_joins = {} @progress_sent = false @block_commands = false @channel_variables = {} @hangup_cause = nil end
Public Instance Methods
after(*args, &block)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 335 def after(*args, &block) translator.after(*args, &block) end
answered?()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 96 def answered? @answered end
channel=(other)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 106 def channel=(other) @channel = other end
channel_var(variable)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 58 def channel_var(variable) @channel_variables[variable] || fetch_channel_var(variable) end
component_with_id(component_id)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 49 def component_with_id(component_id) @components[component_id] end
deregister_component(id)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 45 def deregister_component(id) @components.delete id end
dial(dial_command)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 67 def dial(dial_command) @direction = :outbound channel = dial_command.to || '' channel.match(OUTBOUND_CHANNEL_MATCH) { |m| channel = m[:channel] } params = { :async => true, :context => REDIRECT_CONTEXT, :exten => REDIRECT_EXTENSION, :priority => REDIRECT_PRIORITY, :channel => channel, :callerid => dial_command.from } params[:variable] = variable_for_headers dial_command.headers params[:timeout] = dial_command.timeout unless dial_command.timeout.nil? originate_action = Punchblock::Component::Asterisk::AMI::Action.new :name => 'Originate', :params => params originate_action.request! translator.async.execute_global_command originate_action dial_command.response = Ref.new uri: id end
execute_agi_command(command, *params)
click to toggle source
@return [Hash] AGI result
@raises RubyAMI::Error, ChannelGoneError
# File lib/punchblock/translator/asterisk/call.rb, line 293 def execute_agi_command(command, *params) agi = AGICommand.new Punchblock.new_uuid, channel, command, *params response = Celluloid::Future.new register_tmp_handler :ami, [{name: 'AsyncAGI', [:[], 'SubEvent'] => 'Exec'}, {name: 'AsyncAGIExec'}], [{[:[], 'CommandID'] => agi.id}, {[:[], 'CommandId'] => agi.id}] do |event| response.signal Celluloid::SuccessResponse.new(nil, event) end agi.execute @ami_client event = response.value return unless event agi.parse_result event end
execute_command(command)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 192 def execute_command(command) if @block_commands command.response = ProtocolError.new.setup :item_not_found, "Could not find a call with ID #{id}", id return end if command.component_id if component = component_with_id(command.component_id) component.execute_command command else command.response = ProtocolError.new.setup :item_not_found, "Could not find a component with ID #{command.component_id} for call #{id}", id, command.component_id end end case command when Command::Accept if outbound? command.response = true else execute_agi_command 'EXEC RINGING' command.response = true end when Command::Answer execute_agi_command 'ANSWER' @answered = true command.response = true when Command::Hangup send_hangup_command @hangup_cause = :hangup_command command.response = true when Command::Join other_call = translator.call_with_id command.call_uri if other_call @pending_joins[other_call.channel] = command execute_agi_command 'EXEC Bridge', "#{other_call.channel},F(#{REDIRECT_CONTEXT},#{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY})" else command.response = ProtocolError.new.setup :service_unavailable, "Could not find join party with address #{command.call_uri}", id end when Command::Unjoin other_call = translator.call_with_id command.call_uri redirect_back other_call command.response = true when Command::Reject case command.reason when :busy execute_agi_command 'EXEC Busy' when :decline send_hangup_command 21 when :error execute_agi_command 'EXEC Congestion' else execute_agi_command 'EXEC Congestion' end command.response = true when Command::Redirect execute_agi_command 'EXEC Transfer', command.to status = channel_var 'TRANSFERSTATUS' command.response = case status when 'SUCCESS' true else ProtocolError.new.setup 'error', "TRANSFERSTATUS was #{status}", id end when Punchblock::Component::Asterisk::AGI::Command execute_component Component::Asterisk::AGICommand, command when Punchblock::Component::Output execute_component Component::Output, command when Punchblock::Component::Input execute_component Component::Input, command when Punchblock::Component::Prompt component_class = case command.input.recognizer when 'unimrcp' case command.output.renderer when 'unimrcp' Component::MRCPPrompt when 'asterisk' Component::MRCPNativePrompt else raise InvalidCommandError, 'Invalid recognizer/renderer combination' end else Component::ComposedPrompt end execute_component component_class, command when Punchblock::Component::Record execute_component Component::Record, command else command.response = ProtocolError.new.setup 'command-not-acceptable', "Did not understand command for call #{id}", id end rescue InvalidCommandError => e command.response = ProtocolError.new.setup :invalid_command, e.message, id rescue ChannelGoneError command.response = ProtocolError.new.setup :item_not_found, "Could not find a call with ID #{id}", id rescue RubyAMI::Error => e command.response = ProtocolError.new.setup 'error', e.message, id rescue Celluloid::DeadActorError command.response = ProtocolError.new.setup :item_not_found, "Could not find a component with ID #{command.component_id} for call #{id}", id, command.component_id end
handle_hangup_event(code = nil, timestamp = nil)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 325 def handle_hangup_event(code = nil, timestamp = nil) code ||= 16 reason = @hangup_cause || HANGUP_CAUSE_TO_END_REASON[code] @block_commands = true @components.each_pair do |id, component| component.call_ended end send_end_event reason, code, timestamp end
inbound?()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 92 def inbound? direction == :inbound end
logger_id()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 305 def logger_id "#{self.class}: #{id}" end
outbound?()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 88 def outbound? direction == :outbound end
process_ami_event(ami_event)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 110 def process_ami_event(ami_event) if Asterisk.event_passes_filter?(ami_event) send_pb_event Event::Asterisk::AMI::Event.new(name: ami_event.name, headers: ami_event.headers) end case ami_event.name when 'Hangup' handle_hangup_event ami_event['Cause'].to_i, ami_event.best_time when 'AsyncAGI' component_for_command_id_handle ami_event if @answered == false && ami_event['SubEvent'] == 'Start' @answered = true send_pb_event Event::Answered.new(timestamp: ami_event.best_time) end when 'AsyncAGIStart' component_for_command_id_handle ami_event if @answered == false @answered = true send_pb_event Event::Answered.new(timestamp: ami_event.best_time) end when 'AsyncAGIExec', 'AsyncAGIEnd' component_for_command_id_handle ami_event when 'Newstate' case ami_event['ChannelState'] when '5' send_pb_event Event::Ringing.new(timestamp: ami_event.best_time) end when 'BridgeEnter' if other_call = ami_event['OtherCall'] event = Event::Joined.new call_uri: other_call.id, timestamp: ami_event.best_time send_pb_event event other_call_event = Event::Joined.new call_uri: id, timestamp: ami_event.best_time other_call_event.target_call_id = other_call.id translator.handle_pb_event other_call_event end when 'BridgeLeave' if other_call = ami_event['OtherCall'] event = Event::Unjoined.new call_uri: other_call.id, timestamp: ami_event.best_time send_pb_event event other_call_event = Event::Unjoined.new call_uri: id, timestamp: ami_event.best_time other_call_event.target_call_id = other_call.id translator.handle_pb_event other_call_event end when 'OriginateResponse' if ami_event['Response'] == 'Failure' && ami_event['Uniqueid'] == '<null>' send_end_event :error, nil, ami_event.best_time end when 'BridgeExec' join_command = @pending_joins.delete ami_event['Channel1'] join_command ||= @pending_joins.delete ami_event['Channel2'] join_command.response = true if join_command when 'Bridge' other_call_channel = ([ami_event['Channel1'], ami_event['Channel2']] - [channel]).first if other_call = translator.call_for_channel(other_call_channel) event = case ami_event['Bridgestate'] when 'Link' Event::Joined.new call_uri: other_call.id, timestamp: ami_event.best_time when 'Unlink' Event::Unjoined.new call_uri: other_call.id, timestamp: ami_event.best_time end send_pb_event event end when 'Unlink' other_call_channel = ([ami_event['Channel1'], ami_event['Channel2']] - [channel]).first if other_call = translator.call_for_channel(other_call_channel) send_pb_event Event::Unjoined.new(call_uri: other_call.id, timestamp: ami_event.best_time) end when 'VarSet' @channel_variables[ami_event['Variable']] = ami_event['Value'] end trigger_handler :ami, ami_event end
redirect_back(other_call = nil)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 309 def redirect_back(other_call = nil) redirect_options = { 'Channel' => channel, 'Exten' => Asterisk::REDIRECT_EXTENSION, 'Priority' => Asterisk::REDIRECT_PRIORITY, 'Context' => Asterisk::REDIRECT_CONTEXT } redirect_options.merge!({ 'ExtraChannel' => other_call.channel, 'ExtraExten' => Asterisk::REDIRECT_EXTENSION, 'ExtraPriority' => Asterisk::REDIRECT_PRIORITY, 'ExtraContext' => Asterisk::REDIRECT_CONTEXT }) if other_call send_ami_action 'Redirect', redirect_options end
register_component(component)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 41 def register_component(component) @components[component.id] ||= component end
send_message(body)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 187 def send_message(body) execute_agi_command 'EXEC SendText', body rescue end
send_offer()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 53 def send_offer @direction = :inbound send_pb_event offer_event end
send_progress()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 100 def send_progress return if answered? || outbound? || @progress_sent @progress_sent = true execute_agi_command "EXEC Progress" end
to_s()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 62 def to_s "#<#{self.class}:#{id} Channel: #{channel.inspect}>" end
Also aliased as: inspect
Private Instance Methods
component_for_command_id_handle(ami_event)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 385 def component_for_command_id_handle(ami_event) if component = component_with_id(ami_event['CommandID'] || ami_event['CommandId']) component.handle_ami_event ami_event end end
execute_component(type, command, options = {})
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 360 def execute_component(type, command, options = {}) type.new(command, self).tap do |component| register_component component component.execute end end
fetch_channel_var(variable)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 341 def fetch_channel_var(variable) result = @ami_client.send_action 'GetVar', 'Channel' => channel, 'Variable' => variable result['Value'] == '(null)' ? nil : result['Value'] end
offer_event()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 372 def offer_event Event::Offer.new :to => agi_env.values_at(:agi_dnid, :agi_extension).detect { |e| e && e != 'unknown' }, :from => "#{agi_env[:agi_calleridname]} <#{[agi_env[:agi_type], agi_env[:agi_callerid]].join('/')}>", :headers => sip_headers end
send_ami_action(name, headers = {})
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 350 def send_ami_action(name, headers = {}) AMIErrorConverter.convert { @ami_client.send_action name, headers } end
send_end_event(reason, code = nil, timestamp = nil)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 354 def send_end_event(reason, code = nil, timestamp = nil) end_event = Event::End.new(reason: reason, platform_code: code, timestamp: timestamp) send_pb_event end_event translator.deregister_call id, channel end
send_hangup_command(cause_code = 16)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 346 def send_hangup_command(cause_code = 16) send_ami_action 'Hangup', 'Channel' => channel, 'Cause' => cause_code end
send_pb_event(event)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 367 def send_pb_event(event) event.target_call_id = id translator.handle_pb_event event end
sip_headers()
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 378 def sip_headers agi_env.to_a.inject({}) do |accumulator, element| accumulator['X-' + element[0].to_s] = element[1] || '' accumulator end end
variable_for_headers(headers)
click to toggle source
# File lib/punchblock/translator/asterisk/call.rb, line 391 def variable_for_headers(headers) variables = { :punchblock_call_id => id } header_counter = 51 headers.each do |name, value| variables["SIPADDHEADER#{header_counter}"] = "\"#{name}: #{value}\"" header_counter += 1 end variables.inject([]) do |a, (k, v)| a << "#{k}=#{v}" end.join(',') end