Class: Adhearsion::Call

Inherits:
Object show all
Includes:
Celluloid, HasGuardedHandlers
Defined in:
lib/adhearsion/call.rb

Overview

Encapsulates call-related data and behavior.

Direct Known Subclasses

OutboundCall

Constant Summary collapse

Hangup =
Class.new Adhearsion::Error
CommandTimeout =
Class.new Adhearsion::Error
ExpiredError =
Class.new Celluloid::DeadActorError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Celluloid

logger

Constructor Details

#initialize(offer = nil) ⇒ Call

Returns a new instance of Call.


86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/adhearsion/call.rb', line 86

def initialize(offer = nil)
  register_initial_handlers

  @offer        = nil
  @tags         = []
  @commands     = CommandRegistry.new
  @variables    = HashWithIndifferentAccess.new
  @controllers  = []
  @end_reason   = nil
  @end_code     = nil
  @end_blocker  = Celluloid::Condition.new
  @peers        = {}
  @duration     = nil
  @auto_hangup  = true
  @after_hangup_lifetime = nil

  self << offer if offer
end

Instance Attribute Details

#after_hangup_lifetimeInteger

Returns the number of seconds after the call is hung up that the controller will remain active.

Returns:

  • (Integer)

    the number of seconds after the call is hung up that the controller will remain active


67
68
69
# File 'lib/adhearsion/call.rb', line 67

def after_hangup_lifetime
  @after_hangup_lifetime
end

#answer_timeTime (readonly)

Returns the time at which the call was answered.

Returns:

  • (Time)

    the time at which the call was answered


58
59
60
# File 'lib/adhearsion/call.rb', line 58

def answer_time
  @answer_time
end

#auto_hanguptrue, false

Returns whether or not the call should be automatically hung up after executing its controller.

Returns:

  • (true, false)

    whether or not the call should be automatically hung up after executing its controller


64
65
66
# File 'lib/adhearsion/call.rb', line 64

def auto_hangup
  @auto_hangup
end

#controllersArray<Adhearsion::CallController> (readonly)

Returns the set of call controllers executing on the call.

Returns:


49
50
51
# File 'lib/adhearsion/call.rb', line 49

def controllers
  @controllers
end

#end_codeString (readonly)

Returns the reason code for the call ending.

Returns:

  • (String)

    the reason code for the call ending


46
47
48
# File 'lib/adhearsion/call.rb', line 46

def end_code
  @end_code
end

#end_reasonSymbol (readonly)

Returns the reason for the call ending.

Returns:

  • (Symbol)

    the reason for the call ending


43
44
45
# File 'lib/adhearsion/call.rb', line 43

def end_reason
  @end_reason
end

#end_timeTime (readonly)

Returns the time at which the call ended (was hung up).

Returns:

  • (Time)

    the time at which the call ended (was hung up)


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

def end_time
  @end_time
end

#start_timeTime (readonly)

Returns the time at which the call began. For inbound calls this is the time at which the call was offered to Adhearsion. For outbound calls it is the time at which the call was dialed.

Returns:

  • (Time)

    the time at which the call began. For inbound calls this is the time at which the call was offered to Adhearsion. For outbound calls it is the time at which the call was dialed


55
56
57
# File 'lib/adhearsion/call.rb', line 55

def start_time
  @start_time
end

#variablesHash<String => String> (readonly)

Returns a collection of SIP headers set during the call.

Returns:

  • (Hash<String => String>)

    a collection of SIP headers set during the call


52
53
54
# File 'lib/adhearsion/call.rb', line 52

def variables
  @variables
end

Class Method Details

.uri(transport, id, domain) ⇒ Object


77
78
79
80
81
82
83
84
# File 'lib/adhearsion/call.rb', line 77

def self.uri(transport, id, domain)
  return nil unless id
  s = ""
  s << transport << ":" if transport
  s << id
  s << "@" << domain if domain
  s
end

Instance Method Details

#accept(headers = nil) ⇒ Object


320
321
322
323
324
# File 'lib/adhearsion/call.rb', line 320

def accept(headers = nil)
  @accept_command ||= write_and_await_response Adhearsion::Rayo::Command::Accept.new(:headers => headers)
rescue Adhearsion::ProtocolError => e
  abort e
end

#active?Boolean

Returns if the call is currently active or not (disconnected).

Returns:

  • (Boolean)

    if the call is currently active or not (disconnected)


316
317
318
# File 'lib/adhearsion/call.rb', line 316

