Selectively Receiving incoming calls from Asterisk Queues while dialing out
TweetUse case: Having simultaneous inbound and outbound calls with Asterisk Queues
One of our clients has a call center with a particular business logic. Their agents can place calls and they can also take calls that come from an Asterisk Queue. Nothing new there, but there are some corner cases that are very important:
- When an agent is free (i.e: no outbound/inbound calls), they can either place calls or receive incoming calls from the queue.
- When an agent places a call, they can still receive incoming calls, but ONLY if the outbound call hasn't been answered yet (i.e: RINGING state).
- If an agent receives a call from the queue while his/her placed call is ringing, this outbound call MUST be hangup automatically.
- If an agent places a call and this call is answered, they can't receive inbound calls from the queue until the other call finishes.
In this article we're going to see how to fulfill these particular requirements right from the Asterisk Dialplan with no black magic at all.
When an agent is free (i.e: no outbound/inbound calls), they can either place calls or receive incoming calls from the queue
This one doesn't need anything special other than setup the queue and agents as a queue member as usual. We're good to go.
When an agent places a call, they can still receive incoming calls, but ONLY if the outbound call hasn't been answered yet (i.e: RINGING state)
So let's say that when an agent places call, the extension is handled in a Context defined in a specific part of the Asterisk Dialplan and then the Dial Application is used to actually tell asterisk to create the outbound call.
This is a standard procedure and while your mileage may vary, your dialplan would look pretty much like this:
So the objective here is to somehow manipulate this dial command and block incoming calls (from a queue) to this agent only when the outbound call is answered, but not while the call is ringing.
Blocking or allowing calls to an agent that is a member of a queue can be done in Asterisk by using the dialplan commands:
- PauseQueueMember (that will temporarily disable all calls from queues that an agent is member of).
- UnPauseQueueMember (that will restore the original behavior and allow incoming calls to the queue member).
So that seems to be easy enough. What about knowing when to exactly pause and unpause members? How can we actually detect that a call is in the ringing or answer state?
Luckily for us, we can use Asterisk Pre Dial Handlers or Hooks for the Dial Application, more specifically the U option, that issues a GOSUB to a specific context right before bridging the channels (i.e: the call is answered, and we get to execute a custom dialplan code before the channels get connected to each other):
U - Execute via Gosub the routine x for the called channel before connecting to the calling channel. Arguments can be specified to the Gosub using ^ as a delimiter. The Gosub routine can set the variable GOSUB_RESULT to specify the following actions after the Gosub returns. GOSUB_RESULT ABORT - Hangup both legs of the call. CONGESTION - Behave as if line congestion was encountered. BUSY - Behave as if a busy signal was encountered. CONTINUE - Hangup the called party and allow the calling party to continue dialplan execution at the next priority. GOTO:[[^] ^] - Transfer the call to the specified destination. x - Name of the subroutine to execute via Gosub arg - Arguments for the Gosub routine
Aha! With this option we can call PauseQueueMember right before bridging the channels (at this point we know for sure that the call is going to be answered, and assume it was ringing for the whole time before that happens).
We can then call UnPauseQueueMember after the Dial Application ends, and at this point we know for sure that the call ended. The resulting dialplan should look like this:
As you can see, we added the Dial U option to the Dial application so we can execute the SETBUSY context. And we also have set the variable agent right before issuing the Dial. The leading underscore (i.e: _agent) tells Asterisk to actually pass that variable to all subchannels created to serve this request (channel variable inheritance).
So when the Dial is executed, and right before bridging the calls, SETBUSY is executed and will call PauseQueueMember for that agent, and then return (effectively forbidding all incoming calls from queues to this agent).
And after the call finishes, the special Asterisk h extension gets executed, and this will call UnPauseQueueMember (allowing again all incoming calls from queues to this agent).
NOTE: Your member interface string will probably differ from the one in the example, so you should adjust that to match your own setup.
Great! Neeeeeeext! :)
If an agent receives a call from the queue while his/her placed call is ringing, this outbound call MUST be hangup automatically
Interesting. For this to work, we should somehow save the outgoing channel somewhere and issue a hangup on it if we get an inbound call while that channel is still unbridged (i.e: the outbound call hasn't been answered yet).
The Asterisk Pre Dial Handlers come into the rescue one more time, with the B option:
B - Before initiating the outgoing call(s), Gosub to the specified location using the current channel.
By using the B option of the Dial Application in Asterisk we can execute a context in the dialplan before the call is actually placed but as soon as the outbound channel name is known, that is so cool!
On the other hand, to hangup a channel we can use the dialplan command SoftHangup.
Let's give all of this a try by changing our dialplan like this:
As you can see, we added the option B to our Dial command so we can execute the new context SETRINGING, this will set a global variable in the Asterisk dialplan with the name of the new outbound channel created. Note that the call hasn't actually been made yet. The name of the variable would be for example RINGING_CHANNEL_106.
Since these guys used FOP2 (Flash Operator Panel) to setup their queues and agents, they already had a context where all calls from a queue where being handled, this is mimicked by the queue_context context, where we added the call to the SoftHangup command right before calling the agent. Neat, uh?
If an agent places a call and this call is answered, they can't receive inbound calls from the queue until the other call finishes
And this is already solved by calling the PauseQueueMember/UnPauseQueueMember as we stated before in the right places :)
Conclusion
That's it! We have learned how to use Pre Dial Handlers or hooks, and also how to create our own post dial handlers in the Asterisk dialplan to handle specific use cases for call centers. Enjoy!