Class: Punchblock::Translator::Asterisk

Inherits:
Object
  • Object
show all
Extended by:
ActiveSupport::Autoload
Includes:
Celluloid
Defined in:
lib/punchblock/translator/asterisk.rb,
lib/punchblock/translator/asterisk/call.rb,
lib/punchblock/translator/asterisk/agi_app.rb,
lib/punchblock/translator/asterisk/channel.rb,
lib/punchblock/translator/asterisk/component.rb,
lib/punchblock/translator/asterisk/agi_command.rb,
lib/punchblock/translator/asterisk/unimrcp_app.rb,
lib/punchblock/translator/asterisk/component/input.rb,
lib/punchblock/translator/asterisk/component/output.rb,
lib/punchblock/translator/asterisk/component/record.rb,
lib/punchblock/translator/asterisk/component/asterisk.rb,
lib/punchblock/translator/asterisk/ami_error_converter.rb,
lib/punchblock/translator/asterisk/component/mrcp_prompt.rb,
lib/punchblock/translator/asterisk/component/composed_prompt.rb,
lib/punchblock/translator/asterisk/component/stop_by_redirect.rb,
lib/punchblock/translator/asterisk/component/mrcp_recog_prompt.rb,
lib/punchblock/translator/asterisk/component/mrcp_native_prompt.rb,
lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb,
lib/punchblock/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 Punchblock::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

Constructor Details

#initialize(ami_client, connection) ⇒ Asterisk

Returns a new instance of Asterisk.


48
49
50
51
# File 'lib/punchblock/translator/asterisk.rb', line 48

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


21
22
23
# File 'lib/punchblock/translator/asterisk.rb', line 21

def ami_client
  @ami_client
end

#callsObject (readonly)

Returns the value of attribute calls


21
22
23
# File 'lib/punchblock/translator/asterisk.rb', line 21

def calls
  @calls
end

#connectionObject (readonly)

Returns the value of attribute connection


21
22
23
# File 'lib/punchblock/translator/asterisk.rb', line 21

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

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

Parameters:

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

    filter


38
39
40
# File 'lib/punchblock/translator/asterisk.rb', line 38

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

.event_passes_filter?(event) ⇒ Boolean

Returns:

  • (Boolean)

42
43
44
# File 'lib/punchblock/translator/asterisk.rb', line 42

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

Instance Method Details

#actor_died(actor, reason) ⇒ Object


196
197
198
199
200
201
202
203
204
# File 'lib/punchblock/translator/asterisk.rb', line 196

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

#call_for_channel(channel) ⇒ Object


67
68
69
# File 'lib/punchblock/translator/asterisk.rb', line 67

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

#call_with_id(call_id) ⇒ Object


63
64
65
# File 'lib/punchblock/translator/asterisk.rb', line 63

def call_with_id(call_id)
  @calls[call_id]
end

#check_recording_directoryObject


192
193
194
# File 'lib/punchblock/translator/asterisk.rb', line 192

def check_recording_directory
  pb_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


79
80
81
# File 'lib/punchblock/translator/asterisk.rb', line 79

def component_with_id(component_id)
  @components[component_id]
end

#deregister_call(id, channel) ⇒ Object


58
59
60
61
# File 'lib/punchblock/translator/asterisk.rb', line 58

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

#deregister_component(id) ⇒ Object


75
76
77
# File 'lib/punchblock/translator/asterisk.rb', line 75

def deregister_component(id)
  @components.delete id
end

#execute_call_command(command) ⇒ Object


142
143
144
145
146
147
148
149
150
151
152
# File 'lib/punchblock/translator/asterisk.rb', line 142

def execute_call_command(command)
  if call = call_with_id(command.target_call_id)
    begin
      call.execute_command command
    rescue => e
      deregister_call call.id, call.channel
    end
  else
    command.response = 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


127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/punchblock/translator/asterisk.rb', line 127

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


154
155
156
157
158
159
160
# File 'lib/punchblock/translator/asterisk.rb', line 154

def execute_component_command(command)
  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}", command.target_call_id, command.component_id
  end
end

#execute_global_command(command) ⇒ Object


162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/punchblock/translator/asterisk.rb', line 162

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

#handle_ami_event(event) ⇒ Object


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/punchblock/translator/asterisk.rb', line 83

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

  case event.name
  when 'FullyBooted'
    handle_pb_event Connection::Connected.new
    run_at_fully_booted
    return
  when 'BridgeEnter'
    if other_channel = @bridges.delete(event['BridgeUniqueid'])
      if event['OtherCall'] = call_for_channel(other_channel)
        join_command = event['OtherCall'].pending_joins.delete event['Channel']
        call = call_for_channel(event['Channel'])
        join_command ||= call.pending_joins.delete other_channel if call
        join_command.response = true if join_command
      end
    else
      @bridges[event['BridgeUniqueid']] = event['Channel']
    end
  when 'BridgeLeave'
    if other_channel = @bridges.delete(event['BridgeUniqueid'] + '_leave')
      event['OtherCall'] = call_for_channel(other_channel)
    else
      @bridges[event['BridgeUniqueid'] + '_leave'] = event['Channel']
    end
  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 Event::Asterisk::AMI::Event.new(name: event.name, headers: event.headers)
  end
end

#handle_pb_event(event) ⇒ Object


118
119
120
# File 'lib/punchblock/translator/asterisk.rb', line 118

def handle_pb_event(event)
  connection.handle_event event
end

#register_call(call) ⇒ Object


53
54
55
56
# File 'lib/punchblock/translator/asterisk.rb', line 53

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

#register_component(component) ⇒ Object


71
72
73
# File 'lib/punchblock/translator/asterisk.rb', line 71

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

#run_at_fully_bootedObject


181
182
183
184
185
186
187
188
189
190
# File 'lib/punchblock/translator/asterisk.rb', line 181

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/
    pb_logger.error "Punchblock 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


122
123
124
125
# File 'lib/punchblock/translator/asterisk.rb', line 122

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