def active?
  !end_reason
end

#answer(headers = nil) ⇒ Object


326
327
328
329
330
331
# File 'lib/adhearsion/call.rb', line 326

def answer(headers = nil)
  write_and_await_response Adhearsion::Rayo::Command::Answer.new(:headers => headers)
  @answer_time = Time.now
rescue Adhearsion::ProtocolError => e
  abort e
end

#commandsObject


209
210
211
# File 'lib/adhearsion/call.rb', line 209

def commands
  @commands.clone
end

#deliver_message(message) ⇒ Object Also known as: <<


201
202
203
204
205
206
# File 'lib/adhearsion/call.rb', line 201

def deliver_message(message)
  logger.debug "Receiving message: #{message.inspect}"
  catching_standard_errors do
    trigger_handler :event, message, broadcast: true, exception_callback: ->(e) { Adhearsion::Events.trigger :exception, [e, logger] }
  end
end

#domainString?

Returns The domain on which the call resides.

Returns:

  • (String, nil)

    The domain on which the call resides


116
117
118
# File 'lib/adhearsion/call.rb', line 116

def domain
  offer.domain if offer
end

#durationFloat

Returns The call duration until the current time, or until the call was disconnected, whichever is earlier.

Returns:

  • (Float)

    The call duration until the current time, or until the call was disconnected, whichever is earlier


270
271
272
273
274
275
276
277
278
# File 'lib/adhearsion/call.rb', line 270

def duration
  if @duration
    @duration
  elsif @start_time
    Time.now.to_i - @start_time.to_i
  else
    0.0
  end
end

#execute_controller(controller = nil, completion_callback = nil) { ... } ⇒ Celluloid::ThreadHandle

Execute a call controller asynchronously against this call.

To block and wait until the controller completes, call `#join` on the result of this method.

Parameters:

  • controller (Adhearsion::CallController) (defaults to: nil)

    an instance of a controller initialized for this call

  • a (Proc)

    callback to be executed when the controller finishes execution

Yields:

  • execute the current block as the body of a controller by specifying no controller instance

Returns:

  • (Celluloid::ThreadHandle)

Raises:

  • (ArgumentError)

565
566
567
568
569
570
# File 'lib/adhearsion/call.rb', line 565

def execute_controller(controller = nil, completion_callback = nil, &block)
  raise ArgumentError, "Cannot supply a controller and a block at the same time" if controller && block_given?
  controller ||= CallController.new current_actor, &block
  logger.info "Executing controller #{controller.class}"
  controller.bg_exec completion_callback
end

#fromString

Returns the value of the From header from the signaling protocol.

Returns:

  • (String)

    the value of the From header from the signaling protocol


75
# File 'lib/adhearsion/call.rb', line 75

delegate :from, to: :offer, allow_nil: true

#hangup(headers = nil) ⇒ Object


360
361
362
363
364
365
366
367
# File 'lib/adhearsion/call.rb', line 360

def hangup(headers = nil)
  return false unless active?
  logger.info "Hanging up"
  @end_reason = true
  write_and_await_response Adhearsion::Rayo::Command::Hangup.new(:headers => headers)
rescue Adhearsion::ProtocolError => e
  abort e
end

#idString? Also known as: to_s

Returns The globally unique ID for the call.

Returns:

  • (String, nil)

    The globally unique ID for the call


108
109
110
# File 'lib/adhearsion/call.rb', line 108

def id
  offer.target_call_id if offer
end

#join(target, options = {}) ⇒ Hash

Joins this call to another call or a mixer

Parameters:

  • target (Call, String, Hash)

    the target to join to. May be a Call object, a call ID (String, Hash) or a mixer name (Hash)

  • options (Hash, Optional) (defaults to: {})

    further options to be joined with

Options Hash (target):

  • call_uri (String)

    The call ID to join to

  • mixer_name (String)

    The mixer to join to

Returns:

  • (Hash)

    where :command is the issued command, :joined_waiter is a #wait responder which is triggered when the join is complete, and :unjoined_waiter is a #wait responder which is triggered when the entities are unjoined


384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/adhearsion/call.rb', line 384

def join(target, options = {})
  logger.debug "Joining to #{target}"

  joined_condition = CountDownLatch.new(1)
  on_joined target do
    joined_condition.countdown!
  end

  unjoined_condition = CountDownLatch.new(1)
  on_unjoined target do
    unjoined_condition.countdown!
  end

  on_end do
    joined_condition.countdown!
    unjoined_condition.countdown!
  end

  command = Adhearsion::Rayo::Command::Join.new options.merge(join_options_with_target(target))
  write_and_await_response command
  {command: command, joined_condition: joined_condition, unjoined_condition: unjoined_condition}
