Creating a Simple ACD Queue

To start with, we’re going to create a simple ACD queue. It will accept callers and attempt to deliver them to a member of the queue.

Note

In Asterisk, the term member refers to a peer assigned to a queue that can be dialed, such as SIP/0000FFFF0001. An agent technically refers to the Agent channel also used for dialing endpoints. Unfortunately, the Agent channel is a deprecated technology in Asterisk, as it is limited in flexibility and can cause unexpected issues that can be hard to diagnose and resolve. We will not be covering the use of chan_agent, so be aware that we will generally use the term member to refer to the telephone device and agent to refer to the person who handles the call. Since one isn’t generally effective without the other, either term may refer to both.

We’ll create the queue(s) in the queues.conf file, and manually add queue members to it through the Asterisk console. In the section the section called “Queue Members”, we’ll look into how to create a dialplan that allows us to dynamically add and remove queue members (as well as pause and unpause them).

The first step is to create your queues.conf file in the /etc/asterisk configuration directory:

$ cd /etc/asterisk/
$ touch queues.conf

Populate it with the following configuration, which will create two queues named [sales] and [support]. You can name them anything you want, but we will be using these names later in the book, so if you use different queue names from what we’ve recommended here, make note of your choices for future reference:

[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=no             ; do not join the queue when no members available
leavewhenempty=yes       ; 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

The [general] section defines the default behavior and global options. We’ve only specified two options in the [general] section, since the built-in defaults are sufficient for our needs at this point.

The first option is autofill, which tells the queue to distribute all waiting callers to all available members immediately. Previous versions of Asterisk would only distribute one caller at a time, which meant that while Asterisk was signaling an agent, all other calls were held (even if other agents were available) until the first caller in line had been connected to an agent (which obviously led to bottlenecks in older versions of Asterisk where large, busy queues were being used). Unless you have a particular need for backward-compatibility, this option should always be set to yes.

The second option in the [general] section of queues.conf is shared_lastcall. When we enable shared_lastcall, the last call to an agent who is logged into multiple queues will be the call that is counted for wrapup time[128] in order to avoid sending a call to an agent from another queue during the wrap period. If this option is set to no, the wrap timer will only apply to the queue the last call came from, which means an agent who was wrapping up a call from the support queue might still get a call from the sales queue. This option should also always be set to yes (the default).

The next section, [StandardQueue] is the template we’ll apply to our sales and support queues (we declared it a template by adding (!)). We’ve defined the musicclass to be the default music on hold, as configured in the musiconhold.conf file. The strategy we’ll employ is rrmemory, which stands for Round-Robin with Memory. The rrmemory strategy works by rotating through the agents in the queue in sequential order, keeping track of which agent got the last call, and presenting the next call to the next agent. When it gets to the last agent, it goes back to the top (as agents log in, they are added to the end of the list). We’ve set joinempty to no since it is generally bad form to put callers into a queue where there are no agents available to take their calls.

Note

You could set this to yes for ease of testing, but we would not recommend putting it into production unless you are using the queue for some function that is not about getting your callers to your agents. Nobody wants to wait in a line that is not going anywhere.

The leavewhenempty option is used to control whether callers should fall out of the Queue() application and continue on in the dialplan if no members are available to take their calls. We’ve set this to yes because it makes no sense to wait in a line that’s not going anywhere.

Note

From a business perspective, you should be telling your agents to clear all calls out of the queue before logging off for the day. If you find that there are a lot of calls queued up at the end of the day, you might want to consider extending someone’s shift to deal with them. Otherwise, they’ll just add to your stress when they call back the next day, in a worse mood.

The alternative is to use GotoIfTime() near the end of the day to redirect callers to voicemail, or some other appropriate location in your dialplan.

Finally, we’ve set ringinuse to no, which tells Asterisk not to ring members when their devices are already ringing. The purpose of setting ringinuse to no is to avoid multiple calls to the same member from one or more queues.

Note

It should be mentioned that joinempty and leavewhenempty are looking for either no members logged into the queue, or all members unavailable. Agents that are Ringing or InUse are not considered unavailable, so will not block callers from joining the queue or cause them to be kicked out when joinempty=no and/or leavewhenempty=yes.

Once you’ve finished configuring your queues.conf file, you can save it and reload the app_queue.so module from your Asterisk CLI:

$ asterisk -r
*CLI> module reload app_queue.so
   -- Reloading module 'app_queue.so' (True Call Queueing)

Then verify that your queues were loaded into memory:

localhost*CLI> queue show
support      has 0 calls (max unlimited) in 'rrmemory' strategy 
(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s
   No Members
   No Callers

sales        has 0 calls (max unlimited) in 'rrmemory' strategy 
(0s holdtime, 0s talktime), W:0, C:0, A:0, SL:0.0% within 0s
   No Members
   No Callers

Now that you’ve created the queues, you need to configure your dialplan to allow calls to enter the queue.

Add the following dialplan logic to the extensions.conf file:

[Queues]
exten => 7001,1,Verbose(2,${CALLERID(all)} entering the support queue)
same => n,Queue(support)
same => n,Hangup()

exten => 7002,1,Verbose(2,${CALLERID(all)} entering the sales queue)
same => n,Queue(sales)
same => n,Hangup()

[LocalSets]
include => Queues      ; allow phones to call queues

We’ve included the Queues context in the LocalSets context so that our telephones can call the queues we’ve set up. In Chapter 15, The Automated Attendant, we’ll define menu items that go to these queues. Save the changes to your extensidons.conf file, and reload the dialplan with the dialplan reload CLI command.

If you dial extension 7001 or 7002 at this point, you will end up with output like the following:

 -- Executing [7001@LocalSets:1] Verbose("SIP/0000FFFF0003-00000001", 
    "2,"Leif Madsen" <100> entering the support queue") in new stack
== "Leif Madsen" <1--> entering the support queue
 -- Executing [7001@LocalSets:2] Queue("SIP/0000FFFF0003-00000001", 
    "support") in new stack
    [2011-02-14 08:59:39] WARNING[13981]: app_queue.c:5738 queue_exec: 
    Unable to join queue 'support'
 -- Executing [7001@LocalSets:3]
    Hangup("SIP/0000FFFF0003-00000001", "") in new stack
 == Spawn extension (LocalSets, 7001, 3) exited non-zero on 
    'SIP/0000FFFF0003-00000001'

You don’t join the queue at this point, as there are no agents in the queue to answer calls. Because we have joinempty=no and leavewhenempty=yes configured in queues.conf, callers will not be placed into the queue. (This would be a good opportunity to experiment with the joinempty and leavewhenempty options in queues.conf to better understand their impact on queues.)

In the next section, we’ll demonstrate how to add members to your queue (as well as other member interactions with the queue, such as pause/unpause).



[128] Wrapup time is used for agents who may need to perform some sort of logging or other function once a call is done. It gives them a grace period of several seconds in order to perform this task before taking another call.