Class: Punchblock::Translator::Freeswitch::Call

Inherits:
Object
  • Object
show all
Includes:
Celluloid, HasGuardedHandlers, DeadActorSafety
Defined in:
lib/punchblock/translator/freeswitch/call.rb

Constant Summary collapse

HANGUP_CAUSE_TO_END_REASON =
Hash.new :error
REJECT_TO_HANGUP_REASON =
Hash.new 'NORMAL_TEMPORARY_FAILURE'

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from DeadActorSafety

#safe_from_dead_actors

Constructor Details

#initialize(id, translator, es_env = nil, stream = nil, media_engine = nil, default_voice = nil) ⇒ Call

Returns a new instance of Call.



40
41
42
43
44
45
46
47
# File 'lib/punchblock/translator/freeswitch/call.rb', line 40

def initialize(id, translator, es_env = nil, stream = nil, media_engine = nil, default_voice = nil)
  @id, @translator, @stream, @media_engine, @default_voice = id, translator, stream, media_engine, default_voice
  @es_env = es_env || {}
  @components = {}
  @pending_joins, @pending_unjoins = {}, {}
  @answered = false
  setup_handlers
end

Instance Attribute Details

#default_voiceObject (readonly)

Returns the value of attribute default_voice.



36
37
38
# File 'lib/punchblock/translator/freeswitch/call.rb', line 36

def default_voice
  @default_voice
end

#directionObject (readonly)

Returns the value of attribute direction.



36
37
38
# File 'lib/punchblock/translator/freeswitch/call.rb', line 36

def direction
  @direction
end

#es_envObject (readonly)

Returns the value of attribute es_env.



36
37
38
# File 'lib/punchblock/translator/freeswitch/call.rb', line 36

def es_env
  @es_env
end

#idObject (readonly)

Returns the value of attribute id.



36
37
38
# File 'lib/punchblock/translator/freeswitch/call.rb', line 36

def id
  @id
end

#media_engineObject (readonly)

Returns the value of attribute media_engine.



36
37
38
# File 'lib/punchblock/translator/freeswitch/call.rb', line 36

def media_engine
  @media_engine
end

#streamObject (readonly)

Returns the value of attribute stream.



36
37
38
# File 'lib/punchblock/translator/freeswitch/call.rb', line 36

def stream
  @stream
end

#translatorObject (readonly)

Returns the value of attribute translator.



36
37
38
# File 'lib/punchblock/translator/freeswitch/call.rb', line 36

def translator
  @translator
end

Instance Method Details

#actor_died(actor, reason) ⇒ Object



228
229
230
231
232
233
234
235
236
# File 'lib/punchblock/translator/freeswitch/call.rb', line 228

def actor_died(actor, reason)
  return unless reason
  pb_logger.error "A linked actor (#{actor.inspect}) died due to #{reason.inspect}"
  if id = @components.key(actor)
    @components.delete id
    complete_event = Punchblock::Event::Complete.new :component_id => id, :reason => Punchblock::Event::Complete::Error.new
    send_pb_event complete_event
  end
end

#answered?Boolean

Returns:

  • (Boolean)


163
164
165
# File 'lib/punchblock/translator/freeswitch/call.rb', line 163

def answered?
  @answered
end

#application(*args) ⇒ Object



115
116
117
# File 'lib/punchblock/translator/freeswitch/call.rb', line 115

def application(*args)
  stream.application id, *args
end

#component_with_id(component_id) ⇒ Object



53
54
55
# File 'lib/punchblock/translator/freeswitch/call.rb', line 53

def component_with_id(component_id)
  @components[component_id]
end

#dial(dial_command) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/punchblock/translator/freeswitch/call.rb', line 127

def dial(dial_command)
  @direction = :outbound

  cid_number, cid_name = dial_command.from, nil
  dial_command.from.match(/(?<cid_name>.*)<(?<cid_number>.*)>/) do |m|
    cid_name = m[:cid_name].strip
    cid_number = m[:cid_number]
  end

  options = {
    :return_ring_ready  => true,
    :origination_uuid   => id
  }
  options[:origination_caller_id_number] = "'#{cid_number}'" if cid_number.present?
  options[:origination_caller_id_name] = "'#{cid_name}'" if cid_name.present?
  options[:originate_timeout] = dial_command.timeout/1000 if dial_command.timeout
  dial_command.headers.each do |header|
    options["sip_h_#{header.name}"] = "'#{header.value}'"
  end
  opts = options.inject([]) do |a, (k, v)|
    a << "#{k}=#{v}"
  end.join(',')

  stream.bgapi "originate {#{opts}}#{dial_command.to} &park()"

  dial_command.response = Ref.new :id => id
end

#execute_command(command) ⇒ Object



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/punchblock/translator/freeswitch/call.rb', line 167