rescue Adhearsion::ProtocolError => e
  abort e
end

#muteObject


462
463
464
465
466
# File 'lib/adhearsion/call.rb', line 462

def mute
  write_and_await_response Adhearsion::Rayo::Command::Mute.new
rescue Adhearsion::ProtocolError => e
  abort e
end

#on_end(&block) ⇒ Object


309
310
311
# File 'lib/adhearsion/call.rb', line 309

def on_end(&block)
  register_event_handler Adhearsion::Event::End, &block
end

#on_joined(target = nil, &block) ⇒ Object

Registers a callback for when this call is joined to another call or a mixer

Parameters:

  • target (Call, String, Hash, nil) (defaults to: nil)

    the target to guard on. May be a Call object, a call ID (String, Hash) or a mixer name (Hash)

Options Hash (target):

  • call_uri (String)

    The call ID to guard on

  • mixer_name (String)

    The mixer name to guard on


287
288
289
290
291
# File 'lib/adhearsion/call.rb', line 287

def on_joined(target = nil, &block)
  register_event_handler Adhearsion::Event::Joined, *guards_for_target(target) do |event|
    block.call event
  end
end

#on_unjoined(target = nil, &block) ⇒ Object

Registers a callback for when this call is unjoined from another call or a mixer

Parameters:

  • target (Call, String, Hash, nil) (defaults to: nil)

    the target to guard on. May be a Call object, a call ID (String, Hash) or a mixer name (Hash)

Options Hash (target):

  • call_uri (String)

    The call ID to guard on

  • mixer_name (String)

    The mixer name to guard on


300
301
302
# File 'lib/adhearsion/call.rb', line 300

def on_unjoined(target = nil, &block)
  register_event_handler Adhearsion::Event::Unjoined, *guards_for_target(target), &block
end

#peersHash<String => Adhearsion::Call>

Hash of joined peers

Returns:


166
167
168
# File 'lib/adhearsion/call.rb', line 166

def peers
  @peers.clone
end

#redirect(to, headers = nil) ⇒ Object

Redirect the call to some other target system.

If the redirect is successful, the call will be released from the telephony engine and Adhearsion will lose control of the call.

Note that for the common case, this will result in a SIP 302 or SIP REFER, which provides the caller with a new URI to dial. As such, the redirect target cannot be any telephony-engine specific address (such as sofia/gateway, agent/101, or SIP/mypeer); instead it should be a fully-qualified external SIP URI that the caller can independently reach.

Parameters:

  • to (String)

    the target to redirect to, eg a SIP URI

  • headers (Hash, optional) (defaults to: nil)

    a set of headers to send along with the redirect instruction


354
355
356
357
358
# File 'lib/adhearsion/call.rb', line 354

def redirect(to, headers = nil)
  write_and_await_response Adhearsion::Rayo::Command::Redirect.new(to: to, headers: headers)
rescue Adhearsion::ProtocolError => e
  abort e
end

#register_event_handler(*guards) {|Object| ... } ⇒ String

Register a handler for events on this call. Note that Adhearsion::Call implements the has-guarded-handlers API, and all of its methods are available. Specifically, all Adhearsion events are available on the `:event` channel.

Parameters:

  • guards (guards)

    take a look at the guards documentation

Yields:

  • (Object)

    trigger_object the incoming event

Returns:

  • (String)

    handler ID for later manipulation

See Also:


197
198
199
# File 'lib/adhearsion/call.rb', line 197

def register_event_handler(*guards, &block)
  register_handler :event, *guards, &block
end

#reject(reason = :busy, headers = nil) ⇒ Object


333
334
335
336
337
338
# File 'lib/adhearsion/call.rb', line 333

def reject(reason = :busy, headers = nil)
  write_and_await_response Adhearsion::Rayo::Command::Reject.new(:reason => reason, :headers => headers)
  Adhearsion::Events.trigger :call_rejected, call: current_actor, reason: reason
rescue Adhearsion::ProtocolError => e
  abort e
end

#remove_tag(label) ⇒ Object

Remove a label

Parameters:

  • label (String, Symbol)

149
150
151
# File 'lib/adhearsion/call.rb', line 149

def remove_tag(label)
  @tags.reject! { |tag| tag == label }
end

#routeObject


