In this section we’ll take a look at some of the finer-grained queue controls, such as options for controlling announcements and when callers should be placed into (or removed from) the queue. We’ll also look at penalties and priorities, exploring how we can control the agents in our queue by giving preference to a pool of agents to answer the call and increase that pool dynamically based on the wait times in the queue. Finally, we’ll look at using Local channels as queue members, which gives us the ability to perform dialplan functionality prior to connecting the caller to an agent.
Sometimes you need to add people to a queue at a higher priority than that given to other callers. Perhaps the caller has already spent time waiting in a queue, and an agent has taken some information but realized the caller needed to be transferred to another queue. In this case, to minimize the caller’s overall wait time, it might be desirable to transfer the call to a priority queue that has a higher weight (and thus a higher preference), so it will be answered quickly.
Setting a higher priority on a queue is done
with the weight
option. If you have two queues with differing weights (e.g.,
support
and support-priority
), agents assigned to both
queues will be passed calls from the higher-priority queue in preference
to calls from the lower-priority queue. Those agents will not take any
calls from the lower-priority queue until the higher-priority queue is
cleared. (Normally, there will be some agents who are assigned only to
the lower-priority queue, to ensure that those calls are dealt with in a
timely manner.) For example, if we place queue member James Shaw into
both the support
and support-priority
queues, callers in the
support-priority
queue will have a
preferred standing with James over callers in the support
queue.
Let’s take a look at how we could make this
work. First, we need to create two queues that are identical except for
the weight
option. We can use a
template for this to ensure that the two queues remain identical if
anything should need to change in the future:
[support_template](!) musicclass=default strategy=rrmemory joinempty=no leavewhenempty=yes ringinuse=no [support](support_template) weight=0 [support-priority](support_template) weight=10
With our queues configured (and subsequently
reloaded using module reload
app_queue.so from the Asterisk console), we can now create two
extensions to transfer callers to. This can be done wherever you would
normally place your dialplan logic to perform transfers. We’re going to
use the LocalSets
context, which we’ve previously enabled as the starting context
for our devices:
[LocalSets] include => Queue ; allow direct transfer of calls to queues [Queues] exten => 7000,1,Verbose(2,Entering the support queue) same => n,Queue(support) ; standard support queue available ; at extension 7000 same => n,VoiceMail(7000@queues,u) ; if there are no members in the queue, ; we exit and send the caller to voicemail same => n,Hangup() exten => 8000,1,Verbose(2,Entering the priority support queue) same => n,Queue(support-priority) ; priority queue available at ; extension 8000 same => n,VoiceMail(7000@queues,u) ; if there are no members in the queue, ; we exit and send the caller to voicemail same => n,Hangup()
There you have it: two queues defined with
different weights. We’ve configured our standard queues to start at
extension 7000
, and our priority
queues to start at 8000
. We can
mirror this for several queues by simply matching between the 7XXX
and 8XXX
ranges. So, for example, if we have our
sales
queue at extension 7004
, our priority-sales
queue (for returning customers,
perhaps?) could be placed in the mirrored queue at 8004
, which has a higher weight.
The only other configuration left to do is to
make sure some or all of your queue members are placed in both queues.
If you have more callers in your 7XXXX
queues, you may want to have more queue
members logged into that queue, with a percentage of your queue members
logged into both queues. Exactly how you wish to configure your queues
will depend on your local policy and circumstances.
Within a queue, we can penalize
members in order to lower their preference for being called when
there are people waiting in a particular queue. For example, we may
penalize queue members when we want them to be a member of a queue, but
to be used only when the queue gets full enough that all our preferred
agents are unavailable. This means we can have three queues (say,
support
, sales
, and billing
), each containing the same three queue
members: James Shaw, Kay Madsen, and Danielle Roberts.
Suppose, however, that we want James Shaw to
be the preferred contact in the support
queue, Kay Madsen preferred in
sales
, and Danielle Roberts preferred
in billing
. By
penalizing Kay Madsen and Danielle Roberts in support
, we ensure that James Shaw will be the
preferred queue member called. Similarly, we can penalize James Shaw and
Danielle Roberts in the sales
queue
so Kay Madsen is preferred, and penalize James Shaw and Kay Madsen in
the billing
queue so Danielle Roberts
is preferred.
Penalizing queue members can be done either in
the queues.conf
file, if you’re
specifying queue members statically, or through the AddQueueMember()
dialplan application. Let’s
look at how our queues would be set up with static members in queues.conf
. We’ll be using the StandardQueue
template we defined earlier in
this chapter:
[support](StandardQueue) member => SIP/0000FFFF0001,0,James Shaw ; preferred member => SIP/0000FFFF0002,10,Kay Madsen ; second preferred member => SIP/0000FFFF0003,20,Danielle Roberts ; least preferred [sales](StandardQueue) member => SIP/0000FFFF0002,0,Kay Madsen member => SIP/0000FFFF0003,10,Danielle Roberts member => SIP/0000FFFF0001,20,James Shaw [billing](StandardQueue) member => SIP/0000FFFF0003,0,Danielle Roberts member => SIP/0000FFFF0001,10,James Shaw member => SIP/0000FFFF0002,20,Kay Madsen
By defining different penalties for each
member of the queue, we can help control the preference for where
callers are delivered, but still ensure that other queue members will be
available to answer calls if the preferred member is unavailable.
Penalties can also be defined using AddQueueMember()
, as the following example
demonstrates:
exten => *54,1,Verbose(2,Logging In Queue Member) same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)}) ; *CLI> database put queue support/0000FFFF0001/penalty 0 same => n,Set(QueuePenalty=${DB(queue/support/${CHANNEL(peername)}/penalty)}) ; *CLI> database put queue support/0000FFFF0001/membername "James Shaw" same => n,Set(MemberName=${DB(queue/support/${CHANNEL(peername)}/membername)}) ; AddQueueMember(queuename[,interface[,penalty[,options[,membername ; [,stateinterface]]]]]) same => n,AddQueueMember(support,${MemberChannel},${QueuePenalty},,${MemberName})
Using AddQueueMember()
, we’ve shown how you could retrieve the penalty
associated with a given member name for a particular queue and assign
that value to the member when she logs into the queue. Some additional
abstraction would need to be done to make this work for multiple queues;
for more information see the section called “Automatically Logging Into and Out of Multiple Queues”.
Using the queuerules.conf
file, it is possible to
specify rules to change the values of the QUEUE_MIN_PENALTY
and QUEUE_MAX_PENALTY
channel variables. The QUEUE_MIN_PENALTY
and QUEUE_MAX_PENALTY
channel variables are used
to control which members of a queue are to be used for servicing
callers. Let’s say we have a queue called support
, and we have five queue members with
various penalties ranging from 1
through 5
. If prior to a caller
entering the queue the QUEUE_MIN_PENALTY
channel variable is set to a
value of 2
and the QUEUE_MAX_PENALTY
is set to a value of
4
, only queue members whose penalties
are set to values ranging from 2
through 4
will be considered
available to answer that call:
[Queues] exten => 7000,1,Verbose(2,Entering the support queue) same => n,Set(QUEUE_MIN_PENALTY=2) ; set minimum queue member penalty to be used same => n,Set(QUEUE_MAX_PENALTY=4) ; set maximum queue member penalty we'll use same => n,Queue(support) ; entering the queue with minimum and maximum ; member penalties to be used
What’s more, during the caller’s stay in the
queue, we can dynamically change the values of QUEUE_MIN_PENALTY
and QUEUE_MAX_PENALTY
for that caller. This allows
either more or a different set of queue members to be used, depending on
how long the caller waits in the queue. For instance, in the previous
example, we could modify the minimum penalty to 1
and the maximum penalty to 5
if the caller has to wait more than 60
seconds in the queue.
The rules are defined using the queuerules.conf
file. Multiple rules can be
created in order to facilitate different penalty changes throughout the
call. Let’s take a look at how we’d define the changes described in the
previous paragraph:
[more_members] penaltychange => 60,5,1
If you make changes to the queuerules.conf
file and reload app_queue.so
, the new rules will affect
only new callers in the queue, not existing callers.
We’ve defined the rule more_members
in queuerules.conf
and passed the following
values to penaltychange:
60
is the number of seconds to wait before
changing the penalty values, 5
is the
new QUEUE_MAX_PENALTY
, and 1
is the new QUEUE_MIN_PENALTY
. With our new rule defined,
we must reload app_queue.so
to make
it available to us for use:
*CLI> module reload app_queue.so
-- Reloading module 'app_queue.so' (True Call Queueing)
== Parsing '/etc/asterisk/queuerules.conf': == Found
We can also verify our rules at the console with queue show rules:
*CLI> queue show rules
Rule: more_members
After 60 seconds, adjust QUEUE_MAX_PENALTY to 5 and adjust QUEUE_MIN_PENALTY to 1
With our rule now loaded into memory, we can modify our dialplan
to make use of it. Just modify the Queue()
line to include the new rule, like
so:
[Queues]
exten => 7000,1,Verbose(2,Entering the support queue)
same => n,Set(QUEUE_MIN_PENALTY=2) ; set minimum queue member penalty
same => n,Set(QUEUE_MAX_PENALTY=4) ; set maximum queue member penalty
; Queue(queuename[,options[,URL[,announceoverride[,timeout[,AGI[,macro
; [,gosub[,rule[,position]]]]]]]]])
same => n,Queue(support,,,,,,,,more_members
) ; entering queue with minimum and
; maximum member penalties
The queuerules.conf
file is quite flexible. We
can define our rule using relative instead of absolute penalty values,
and we can define multiple rules:
[more_members] penaltychange => 30,+1 penaltychange => 45,,-1 penaltychange => 60,+1 penaltychange => 120,+2
Here, we’ve modified our more_members
rule to use relative values.
After 30 seconds, we increase the maximum penalty by 1
(which would take us to 5
using our sample dialplan). After 45 seconds, we
decrease the minimum penalty by 1
,
and so on. We can verify our new rule changes after a module reload app_queue.so at the Asterisk console:
*CLI> queue show rules
Rule: more_members
After 30 seconds, adjust QUEUE_MAX_PENALTY by 1 and adjust QUEUE_MIN_PENALTY by 0
After 45 seconds, adjust QUEUE_MAX_PENALTY by 0 and adjust QUEUE_MIN_PENALTY by -1
After 60 seconds, adjust QUEUE_MAX_PENALTY by 1 and adjust QUEUE_MIN_PENALTY by 0
After 120 seconds, adjust QUEUE_MAX_PENALTY by 2 and adjust QUEUE_MIN_PENALTY by 0
Asterisk has the ability to play several announcements to callers waiting in the queue. For example, you might want to announce the caller’s position in the queue, the average wait time, or make periodic announcements thanking your callers for waiting (or whatever your audio files say). It’s important to tune the values that control when these announcements are played to the callers, because announcing their position, thanking them for waiting, and telling them the average hold time too often may annoy them, causing them to either hang up or take it out on your agents.
There are several options in the queues.conf
file that you can use to
fine-tune what and when announcements are played to your callers. The
full list of queue options is available in the section called “The queues.conf File”, but we’ll review the relevant ones
here.
Table 13.5, “Options related to prompt control timing within a queue” lists the options you can use to control when announcements are played to the caller.
Table 13.5. Options related to prompt control timing within a queue
Table 13.6, “Options for controlling the playback of prompts within a queue” shows what files will be used when announcements are played to the caller.
Table 13.6. Options for controlling the playback of prompts within a queue
If the number of options devoted to playing announcements to callers is any indication of their importance, it’s probably in our best interest to use them to their fullest potential. The options in Table 13.5, “Options related to prompt control timing within a queue” help us define when we’ll play announcements to callers, and the options in Table 13.6, “Options for controlling the playback of prompts within a queue” help us control what we play to our callers. With those tables in hand, let’s take a look at an example queue where we’ve defined some values. We’ll use our basic queue template as a starting point:
[general] autofill=yes ; distribute all waiting callers to available members shared_lastcall=yes ; respect the wrapup time for members logged into more ; than one queue [StandardQueue](!) ; template to provide common features musicclass=default ; play [default] music strategy=rrmemory ; use the Round Robin Memory strategy joinempty=yes ; do not join the queue when no members available leavewhenempty=no ; leave the queue when no members available ringinuse=no ; don't ring members when already InUse (prevents ; multiple calls to an agent) [sales](StandardQueue) ; create the sales queue using the parameters in the ; StandardQueue template [support](StandardQueue) ; create the support queue using the parameters in the ; StandardQueue template
We’ll now modify the StardardQueue
template to control our
announcements:
[StandardQueue](!) ; template to provide common features musicclass=default ; play [default] music strategy=rrmemory ; use the Round Robin Memory strategy joinempty=yes ; do not join the queue when no members available leavewhenempty=no ; leave the queue when no members available ringinuse=no ; don't ring members when already InUse (prevents ; multiple calls to an agent) ; -------- Announcement Control -------- announce-frequency=30 ; announces caller's hold time and position every 30 ; seconds min-announce-frequency=30 ; minimum amount of time that must pass before the ; caller's position is announced periodic-announce-frequency=45 ; defines how often to play a periodic announcement to ; caller random-periodic-announce=no ; defines whether to play periodic announcements in ; a random order, or serially relative-periodic-announce=yes ; defines whether the timer starts at the end of ; file playback (yes) or the beginning (no) announce-holdtime=once ; defines whether the estimated hold time should be ; played along with the periodic announcement announce-position=limit ; defines if we should announce the caller's position ; in the queue announce-position-limit=10 ; defines the limit value where we announce the ; caller's position (when announce-position is set to ; limit or more) announce-round-seconds=30 ; rounds the hold time announcement to the nearest ; 30-second value
Let’s describe what we’ve just set in our
StandardQueue
template.
We’ll announce the caller’s hold time and
position every 30 seconds (announce
-
frequency
),[129] and make sure the minimum amount of time that passes
before we announce it again is at least 30 seconds (min-announce-frequency
). We do this to limit
how often our announcements are played to the callers, in order to avoid
the updates becoming annoying. Periodically, we’ll play an announcement
to the callers that thanks them for holding and assures them that an
agent will be with them shortly. (The announcement is defined by the
periodic-announcement
setting. We’re
using the default announcement, but you can define one or more
announcements yourself using periodic
-
announce
.)
These periodic announcements will be played
every 45 seconds (periodic-announce-frequency
), in the order
they were defined (random-period-announce
). To determine when the
periodic-announce-frequency
timer
should start, we use relative-periodic-announce
. The yes
setting means the timer will start after
the announcement has finished playing, rather than when it starts to
play. The problem you could run into if you set this to no
is that if your periodic announcement runs
for any significant length of time (lets say 30 seconds), it will appear
as if it is being played every 15 seconds, rather than every 45 seconds
as may be intended.
How many times we announce the hold time to
the caller is controlled via the announce-holdtime
option, which we’ve set to
once
. Setting the value to yes
will announce it every time, and setting
to no
will disable it.
We configure how and when we announce the
caller’s estimated remaining hold time via announce-position
, which we’ve set to limit
. Using the value of limit
for announce-position
lets us announce the
caller’s position only if it is within the limit defined by announce-position-limit
. So, in this case
we’re only announcing the callers’ positions if they are in the first 10
positions of the queue. We could also use yes
to announce the position every time the
periodic announcement is played, set it to no
to never announce it, or use the value
more
if we want to announce the
position only when it is greater than the value set for announce-position-limit
.
Our last option, announce-round-seconds
, controls the value to
round to when we announce the caller’s hold time. In this case, instead
of saying “1 minute and 23 seconds,” the value would be rounded to the
nearest 30-second value, which would result in a prompt of “1 minute and
30 seconds.”
Overflowing out of the queue is done either with a timeout
value, or when no queue members are available (as defined by joinempty
or leavewhenempty
). In this section we’ll discuss
how to control when overflow happens.
The Queue()
application supports two kinds of timeout: one is for the maximum
period of time a caller stays in the queue, and the other is how long
to ring a device when attempting to connect a caller to a queue
member. We’ll be talking about the maximum period of time a caller
stays in the queue before the call overflows to another location, such
as VoiceMail()
. Once the call has
fallen out of the queue, it can go anywhere that a call could normally
go when controlled by the dialplan.
The timeouts are specified in two locations. The timeout that
indicates how long to ring queue members for is specified in the
queues.conf
file. The absolute
timeout (how long the caller stays in the queue) is controlled via the
Queue()
application. To set a maximum amount of time for callers to stay in
a queue, simply specify it after the queue name in the Queue()
application:
[Queues]
exten => 7000,1,Verbose(2,Joining the support queue for a maximum of 2 minutes)
same => n,Queue(support,120
)
same => n,VoiceMail(support@queues,u)
same => n,Hangup()
Of course, we could define a
different destination, but the VoiceMail()
application is as good as any. Just make sure that if you’re going
to send callers to voicemail someone checks it regularly and calls
your customers back.
Now let’s say we have the scenario where we have set our absolute timeout to 10 seconds, our timeout value for ringing queue members to 5 seconds, and our retry timeout value to 4 seconds. In this scenario, we would ring the queue member for 5 seconds, then wait 4 seconds before attempting another queue member. That brings us up to 9 seconds of our absolute timeout of 10 seconds. At this point, should we ring the second queue member for 1 second and then exit the queue, or should we ring this member for the full 5 seconds before exiting?
We control which timeout value has priority
with the timeoutpriority
option
in queues.conf
. The
available values are app
and
conf
. If we want the application
timeout (the absolute timeout) to take priority, which would cause our
caller to be kicked out after exactly 10 seconds, we should set the
timeoutpriority
value to app
. If we want the configuration file
timeout to take priority and finish ringing the queue member, which
will cause the caller to stay in the queue a little longer, we should
set timeoutpriority
to conf
. The default value is app
(which is the default behavior in
previous versions of Asterisk).
Asterisk provides two options that control when callers
can join and are forced to leave queues, based on the statuses of the
queue members. The first option, joinempty
, is used to control whether callers can enter a queue.
The leavewhenempty
option
is used to control when callers already in a queue
should be removed from that queue (i.e., if all of the queue members
become unavailable). Both options take a comma-separated list of
values that control this behavior. The factors are listed in Table 13.7, “Options that can be set for joinempty or
leavewhenempty”.
Table 13.7. Options that can be set for joinempty or leavewhenempty
Value | Description |
---|---|
paused | Members are considered unavailable if they are paused. |
penalty | Members are considered unavailable if their penalties
are less than QUEUE_MAX_PENALTY . |
inuse | Members are considered unavailable if their device
status is In Use . |
ringing | Members are considered unavailable if their device
status is Ringing . |
unavailable | Applies primarily to agent channels; if the agent is not logged in but is a member of the queue it is considered unavailable. |
invalid | Members are considered unavailable if their device
status is Invalid . This is
typically an error condition. |
unknown | Members are considered unavailable if device status is unknown. |
wrapup | Members are considered unavailable if they are currently in the wrapup time after the completion of a call. |
For joinempty
, prior to placing a caller into
the queue, all the members are checked for availability using the
factors you list as criteria. If all members are deemed to be
unavailable, the caller will not be permitted to enter the queue, and
dialplan execution will continue at the next priority.[130] For the leavewhempty
option, the members’ statuses are checked periodically against the
listed conditions; if it is determined that no members are available to take calls, the
caller is removed from the queue, with dialplan execution continuing
at the next priority.
An example use of joinempty
could be:
joinempty=paused,inuse,invalid
With
this configuration, prior to a caller entering the queue the statuses
of all queue members will be checked, and the caller will not be
permitted to enter the queue unless at least one queue member is found
to have a status that is not paused
, inuse
, or invalid
.
The leavewhenempty
example could be something
like:
leavewhenempty=inuse,ringing
In
this case, the queue members’ statuses will be checked periodically,
and callers will be removed from the queue if no queue members can be
found who do not have a status of either inuse
or ringing
.
Previous versions of Asterisk used the
values yes
, no
, strict
, and loose
as the available values to be
assigned. The mapping of those values is shown in Table 13.8, “Mapping between old and new values for controlling when
callers join and leave queues”.
Table 13.8. Mapping between old and new values for controlling when callers join and leave queues
Value | Mapping (joinempty) | Mapping (leavewhenempty) |
---|---|---|
yes | (empty) | penalty,paused,invalid |
no | penalty,paused,invalid | (empty) |
strict | penalty,paused,invalid,unavailable | penalty,paused,invalid,unavailable |
loose | penalty,invalid | penalty,invalid |
The use of Local channels as queue members is a popular way of executing parts of the dialplan and performing checks prior to dialing the actual agent’s device. For example, it allows us to do things like start recording the call, set up channel variables, write to a log file, set a limit on the call length (e.g., if it is a paid service), or do any of the other things we might need to do once we know which location we’re going to call.
When using Local channels for queues, they are
added just like any other channels. In the queues.conf
file, adding a Local channel
would look like this:
; queues.conf [support](StandardQueue) member => Local/SIP-0000FFFF0001@MemberConnector ; pass the technology to dial over ; and the device identifier, ; separated by a hyphen. We'll ; break it apart inside the ; MemberConnector context.
Notice how we passed the type of
technology we want to call along with the device identifier to the
MemberConnector
context. We’ve
simply used a hyphen (although we could have used nearly anything as
a separator argument) as the field marker. We’ll use the CUT()
function inside the MemberConnector
context and assign the
first field (SIP
) to one channel
variable and the second field (0000FFFF0001
) to another channel variable,
which will then be used to call the endpoint.
Passing information to be later “exploded”
in the context used by the Local channel is a common and useful
technique
(kind of like the
explode()
function in
PHP).
Of course, we’ll need the MemberConnector
context to actually connect
the caller to the agent:
[MemberConnector] exten => _[A-Za-z0-9].,1,Verbose(2,Connecting ${CALLERID(all)} to Agent at ${EXTEN}) ; filter out any bad characters, allowing alphanumeric characters and the hyphen same => n,Set(QueueMember=${FILTER(A-Za-z0-9\-,${EXTEN}) ; assign the first field of QueueMember to Technology using the hyphen separator same => n,Set(Technology=${CUT(QueueMember,-,1)}) ; assign the second field of QueueMember to Device using the hyphen separator same => n,Set(Device=${CUT(QueueMember,-,2)}) ; dial the agent same => n,Dial(${Technology}/${Device}) same => n,Hangup()
So, now we’ve passed our queue member
to the context, and we can dial the device. However, because we’re using
the Local channel as the queue member, the Queue()
won’t necessarily know the state the call is in,
especially when the Local channel is optimized out of the path (see
https://wiki.asterisk.org/wiki/display/AST/Local+Channel+Modifiers
for information about the /n
modifier, which causes the Local channel to not be optimized out of the
path). The queue will be monitoring the state of the Local channel, and
not that of the device we really want to monitor.
Luckily, we can give the Queue()
the actual device to monitor and
associate that with the Local channel, so that the Local channel’s state
is always that of the device we’ll end up calling. Our queue member
would be modified in the queues.conf
file like so:
; queues.conf [support](StandardQueue) member => Local/SIP-0000FFFF0001@MemberConnector,,,SIP/0000FFFF0001
Only SIP channels are capable of sending back reliable device state information, so it is highly recommended that you use only these channels when using Local channels as queue members.
You can also use the AddQueueMember()
and RemoveQueueMember()
applications to add members to and remove members from a queue, just
like with any other channel. AddQueueMember()
also has the ability to set
the state interface, which we defined statically in the queues.conf
file. An example of how you might
do this follows:
[QueueMemberLogin] exten => 500,1,Verbose(2,Logging in device ${CHANNEL(peername)} into the support queue) ; Save the device's technology to the MemberTech channel variable same => n,Set(MemberTech=${CHANNEL(channeltype)}) ; Save the device's identifier to the MemberIdent channel variable same => n,Set(MemberIdent=${CHANNEL(peername)}) ; Build up the interface name and assign it to the Interface channel variable same => n,Set(Interface=${MemberTech}/${MemberIdent}) ; Add the member to the support queue using a Local channel. We're using the same ; format as before, separating the technology and the device indentifier with ; a hyphen and passing that information to the MemberConnector context. We then ; use the IF() function to determine if the member's technology is SIP and, if so, ; to pass back the contents of the Interface channel variable as the value to the ; state interface field of the AddQueueMember() application. ; ; *** This line should not have any line breaks same => n,AddQueueMember(support,Local/${MemberTech}-${MemberIdent} @MemberConnector,,,${IF($[${MemberTech} = SIP]?${Interface})}) same => n,Playback(silence/1) ; Play back either the agent-loginok or agent-incorrect file, depending on what ; the AQMSTATUS variable is set to. same => n,Playback(${IF($[${AQMSTATUS} = ADDED]?agent-loginok:agent-incorrect)}) same => n,Hangup()
Now that we can add devices to the
queue using Local channels, let’s look at how we might control the
number of calls to either non-SIP channels or devices with more than one
line on them. We can make use of the GROUP()
and GROUP_COUNT()
functions to track call counts
to an endpoint. We’ll modify our MemberConnector
context to take this into
account:
[MemberConnector] exten => _[A-Za-z0-9].,1,Verbose(2,Connecting ${CALLERID(all)} to Agent at ${EXTEN}) ; filter out any bad characters, allowing alphanumeric characters and the hyphen same => n,Set(QueueMember=${FILTER(A-Za-z0-9\-,${EXTEN}) ; assign the first field of QueueMember to Technology using the hyphen separator same => n,Set(Technology=${CUT(QueueMember,-,1)}) ; assign the second field of QueueMember to Device using the hyphen separator same => n,Set(Device=${CUT(QueueMember,-,2)}) ; Increase the value of the group inside the queue_members category by one same => n,Set(GROUP(queue_members)=${Technology}-${Device}) ; Check if the group@category is greater than 1, and, if so, return Congestion() ; (too many channels) ; ; *** This line should not have any line breaks same => n,ExecIf($[${GROUP_COUNT(${Technology}-${Device}@queue_members)} > 1] ?Congestion()) ; dial the agent same => n,Dial(${Technology}/${Device}) same => n,Hangup()
The passing back of Congestion()
will cause the caller to be
returned to the queue (while this is happening, the caller gets no
indication that anything is amiss and keeps hearing music until we
actually connect to the device). While this is not an ideal situation
because the queue will keep trying the member over and over again (or at
least include it in the cycle of agents, depending on how many members
you have and their current statuses), it is better than an agent getting
multiple calls at the same time.
We’ve also used this same method to create a
type of reservation process. If you want to call an agent directly (for
example, if the caller needs to follow up with a particular agent), you
could reserve that agent by using the GROUP()
and GROUP_COUNT()
functions to essentially pause
the agent in the queue until the caller can be connected. This is
particularly useful in situations where you need to play some
announcements to the caller prior to connecting her with the agent, but
you don’t want the agent to get connected to another caller while the
announcements are being played.