[Ref: Automatic Call Distribution (Queues) , Asterisk Admin Guide 2013, , Asterisk WIKI ]
Table of Contents
We use Queues for to help groups manage their incoming calls (distribution etc.) and more recently because we wanted to offer some services under different “labels.”
For example, we wanted to have a dedicated phone numbers for our reception, sales department, and Support. The flexibility will allow us to publish a number that clients can dial that not only puts them into the department, but also connect them with the next available person to manage their query..
Unfortunately for us, the groups are not quite distinct and we have a few people serving in multiple groups. The following is a quick review of how we used Asterik’s Queues, and messages to enable our desired solution.
Following the above reference documentation we define a simple queue template ServiceQueue from which we derive our queues.
[ServiceQueue](!)
weight=0
wrapuptime=15
autopause=all
maxlen=4
announce-frequency=240
min-announce-frequency=15
relative-periodic-announce=yes
announce-position = no
ringinuse = no
periodic-announce = queues-thank-you-for-waiting-our-operators-are-currently-busy
[general]
persistentmembers = yes
monitor-type = MixMonitor
[ServiceQueue](!)
weight=0
wrapuptime=15
autopause=all
maxlen=4
announce-frequency=240
min-announce-frequency=15
relative-periodic-announce=yes
announce-position = no
ringinuse = no
[Reception](ServiceQueue)
announce = queues-phone-call-for-Company
periodic-announce = queues-thank-you-for-waiting-our-operators-are-currently-busy
member = SIP/6501
member = SIP/6502
[Sales](ServiceQueue)
announce = queues-phone-call-for-Sales
periodic-announce = queues-thank-you-for-waiting-our-operators-are-currently-busy
member = SIP/6579
member = SIP/6544
[Support](ServiceQueue)
announce = queues-phone-call-for-Support
periodic-announce = queues-thank-you-for-waiting-our-operators-are-currently-busy
member = SIP/6579
member = SIP/6553
The queue definition has a periodic-announce(ment) thanking callers for waiting in the queue.
An example audio message could be:
Since we’re creating multiple queues, but this message is common to all, then it makes sense to put it in once.
The audio files “queues-thank-you-for-waiting-our-operators-are-currently-busy.XXX” (with extensions .gsm, .alaw, .ulaw etc. ) are stored in the $astdatadir (defined in /etc/asterisk/asterisk.conf) which is usually /usr/local/share/asterisk/
Our queue for Reception will have a unique announce which announces the call to the our staff when picking up a call.
We also choose to explicitly set the members of the queue in our configuration file. Later we will show how we can have dynamic members.
Define: Reception Queue:
[Reception](ServiceQueue)
announce = queues-phone-call-for-XYZ-reception
member = SIP/6501
member = SIP/6502
Our queue for Sales and Support will have a different announcement, and a different member list.
To simplify dialplan routing for multiple Queues, we will use a macro for adding (inserting) calls into one of our chosen queues.
Very simply, we are going to take incoming calls to the imaginary phone numbers below, and route them into our new queues.
Incoming Phone Number | Queue |
---|---|
02-1111-6500 | Reception |
02-1111-6510 | Sales |
02-1111-6511 | Support |
exten => _0211116500.,1,Goto(queue-insert,Reception,1)
exten => _0211116510.,1,Goto(queue-insert,Sales,1)
exten => _0211116511.,1,Goto(queue-insert,Support,1)
To allow staff to dynamically enter, or exit, from a call queue we need to set up a dialplan. The below is one approach, using contexts defined further below.
exten => *9010, 1, Answer
same => n, Gosub(queue-login, Sales,)
same => n, Hangup()
exten => *9011, 1, Answer
same => n, Goto(queue-logout, Sales)
same => n, Hangup()
exten => *9012, 1, Answer
same => n, Goto(queue-pause, Sales)
same => n, Hangup()
To simplify our dialplan, we liberally use [contexts] as functions/subroutines to jump processing to.
The context queue-insert context takes as the 1st argument ${ARG1} the name of the queue we want to insert the call into and then goes through some processing.
[queue-insert]
exten => Reception,1,GotoIfTime(08:00-17:58,mon-fri,*,*?q-Reception,1)
same => n,Playback(business-hours-between-0800-1800)
same => n,Hangup()
The basic processing of a call, within the valid business hours, is to
exten => q-Reception,1,Playback(queues-thank-you-for-calling-Company-one-of-our-friendly-staff)
same => n,Queue(${queue},hHiIRtwkx,,,3000) ; don't set n option until really needed
same => n,Gosub(qjoin-${QUEUESTATUS},1)
same => n,Hangup()
File: queues
[queue-insert]
exten => Reception,1,GotoIfTime(08:00-17:58,mon-fri,*,*?q-Reception,1)
same => n,Goto(closed-${queue},1)
same => n,Playback(business-hours-between-0800-1800)
same => n,Hangup()
exten => Sales,1,GotoIfTime(08:00-17:58,mon-fri,*,*?q-Sales,1)
same => n,Goto(closed-${queue},1)
same => n,Playback(business-hours-between-0800-1800)
same => n,Hangup()
exten => Support,1,GotoIfTime(08:00-17:58,mon-fri,*,*?q-Support,1)
same => n,Goto(closed-${queue},1)
same => n,Playback(business-hours-between-0800-1800)
same => n,Hangup()
exten => q-Reception,1,Playback(queues-thank-you-for-calling-Company-one-of-our-friendly-staff)
same => n,Queue(${queue},hHiIRtwkx,,,3000) ; don't set n option until really needed
same => n,Gosub(qinsert-${QUEUESTATUS},1)
same => n,Hangup()
; ${QUEUESTATUS} Jump based on status (TIMEOUT, FULL, JOINEMPTY, LEAVEEMPTY, JOINUNAVAIL, LEAVEUNAVAIL, CONTINUE)
exten => q-Sales,1,Playback(queues-thank-you-for-calling-Sales-one-of-our-friendly-staff)
same => n,Queue(${queue},hHiIRtwkx,,,3000)
same => n,Gosub(qinsert-${QUEUESTATUS},1)
same => n,Hangup()
exten => q-Support,1,Playback(queues-hello-and-thank-you-for-calling-Support-one-of-our-friendly-staff)
same => n,Queue(${queue},hHiIRtwkx,,,3000)
same => n,Gosub(qinsert-${QUEUESTATUS},1)
same => n,Hangup()
exten => qinsert-TIMEOUT,1,Background(Msg Queue Timed Out)
same => n,VoiceMail(${queue})
same => n,Hangup()
exten => qinsert-FULL,1,Playback(Msg Queue is Full)
same => n,Hangup()
exten => qinsert-JOINEMPTY,1,Playback(Msg Queue is closed)
same => n,Hangup()
exten => qinsert-LEAVEEMPTY,1,Playback(Msg Queue is closed)
same => n,Hangup()
exten => qinsert-JOINUNAVAIL,1,Playback(Msg Queue is closed)
same => n,Hangup()
exten => qinsert-LEAVEUNAVAIL,1,Playback(Msg Queue is closed)
same => n,Hangup()
exten => qinsert-CONTINUE,1,Playback(some_announce_after_leaving_queue)
same => n,Hangup()
[queue-login]
exten => s,1,Set(queue=${ARG1})
same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})
same => n,AddQueueMember(${queue},${MemberChannel})
same => n,Gosub(qlogin-${AQMSTATUS},1) ; Jump based on status (ADDED, MEMBERALREADY, NOSUCHQUEUE)
same => n,Hangup()
exten => qlogin-ADDED,1,Background(agent-loginok)
same => n,Hangup() ; If they press #, return to start
exten => qlogin-MEMBERALREADY,1,Background(agent-alreadyon)
same => n,Hangup() ; If they press #, return to start
exten => qlogin-NOSUCHQUEUE,1,Background(pm-invalid-option)
same => n,Hangup()
[queue-member-pause]
exten => s,1,Set(queue=${ARG1})
same => n,NoOp(Queue PAUSE agent ${CHANNEL(peername)} to queue ${queue})
same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})
same => n,PauseQueueMember(${queue},${MemberChannel})
same => n,Gosub(qpause-${PQMSTATUS},1) ; Jump based on status (PAUSED,NOTFOUND)
same => n,Hangup()
exten => qpause-PAUSED,1,Background(dictate/paused)
same => n,Hangup() ; If they press #, return to start
exten => qpause-NOTFOUND,1,Background(pm-invalid-option)
same => n,Hangup() ; If they press #, return to start
[queue-member-unpause]
exten => s,1,Set(queue=${ARG1})
same => n,NoOp(Queue PAUSE agent ${CHANNEL(peername)} to queue ${queue})
same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})
same => n,UnPauseQueueMember(${queue},${MemberChannel})
same => n,Gosub(qunpause-${UPQMSTATUS},1) ; Jump based on status (UNPAUSED,NOTFOUND)
same => n,Hangup()
exten => qunpause-UNPAUSED,1,Background(agent-loginok)
same => n,Hangup() ; If they press #, return to start
exten => qunpause-NOTFOUND,1,Background(pm-invalid-option)
same => n,Hangup() ; If they press #, return to start
[queue-member-logout]
exten => s,1,Set(queue=${ARG1})
same => n,NoOp(Queue login agent ${CHANNEL(peername)} to queue ${queue})
same => n,Set(MemberChannel=${CHANNEL(channeltype)}/${CHANNEL(peername)})
same => n,RemoveQueueMember(${queue},${MemberChannel})
same => n,Gosub(qlogout-${RQMSTATUS},1) ; Jump based on status (REMOVED, NOTINQUEUE, NOSUCHQUEUE)
same => n,Hangup()
exten => qlogout-REMOVED,1,Background(agent-loggedoff)
same => n,Hangup() ; If they press #, return to start
exten => qlogout-NOTINQUEUE,1,Background(pm-invalid-option)
same => n,Hangup() ; If they press #, return to start
exten => qlogout-NOSUCHQUEUE,1,Background(pm-invalid-option)
same => n,Hangup()
File: itsp.source
[incoming_ITSP]
; (Sample) ITSP Supplied Number Range: 02-1111-6300 to 02-1111-6399
exten => _0211116300.,1,Goto(queue-insert,${FILTER(0-9,${EXTEN:0:10})},1(Reception))
exten => _0211116310.,1,Goto(queue-insert,${FILTER(0-9,${EXTEN:0:10})},1(Sales))
exten => _0211116311.,1,Goto(queue-insert,${FILTER(0-9,${EXTEN:0:10})},1(Support))
; -- Local Extensions (SIP devices) are accessible extensions 6320 ~ 6399
exten => _02111163XX.,1,Goto(localexten,${FILTER(0-9,${EXTEN:6:4})},1)
; (Sample) ITSP Supplied Number Range: 02-1111-4500 to 02-1111-4599 at friendpbx_1
exten => _02111145XX.,1,Goto(friendpbx_dial,${EXTEN:6:4},1(friendpbx_1,${GLOBAL(CONSOLE)}))
; (Sample) ITSP Supplied Number Range: 02-2222-4500 to 02-2222-4599 at friendpbx_2
; at extensions 6800 to 6899
exten => _02111145XX.,1,Goto(friendpbx_dial,68${EXTEN:8:2},1(friendpbx_2,${GLOBAL(CONSOLE)}))
File: services.queues
[queue-access]
; Agent logins
exten => *90, 1, Answer
same => n, Set(CHANNEL(musicclass)=default)
same => n,WaitMusicOnHold(20)
same => n, Hangup()
exten => *9010, 1, Answer
same => n, Macro(queue-login, Sales)
same => n, Hangup()
exten => *9011, 1, Answer
same => n, Macro(queue-logout, Sales)
same => n, Hangup()
exten => *9012, 1, Answer
same => n, Macro(queue-pause, Sales)
same => n, Hangup()
exten => *9013, 1, Answer
same => n, Macro(queue-unpause, Sales)
same => n, Hangup()
exten => *9019, 1, Answer
same => n, Goto(queues-join,${FILTER(0-9,${EXTEN:1:4})},1(Sales))
same => n, Hangup()
exten => *9020, 1, Answer
same => n, Macro(queue-login, Support)
same => n, Hangup()
exten => *9021, 1, Answer
same => n, Macro(queue-logout, Support)
same => n, Hangup()
exten => *9022, 1, Answer
same => n, Macro(queue-pause, Support)
same => n, Hangup()
exten => *9023, 1, Answer
same => n, Macro(queue-unpause, Support)
same => n, Hangup()
exten => *9029, 1, Answer
same => n, Goto(queues-join,${FILTER(0-9,${EXTEN:1:4})},1(Support))
same => n, Hangup()