512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/adhearsion/call.rb', line 512

def route
  case Adhearsion::Process.state_name
  when :booting, :rejecting
    logger.info "Declining call because the process is not yet running."
    reject :decline
  when :running, :stopping
    logger.info "Routing call"
    Adhearsion.router.handle current_actor
  else
    reject :error
  end
rescue Call::Hangup, Call::ExpiredError
  logger.warn "Call routing could not be completed because call was unavailable."
  self << Adhearsion::Event::End.new(reason: :error)
end

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

Sends a message to the caller

Parameters:

  • body (String)

    The message text.

  • options (Hash, Optional) (defaults to: {})

    The message options.

Options Hash (options):

  • subject (String)

    The message subject.


535
536
537
538
# File 'lib/adhearsion/call.rb', line 535

def send_message(body, options = {})
  logger.debug "Sending message: #{body}"
  client.send_message id, domain, body, options
end

#tag(label) ⇒ Object

Tag a call with an arbitrary label

Parameters:

  • label (String, Symbol)

    String or Symbol with which to tag this call


139
140
141
142
# File 'lib/adhearsion/call.rb', line 139

def tag(label)
  abort ArgumentError.new "Tag must be a String or Symbol" unless [String, Symbol].include?(label.class)
  @tags << label
end

#tagged_with?(label) ⇒ Boolean

Establish if the call is tagged with the provided label

Parameters:

  • label (String, Symbol)

Returns:

  • (Boolean)

158
159
160
# File 'lib/adhearsion/call.rb', line 158

def tagged_with?(label)
  @tags.include? label
end

#tagsArray

Returns The set of labels with which this call has been tagged.

Returns:

  • (Array)

    The set of labels with which this call has been tagged.


130
131
132
# File 'lib/adhearsion/call.rb', line 130

def tags
  @tags.clone
end

#toString

Returns the value of the To header from the signaling protocol.

Returns:

  • (String)

    the value of the To header from the signaling protocol


72
# File 'lib/adhearsion/call.rb', line 72

delegate :to, to: :offer, allow_nil: true

#unjoin(target = nil) ⇒ Object

Unjoins this call from another call or a mixer

Parameters:

  • target (Call, String, Hash, nil) (defaults to: nil)

    the target to unjoin from. May be a Call object, a call ID (String, Hash), a mixer name (Hash) or missing to unjoin from every existing join (nil)

Options Hash (target):

  • call_uri (String)

    The call ID to unjoin from

  • mixer_name (String)

    The mixer to unjoin from


416
417
418
419
420
421
422
# File 'lib/adhearsion/call.rb', line 416

def unjoin(target = nil)
  logger.info "Unjoining from #{target}"
  command = Adhearsion::Rayo::Command::Unjoin.new join_options_with_target(target)
  write_and_await_response command
rescue Adhearsion::ProtocolError => e
  abort e
end

#unmuteObject


468
469
470
471
472
# File 'lib/adhearsion/call.rb', line 468

def unmute
  write_and_await_response Adhearsion::Rayo::Command::Unmute.new
rescue Adhearsion::ProtocolError => e
  abort e
end

#uriString?

Returns The uri at which the call resides.

Returns:

  • (String, nil)

    The uri at which the call resides


123
124
125
# File 'lib/adhearsion/call.rb', line 123

def uri
  self.class.uri(transport, id, domain)
end

#wait_for_end(timeout = nil) ⇒ Symbol

Wait for the call to end. Returns immediately if the call has already ended, else blocks until it does so.

Parameters:

  • timeout (Integer, nil) (defaults to: nil)

    a timeout after which to unblock, returning `:timeout`

Returns:

  • (Symbol)

    the reason for the call ending


176
177
178
179
180
181
182
183
184
# File 'lib/adhearsion/call.rb', line 176

def wait_for_end(timeout = nil)
  if end_reason
    end_reason
  else
    @end_blocker.wait(timeout)
  end
rescue Celluloid::ConditionError => e
  abort e
end

#wait_for_joined(expected_target) ⇒ Object


448
449
450
451
452
453
# File 'lib/adhearsion/call.rb', line 448

def wait_for_joined(expected_target)
  target = nil
  until target == expected_target do
    target = wait :joined
  end
end

#wait_for_unjoined(expected_target) ⇒ Object


455
456
457
458
459
460
# File 'lib/adhearsion/call.rb', line 455

def wait_for_unjoined(expected_target)
  target = nil
  until target == expected_target do
    target = wait :unjoined
  end
end