Class: ElectricSlide::CallQueue
- Inherits:
-
Object
- Object
- ElectricSlide::CallQueue
- Includes:
- Celluloid
- Defined in:
- lib/electric_slide/call_queue.rb
Defined Under Namespace
Classes: InvalidConnectionType, InvalidRequeueMethod
Constant Summary collapse
- ENDED_CALL_EXCEPTIONS =
[ Adhearsion::Call::Hangup, Adhearsion::Call::ExpiredError, Adhearsion::Call::CommandTimeout, Celluloid::DeadActorError, Punchblock::ProtocolError ]
- CONNECTION_TYPES =
[ :call, :bridge, ].freeze
- AGENT_RETURN_METHODS =
[ :auto, :manual, ].freeze
- Error =
Class.new(StandardError)
- MissingAgentError =
Class.new(Error)
- DuplicateAgentError =
Class.new(Error)
Instance Attribute Summary collapse
-
#agent_return_method ⇒ Object
Returns the value of attribute agent_return_method.
-
#agent_strategy ⇒ Object
Returns the value of attribute agent_strategy.
-
#connection_type ⇒ Object
Returns the value of attribute connection_type.
Class Method Summary collapse
- .valid_agent_return_method?(agent_return_method) ⇒ Boolean
- .valid_connection_type?(connection_type) ⇒ Boolean
- .valid_with?(attrs = {}) ⇒ Boolean
Instance Method Summary collapse
-
#add_agent(agent) ⇒ Object
Registers an agent to the queue.
-
#agent_available? ⇒ Boolean
Checks whether an agent is available to take a call.
-
#available_agent_summary ⇒ Hash
Returns information about the number of available agents The data returned depends on the AgentStrategy in use.
-
#call_waiting? ⇒ Boolean
Checks whether any callers are waiting.
-
#calls_waiting ⇒ Fixnum
Returns the number of callers waiting in the queue.
-
#check_for_connections ⇒ Object
Checks to see if any callers are waiting for an agent and attempts to connect them to an available agent.
-
#checkout_agent ⇒ Agent
Assigns the first available agent, marking the agent :on_call.
- #conditionally_return_agent(agent, return_method = @agent_return_method) ⇒ Object
-
#connect(agent, queued_call) ⇒ Object
Connect an Agent to a caller.
-
#enqueue(call) ⇒ Object
Add a call to the end of the queue, the normal FIFO queue behavior.
-
#get_agent(id) ⇒ Agent, Nil
Finds an agent known to the queue by that agent’s ID.
-
#get_agents ⇒ Array
Returns a copy of the set of agents that are known to the queue.
-
#get_next_caller ⇒ Adhearsion::Call
Returns the next waiting caller.
-
#get_queued_calls ⇒ Array
Returns a copy of the set of calls waiting to be answered that are known to the queue.
-
#initialize(opts = {}) ⇒ CallQueue
constructor
A new instance of CallQueue.
-
#priority_enqueue(call) ⇒ Object
Add a call to the head of the queue.
-
#remove_agent(agent, extra_params = {}) ⇒ Agent, Nil
Removes an agent from the queue entirely.
-
#remove_call(call) ⇒ Object
Remove a waiting call from the queue.
-
#return_agent(agent, new_presence = :available, address = nil) ⇒ Object
Marks an agent as available to take a call.
-
#return_agent!(*args) ⇒ Object
Marks an agent as available to take a call.
- #update(attrs) ⇒ Object
-
#update_agent(agent, agent_attrs) ⇒ Object
Updates a queued agent’s attributes.
Constructor Details
#initialize(opts = {}) ⇒ CallQueue
Returns a new instance of CallQueue.
75 76 77 78 79 80 81 82 83 84 |
# File 'lib/electric_slide/call_queue.rb', line 75 def initialize(opts = {}) @agents = [] # Needed to keep track of global list of agents @queue = [] # Calls waiting for an agent update( agent_strategy: opts[:agent_strategy] || AgentStrategy::LongestIdle, connection_type: opts[:connection_type] || :call, agent_return_method: opts[:agent_return_method] || :auto ) end |
Instance Attribute Details
#agent_return_method ⇒ Object
Returns the value of attribute agent_return_method.
45 46 47 |
# File 'lib/electric_slide/call_queue.rb', line 45 def agent_return_method @agent_return_method end |
#agent_strategy ⇒ Object
Returns the value of attribute agent_strategy.
45 46 47 |
# File 'lib/electric_slide/call_queue.rb', line 45 def agent_strategy @agent_strategy end |
#connection_type ⇒ Object
Returns the value of attribute connection_type.
45 46 47 |
# File 'lib/electric_slide/call_queue.rb', line 45 def connection_type @connection_type end |
Class Method Details
.valid_agent_return_method?(agent_return_method) ⇒ Boolean
71 72 73 |
# File 'lib/electric_slide/call_queue.rb', line 71 def self.valid_agent_return_method?(agent_return_method) AGENT_RETURN_METHODS.include? agent_return_method end |
.valid_connection_type?(connection_type) ⇒ Boolean
67 68 69 |
# File 'lib/electric_slide/call_queue.rb', line 67 def self.valid_connection_type?(connection_type) CONNECTION_TYPES.include? connection_type end |
.valid_with?(attrs = {}) ⇒ Boolean
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/electric_slide/call_queue.rb', line 47 def self.valid_with?(attrs = {}) return false unless Hash === attrs if agent_strategy = attrs[:agent_strategy] begin agent_strategy.new rescue Exception return false end end if connection_type = attrs[:connection_type] return false unless valid_connection_type? connection_type end if agent_return_method = attrs[:agent_return_method] return false unless valid_agent_return_method? agent_return_method end true end |
Instance Method Details
#add_agent(agent) ⇒ Object
Registers an agent to the queue
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/electric_slide/call_queue.rb', line 163 def add_agent(agent) abort ArgumentError.new("#add_agent called with nil object") if agent.nil? abort DuplicateAgentError.new("Agent is already in the queue") if get_agent(agent.id) agent.queue = current_actor accept_agent! agent logger.info "Adding agent #{agent} to the queue" @agents << agent @strategy << agent if agent.presence == :available # Fake the presence callback since this is a new agent agent.callback :presence_change, current_actor, agent.call, agent.presence, :unavailable async.check_for_connections end |
#agent_available? ⇒ Boolean
Checks whether an agent is available to take a call
116 117 118 |
# File 'lib/electric_slide/call_queue.rb', line 116 def agent_available? @strategy.agent_available? end |
#available_agent_summary ⇒ Hash
Returns information about the number of available agents The data returned depends on the AgentStrategy in use. The data will always include a :total count of the agents available
124 125 126 127 |
# File 'lib/electric_slide/call_queue.rb', line 124 def available_agent_summary # TODO: Make this a delegator? @strategy.available_agent_summary end |
#call_waiting? ⇒ Boolean
Checks whether any callers are waiting
343 344 345 |
# File 'lib/electric_slide/call_queue.rb', line 343 def call_waiting? @queue.length > 0 end |
#calls_waiting ⇒ Fixnum
Returns the number of callers waiting in the queue
349 350 351 |
# File 'lib/electric_slide/call_queue.rb', line 349 def calls_waiting @queue.length end |
#check_for_connections ⇒ Object
Checks to see if any callers are waiting for an agent and attempts to connect them to an available agent
243 244 245 |
# File 'lib/electric_slide/call_queue.rb', line 243 def check_for_connections connect checkout_agent, get_next_caller while call_waiting? && agent_available? end |
#checkout_agent ⇒ Agent
Assigns the first available agent, marking the agent :on_call
131 132 133 134 135 136 137 |
# File 'lib/electric_slide/call_queue.rb', line 131 def checkout_agent agent = @strategy.checkout_agent if agent agent.update_presence(:on_call) end agent end |
#conditionally_return_agent(agent, return_method = @agent_return_method) ⇒ Object
323 324 325 326 327 328 329 330 331 332 333 |
# File 'lib/electric_slide/call_queue.rb', line 323 def conditionally_return_agent(agent, return_method = @agent_return_method) raise ArgumentError, "Invalid requeue method; must be one of #{AGENT_RETURN_METHODS.join ','}" unless AGENT_RETURN_METHODS.include? return_method if agent && @agents.include?(agent) && agent.on_call? && return_method == :auto logger.info "Returning agent #{agent.id} to queue" return_agent agent else logger.debug "Not returning agent #{agent.inspect} to the queue" return_agent agent, :after_call end end |
#connect(agent, queued_call) ⇒ Object
Connect an Agent to a caller
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/electric_slide/call_queue.rb', line 290 def connect(agent, queued_call) unless queued_call && queued_call.active? logger.warn "Inactive queued call found in #connect" return_agent agent return end queued_call[:agent] = agent logger.info "Connecting #{agent} with #{remote_party queued_call}" case @connection_type when :call call_agent agent, queued_call when :bridge bridge_agent agent, queued_call end rescue *ENDED_CALL_EXCEPTIONS ignoring_ended_calls do if queued_call && queued_call.active? logger.warn "Dead call exception in #connect but queued_call still alive, reinserting into queue" priority_enqueue queued_call end end ignoring_ended_calls do if agent.call && agent.call.active? logger.warn "Dead call exception in #connect but agent call still alive, reinserting into queue" agent.callback :connection_failed, current_actor, agent.call, queued_call return_agent agent end end end |
#enqueue(call) ⇒ Object
Add a call to the end of the queue, the normal FIFO queue behavior
265 266 267 268 269 270 271 272 273 274 |
# File 'lib/electric_slide/call_queue.rb', line 265 def enqueue(call) ignoring_ended_calls do logger.info "Adding call from #{remote_party call} to the queue" call[:electric_slide_enqueued_at] = DateTime.now call.on_end { remove_call call } @queue << call unless @queue.include? call check_for_connections end end |
#get_agent(id) ⇒ Agent, Nil
Finds an agent known to the queue by that agent’s ID
154 155 156 |
# File 'lib/electric_slide/call_queue.rb', line 154 def get_agent(id) @agents.detect { |agent| agent.id == id } end |
#get_agents ⇒ Array
Returns a copy of the set of agents that are known to the queue
141 142 143 |
# File 'lib/electric_slide/call_queue.rb', line 141 def get_agents @agents.dup end |
#get_next_caller ⇒ Adhearsion::Call
Returns the next waiting caller
337 338 339 |
# File 'lib/electric_slide/call_queue.rb', line 337 def get_next_caller @queue.shift end |
#get_queued_calls ⇒ Array
Returns a copy of the set of calls waiting to be answered that are known to the queue
147 148 149 |
# File 'lib/electric_slide/call_queue.rb', line 147 def get_queued_calls @queue.dup end |
#priority_enqueue(call) ⇒ Object
Add a call to the head of the queue. Among other reasons, this is used when a caller is sent to an agent, but the connection fails because the agent is not available.
250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/electric_slide/call_queue.rb', line 250 def priority_enqueue(call) # In case this is a re-insert on agent failure... # ... reset `:agent` call variable call[:agent] = nil # ... set, but don't reset, the enqueue time call[:electric_slide_enqueued_at] ||= DateTime.now call.on_end { remove_call call } @queue.unshift call check_for_connections end |
#remove_agent(agent, extra_params = {}) ⇒ Agent, Nil
Removes an agent from the queue entirely
233 234 235 236 237 238 239 |
# File 'lib/electric_slide/call_queue.rb', line 233 def remove_agent(agent, extra_params = {}) agent.update_presence(:unavailable, extra_params) @strategy.delete agent @agents.delete agent logger.info "Removing agent #{agent} from the queue" rescue Adhearsion::Call::ExpiredError end |
#remove_call(call) ⇒ Object
Remove a waiting call from the queue. Used if the caller hangs up or is otherwise removed.
278 279 280 281 282 283 284 285 |
# File 'lib/electric_slide/call_queue.rb', line 278 def remove_call(call) ignoring_ended_calls do unless call[:electric_slide_connected_at] logger.info "Caller #{remote_party call} has abandoned the queue" end end @queue.delete call end |
#return_agent(agent, new_presence = :available, address = nil) ⇒ Object
Marks an agent as available to take a call. To be called after an agent completes a call and is ready to take the next call.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'lib/electric_slide/call_queue.rb', line 202 def return_agent(agent, new_presence = :available, address = nil) logger.debug "Returning #{agent} to the queue" return false unless get_agent(agent.id) agent.update_presence(new_presence) agent.address = address if address case agent.presence when :available bridged_agent_health_check agent @strategy << agent check_for_connections when :unavailable @strategy.delete agent end agent end |
#return_agent!(*args) ⇒ Object
Marks an agent as available to take a call.
225 226 227 |
# File 'lib/electric_slide/call_queue.rb', line 225 def return_agent!(*args) return_agent(*args) || abort(MissingAgentError.new('Agent is not in the queue. Unable to return agent.')) end |
#update(attrs) ⇒ Object
86 87 88 89 90 91 |
# File 'lib/electric_slide/call_queue.rb', line 86 def update(attrs) attrs.each do |attr, value| setter = "#{attr}=" send setter, value if respond_to?(setter) end unless attrs.nil? end |
#update_agent(agent, agent_attrs) ⇒ Object
Updates a queued agent’s attributes
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/electric_slide/call_queue.rb', line 180 def update_agent(agent, agent_attrs) abort ArgumentError.new('Agent must not be `nil`') unless agent unless get_agent(agent.id) abort MissingAgentError.new('Agent is not in the queue') end # check if the agent is allowed to have the given set of attributes using # a dupe, to preserve the state of the original in case of failure agent.dup.tap do |double_agent| double_agent.update agent_attrs accept_agent! double_agent end agent.update agent_attrs return_agent agent, agent.presence end |