def execute_command(command)
  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
    application 'respond', '180 Ringing'
    command.response = true
  when Command::Answer
    if answered?
      command.response = true
    else
      command_id = Punchblock.new_uuid
      register_tmp_handler :es, :event_name => 'CHANNEL_ANSWER', [:[], :scope_variable_punchblock_command_id] => command_id do
        command.response = true
      end
      application 'answer', "%[punchblock_command_id=#{command_id}]"
    end
  when Command::Hangup
    hangup
    command.response = true
  when Command::Join
    @pending_joins[command.call_id] = command
    uuid_foo :bridge, command.call_id
  when Command::Unjoin
    @pending_unjoins[command.call_id] = command
    uuid_foo :transfer, '-both park inline'
  when Command::Reject
    hangup REJECT_TO_HANGUP_REASON[command.reason]
    command.response = true
  when Punchblock::Component::Output
    media_renderer = command.renderer || media_engine || :freeswitch
    case media_renderer.to_sym
    when :freeswitch, :native, nil
      execute_component Component::Output, command
    when :flite
      execute_component Component::FliteOutput, command, media_engine, default_voice
    else
      execute_component Component::TTSOutput, command, media_engine, default_voice
    end
  when Punchblock::Component::Input
    execute_component Component::Input, 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
end

#handle_es_event(event) ⇒ Object



111
112
113
# File 'lib/punchblock/translator/freeswitch/call.rb', line 111

def handle_es_event(event)
  trigger_handler :es, event
end

#hangup(reason = 'NORMAL_CLEARING') ⇒ Object



220
221
222
# File 'lib/punchblock/translator/freeswitch/call.rb', line 220

def hangup(reason = 'NORMAL_CLEARING')
  sendmsg :call_command => 'hangup', :hangup_cause => reason
end

#inbound?Boolean

Returns:

  • (Boolean)


159
160
161
# File 'lib/punchblock/translator/freeswitch/call.rb', line 159

def inbound?
  direction == :inbound
end

#logger_idObject



224
225
226
# File 'lib/punchblock/translator/freeswitch/call.rb', line 224

def logger_id
  "#{self.class}: #{id}"
end

#outbound?Boolean

Returns:

  • (Boolean)


155
156
157
# File 'lib/punchblock/translator/freeswitch/call.rb', line 155

def outbound?
  direction == :outbound
end

#register_component(component) ⇒ Object



49
50
51
# File 'lib/punchblock/translator/freeswitch/call.rb', line 49

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

#send_offerObject



57
58
59
60
# File 'lib/punchblock/translator/freeswitch/call.rb', line 57

def send_offer
  @direction = :inbound
  send_pb_event offer_event
end

#sendmsg(*args) ⇒ Object



119
120
121
# File 'lib/punchblock/translator/freeswitch/call.rb', line 119

def sendmsg(*args)
  stream.sendmsg id, *args
end

#setup_handlersObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
# File 'lib/punchblock/translator/freeswitch/call.rb', line 67

def setup_handlers
  register_handler :es, :event_name => 'CHANNEL_ANSWER' do
    @answered = true
    send_pb_event Event::Answered.new
    throw :pass
  end

  register_handler :es, :event_name => 'CHANNEL_STATE', [:[], :channel_call_state] => 'RINGING' do
    send_pb_event Event::Ringing.new
  end

  register_handler :es, :event_name => 'CHANNEL_HANGUP' do |event|
    @components.dup.each_pair do |id, component|
      safe_from_dead_actors do
        component.call_ended if component.alive?
      end
    end
    send_end_event HANGUP_CAUSE_TO_END_REASON[event[:hangup_cause]]
  end

  register_handler :es, :event_name => 'CHANNEL_BRIDGE' do |event|
    command = @pending_joins[event[:other_leg_unique_id]]
    command.response = true if command

    other_call_id = event[:unique_id] == id ? event[:other_leg_unique_id] : event[:unique_id]
    send_pb_event Event::Joined.new(:call_id => other_call_id)
  end

  register_handler :es, :event_name => 'CHANNEL_UNBRIDGE' do |event|
    command = @pending_unjoins[event[:other_leg_unique_id]]
    command.response = true if command

    other_call_id = event[:unique_id] == id ? event[:other_leg_unique_id] : event[:unique_id]
    send_pb_event Event::Unjoined.new(:call_id => other_call_id)
  end

  register_handler :es, [:has_key?, :scope_variable_punchblock_component_id] => true do |event|
    if component = component_with_id(event[:scope_variable_punchblock_component_id])
      safe_from_dead_actors { component.handle_es_event event if component.alive? }
    end
    throw :pass
  end
end

#to_sObject Also known as: inspect



62
63
64
# File 'lib/punchblock/translator/freeswitch/call.rb', line 62

def to_s
  "#<#{self.class}:#{id}>"
end

#uuid_foo(app, args = '') ⇒ Object



123
124
125
# File 'lib/punchblock/translator/freeswitch/call.rb', line 123

def uuid_foo(app, args = '')
  stream.bgapi "uuid_#{app} #{id} #{args}"
end