I always wonder if I’m missing some basic features of Asterisk that would allow me to do things in an easier way, but often I find it difficult or impossible to find good solutions online, meaning I end up creating my own solutions.
My office has a rather convoluted queue setup that was initially configured on a Cisco CallManager system. I had to find a smooth way of replicating this on Asterisk. The basic setup is we have about 12 different queues. Users need a simple way to login/logout of a queue (only one at a time). That is the easy part. The extra catch is each phone can be reached directly from the outside, and if they don’t answer the call should roll into their queue, as extensions do not have their own voicemail. We frequently have new hires and people moved to different groups, so this saves me a lot of trouble with manual queue changes for each phone.
I’m still using the older style extension language as I haven’t had time to switch our many offices to anything newer (yet). I could really use case statements, as you will see. I am also accessing a MySQL database using func_odbc. Back when I first set these systems up, I liked using that better than the mysql features, as it was part of the core Asterisk distribution and the mysql things were in asterisk-addons. I wanted to stick with what I saw as the simpler setup. First the easy stuff.
A basic queue config in queues.conf
[5551212] ; queue 1212
musiconhold=default
strategy=rrmemory
timeout=12
ringinuse=yes
Now for the dialplan. Users login with *7 and logout with *9. When dialing *7, they are logged out of any other queue (in case they forgot to log out), are prompted for their 4-digit queue number, then logged in. We also update a database entry. I’ll get to that in a moment. Here is the extensions.conf:
exten => *7,1,Answer()
exten => *7,2,Set(TRIES=0)
exten => *7,3,Set(ATTEMPT=0)
exten => *7,4,Wait(1)
exten => *7,5,Read(QNUM|enter-qnum|4)
exten => *7,6,GotoIf($["${QNUM}" = "1212"]?51:7)
exten => *7,7,GotoIf($["${QNUM}" = "1213"]?51:8)
exten => *7,8,GotoIf($["${QNUM}" = "1214"]?51:9)
exten => *7,9,GotoIf($["${QNUM}" = "1215"]?51:800)
exten => *7,51,Set(OLDQNUM=${ODBC_QUEUENUMR(queue,${CALLERID(num)})})
exten => *7,52,System(asterisk -rx "queue remove member SIP/${CALLERID(num)} from 555${OLDQNUM}")
exten => *7,53,Set(ODBC_QUEUENUMW()=${QNUM}\,${CALLERID(num)})
exten => *7,54,System(asterisk -rx "queue add member SIP/${CALLERID(num)} to 555${QNUM}")
exten => *7,55,Noop(Agent ${CALLERID(num)} added to queue 555${QNUM})
exten => *7,56,Playback(agent-loginok)
exten => *7,57,Hangup
exten => *7,800,Playback(invalid-qnum)
exten => *7,801,Wait(1)
exten => *7,802,Set(TRIES=$[${TRIES} + 1])
exten => *7,803,GoToIf($[${TRIES} > 2]?*7,900:*7,5)
exten => *7,804,Hangup
exten => *7,900,Playback(goodbye-qnum)
exten => *7,901,Hangup
exten => *9,1,Answer()
exten => *9,2,Set(QNUM=${ODBC_QUEUENUMR(queue,${CALLERID(num)})})
exten => *9,3,System(asterisk -rx "queue remove member SIP/${CALLERID(num)} from 555${QNUM}")
exten =>*9,4,Playback(agent-loggedoff)
exten => *9,5,Hangup()
The call flow for the *7 login sets counters to zero, plays a recorded “Please enter your queue number…” message, then waits for input. If they put in an invalid number, it tells them, updates the try counter, and re-prompts for the queue number. After several failed attempts it plays a goodbye message and disconnects. If they enter a valid number, it checks their last logged in queue (extension is grabbed from their callerid and their last queue number is pulled from the database), logs them out (just to make sure – I don’t check if they actually were logged out first, I just try to log them out. It’s probably a bit lazy), then it updates the database information with the queue number they entered, logs them into that queue, and plays an “Agent logged in” message before disconnecting. We use 7-digit queue numbers for some other office features, but to make things easy we only ask for the last 4 digits from the users, so I append the first three digits to the queue number (in this case, the first three are “555″).
The “*9″ logout feature is much more simplistic. It reads the queue they were logged into from the database, logs them out, and plays the “Agent logged off” message.
The database table structure we’re using is rather simple. The table is called “queuenum” which contains three fields; id, exten, queue. “Id” is just an auto-incremented field number. “exten” is the extension number (table was pre-populated with all extensions), and “queue” is the queue they are logged into/last logged into. Now for the function calls to read and write the queue info:
func_odbc.conf
[QUEUENUMW]
dsn=asterisk
write=UPDATE queuenum SET queue='${VAL1}' WHERE EXTEN='${VAL2}'
[QUEUENUMR]
dsn=asterisk
read=SELECT ${ARG1} FROM queuenum WHERE EXTEN = '${ARG2}'
Pretty basic – one read the queue number, one writes the queue number.
So that takes care of the queues.conf setup, all extensions are in agents.conf, we have a database table full of extension numbers, we have a way for users to log in/log out of their queues, and we have the func_odbc calls to read/write queue information to/from a database. All that is left is the extensions.conf configuration for each phone so inbound calls roll to their queue (or the queue they were last logged in to):
extensions.conf (for the phone):
exten => 5559999,1,Dial(SIP/5559999,20)
exten => 5559999,n,Goto(queue-routing,s,1)
And here is the queue routing routine:
[queue-routing]
exten => s,1,Set(QNUM=${ODBC_QUEUENUMR(queue,${CALLERID(num)})})
exten => s,2,Noop(loop routing from ${CALLERID(num)} to queue 555${QNUM})
exten => s,3,Goto(inboundqueues,555${QNUM},1)
This pulls the queue number from the database and sends the call to the context (in this example, I called it “inboundqueues”) and the correct queue number. There is also code to handle invalid entries (if they would happen) to send the calls to our front desk.
That’s it! The only changes I want to make is to have it automatically create the extension entry in the database table so when adding new phones I don’t have to do it manually. It will only be a few lines of code to check for the existence of the extension, and if it’s not there, create it (which would happen immediately after verifying the user puts in a valid queue number. It’s worth noting that when I auto-generated the database table, I also put all extensions into a default queue so there is no code to handle a “NULL” queue situation. Once I add the code to auto-populate the table, it will always be a non-issue.
Go ahead and critique or tell me there is a far easier way to handle this. I enjoy finding better ways of doing things!


Posted in