lib/adhearsion/call.rb in adhearsion-2.3.5 vs lib/adhearsion/call.rb in adhearsion-2.4.0.beta1
- old
+ new
@@ -1,9 +1,10 @@
# encoding: utf-8
require 'has_guarded_handlers'
require 'thread'
+require 'active_support/hash_with_indifferent_access'
module Adhearsion
##
# Encapsulates call-related data and behavior.
#
@@ -15,10 +16,11 @@
include Celluloid
include HasGuardedHandlers
execute_block_on_receiver :register_handler, :register_tmp_handler, :register_handler_with_priority, :register_event_handler, :on_joined, :on_unjoined, :on_end, :execute_controller
+ finalizer :finalize
def self.new(*args, &block)
super.tap do |proxy|
def proxy.method_missing(*args)
super
@@ -26,25 +28,27 @@
raise ExpiredError, "This call is expired and is no longer accessible"
end
end
end
- attr_reader :end_reason, :commands, :controllers, :variables
+ attr_reader :end_reason, :controllers, :variables, :start_time, :end_time
delegate :[], :[]=, :to => :variables
delegate :to, :from, :to => :offer, :allow_nil => true
def initialize(offer = nil)
register_initial_handlers
@offer = nil
@tags = []
@commands = CommandRegistry.new
- @variables = {}
+ @variables = HashWithIndifferentAccess.new
@controllers = []
@end_reason = nil
+ @end_blocker = Celluloid::Condition.new
@peers = {}
+ @duration = nil
self << offer if offer
end
#
@@ -53,10 +57,29 @@
def id
offer.target_call_id if offer
end
#
+ # @return [String, nil] The domain on which the call resides
+ #
+ def domain
+ offer.domain if offer
+ end
+
+ #
+ # @return [String, nil] The uri at which the call resides
+ #
+ def uri
+ return nil unless id
+ s = ""
+ s << transport << ":" if transport
+ s << id
+ s << "@" << domain if domain
+ s
+ end
+
+ #
# @return [Array] The set of labels with which this call has been tagged.
#
def tags
@tags.clone
end
@@ -95,60 +118,91 @@
#
def peers
@peers.clone
end
+ #
+ # Wait for the call to end. Returns immediately if the call has already ended, else blocks until it does so.
+ # @return [Symbol] the reason for the call ending
+ #
+ def wait_for_end
+ if end_reason
+ end_reason
+ else
+ @end_blocker.wait
+ end
+ end
+
def register_event_handler(*guards, &block)
register_handler :event, *guards, &block
end
def deliver_message(message)
logger.debug "Receiving message: #{message.inspect}"
catching_standard_errors { trigger_handler :event, message }
end
alias << deliver_message
+ def commands
+ @commands.clone
+ end
+
# @private
def register_initial_handlers
register_event_handler Punchblock::Event::Offer do |offer|
@offer = offer
@client = offer.client
+ @start_time = Time.now
throw :pass
end
register_event_handler Punchblock::HasHeaders do |event|
- variables.merge! event.headers_hash
+ merge_headers event.headers
throw :pass
end
on_joined do |event|
- target = event.call_id || event.mixer_name
+ target = event.call_uri || event.mixer_name
@peers[target] = Adhearsion.active_calls[target]
signal :joined, target
end
on_unjoined do |event|
- target = event.call_id || event.mixer_name
+ target = event.call_uri || event.mixer_name
@peers.delete target
signal :unjoined, target
end
on_end do |event|
- logger.info "Call ended"
+ logger.info "Call ended due to #{event.reason}"
+ @end_time = Time.now
+ @duration = @end_time - @start_time if @start_time
clear_from_active_calls
@end_reason = event.reason
- commands.terminate
+ @end_blocker.broadcast event.reason
+ @commands.terminate
after(Adhearsion.config.platform.after_hangup_lifetime) { terminate }
throw :pass
end
end
+ # @return [Float] The call duration until the current time, or until the call was disconnected, whichever is earlier
+ def duration
+ if @duration
+ @duration
+ elsif @start_time
+ Time.now - @start_time
+ else
+ 0.0
+ end
+ end
+
##
# Registers a callback for when this call is joined to another call or a mixer
#
# @param [Call, String, Hash, nil] target the target to guard on. May be a Call object, a call ID (String, Hash) or a mixer name (Hash)
- # @option target [String] call_id The call ID to guard on
+ # @option target [String] call_uri The call ID to guard on
# @option target [String] mixer_name The mixer name to guard on
#
def on_joined(target = nil, &block)
register_event_handler Punchblock::Event::Joined, *guards_for_target(target) do |event|
block.call event
@@ -158,11 +212,11 @@
##
# Registers a callback for when this call is unjoined from another call or a mixer
#
# @param [Call, String, Hash, nil] target the target to guard on. May be a Call object, a call ID (String, Hash) or a mixer name (Hash)
- # @option target [String] call_id The call ID to guard on
+ # @option target [String] call_uri The call ID to guard on
# @option target [String] mixer_name The mixer name to guard on
#
def on_unjoined(target = nil, &block)
register_event_handler Punchblock::Event::Unjoined, *guards_for_target(target) do |event|
block.call event
@@ -216,11 +270,11 @@
##
# Joins this call to another call or a mixer
#
# @param [Call, String, Hash] target the target to join to. May be a Call object, a call ID (String, Hash) or a mixer name (Hash)
- # @option target [String] call_id The call ID to join to
+ # @option target [String] call_uri The call ID to join to
# @option target [String] mixer_name The mixer to join to
# @param [Hash, Optional] options further options to be joined with
#
def join(target, options = {})
command = Punchblock::Command::Join.new options.merge(join_options_with_target(target))
@@ -229,11 +283,11 @@
##
# Unjoins this call from another call or a mixer
#
# @param [Call, String, Hash] target the target to unjoin from. May be a Call object, a call ID (String, Hash) or a mixer name (Hash)
- # @option target [String] call_id The call ID to unjoin from
+ # @option target [String] call_uri The call ID to unjoin from
# @option target [String] mixer_name The mixer to unjoin from
#
def unjoin(target)
command = Punchblock::Command::Unjoin.new join_options_with_target(target)
write_and_await_response command
@@ -241,15 +295,15 @@
# @private
def join_options_with_target(target)
case target
when Call
- { :call_id => target.id }
+ { :call_uri => target.uri }
when String
- { :call_id => target }
+ { :call_uri => "#{transport}:#{target}@#{domain}" }
when Hash
- abort ArgumentError.new "You cannot specify both a call ID and mixer name" if target.has_key?(:call_id) && target.has_key?(:mixer_name)
+ abort ArgumentError.new "You cannot specify both a call URI and mixer name" if target.has_key?(:call_uri) && target.has_key?(:mixer_name)
target
else
abort ArgumentError.new "Don't know how to join to #{target.inspect}"
end
end
@@ -276,14 +330,15 @@
write_and_await_response Punchblock::Command::Unmute.new
end
# @private
def write_and_await_response(command, timeout = 60)
- commands << command
+ @commands << command
write_command command
- case (response = command.response timeout)
+ response = defer { command.response timeout }
+ case response
when Punchblock::ProtocolError
if response.name == :item_not_found
abort Hangup.new(@end_reason)
else
abort response
@@ -293,36 +348,37 @@
end
command
rescue Timeout::Error
abort CommandTimeout.new(command.to_s)
+ ensure
+ @commands.delete command
end
# @private
def write_command(command)
abort Hangup.new(@end_reason) unless active? || command.is_a?(Punchblock::Command::Hangup)
- variables.merge! command.headers_hash if command.respond_to? :headers_hash
+ merge_headers command.headers if command.respond_to? :headers
logger.debug "Executing command #{command.inspect}"
- client.execute_command command, :call_id => id, :async => true
+ client.execute_command command, call_id: id, domain: domain, async: true
end
# @private
def logger_id
- "#{self.class}: #{id}"
+ "#{self.class}: #{id}@#{domain}"
end
-
# @private
def to_ary
[current_actor]
end
# @private
def inspect
attrs = [:offer, :end_reason, :commands, :variables, :controllers, :to, :from].map do |attr|
"#{attr}=#{send(attr).inspect}"
end
- "#<#{self.class}:#{id} #{attrs.join ', '}>"
+ "#<#{self.class}:#{id}@#{domain} #{attrs.join ', '}>"
end
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
@@ -353,11 +409,25 @@
def client
@client
end
+ def transport
+ offer.transport if offer
+ end
+
+ def merge_headers(headers)
+ headers.each do |name, value|
+ variables[name.to_s.downcase.gsub('-', '_')] = value
+ end
+ end
+
+ def finalize
+ ::Logging::Repository.instance.delete logger_id
+ end
+
# @private
- class CommandRegistry < ThreadSafeArray
+ class CommandRegistry < Array
def terminate
hangup = Hangup.new
each { |command| command.response = hangup if command.requested? }
end
end