Call queues

I am attempting to setup a call queue with Addqueuemeber, but ran into a few issues.

Item one is the lack of an agent login prompt. I have tried AgentCallBackLogin in the past, but it never seemed to work right. AddQueueMember seems to work much better, but I’d like to still have the agent login. We have a call center where our agents more around from desk to desk and it would be nice to keep track of who is doing what. I couldn’t find another app that gives what AgentCallbackLogin was supposed to, so maybe there is something that can be done in the dialplan that will help me accomplish this?

The other hurdle - queue calls ringing at phones that are already on another call. I am running our test setup with Aastra 9133 phones. When I pickup a call that is in queue, other calls still within the queue will ring on line 2. When I tested this in the past with Polycom phones, I was able to change the phone configuration to accomidate this problem (and of course do not remember what I did). With the Aastra phones, I need some guidance.

Thanks for you suggestions!

  1. yes, you can make addqueuemember emulate agentcallbacklogin. FYI, you might as well start using addqueuemember because agentcallbacklogin is (supposedly) being deprecated in future releases of asterisk due to the number of problems digium is having with that particular app.
    here are some links on using AQM to replace ACBL: … nformation … nt+channel

  2. this is a known bug:
    fixes are to use the Local channel and groupcount to emulate a call limit check on queue calls only, or just set call-limit to 1. alternately, what you probably did on the polycom was set up only one line.

Thanks much, that information is just what I needed!!!


no problem, best of luck.

The second example in the “agents without agent chanel” works nicely (after fixing it up some.) The only issue I still have is with authentication. The example code does not allow for entering a password in addition to an agent number. I started looking around and quickly realized that the authenticate app probably isn’t the best solution.

I came across a few examples of using voicemail.conf to authenticate users, which seems like a logical way of doing things. … +passwords

I tried both of these examples, but failed to get them to work properly. I am trying to stick to the perl example because it is easeier for me to undrestand. The problem I have is with the AGI script passing the "result"variable back to asterisk. I can run the script, but I the result variable does not get updated. I tried setting up the variable before running the script, but it never returns the result. I have never used AGI scripts before, so hopefully it is just something simple I am missing.

Thanks again to anyone that may be able to point me in the right direction.


[quote=“kue”]The second example in the “agents without agent chanel” works nicely (after fixing it up some.)

Couldn’t you update the Wiki ?

Well just got back to work after having surgery. When I have a free moment, I will update the wiki with what I have. Still looking to figure out the authentication problem though…

in regards to the authentication issue…see here: … thenticate

this is a built in function of asterisk to authenticate a user based on their VM password, so if you can incorporate this into your dynamic queue member logon script, you should be good to go…


Well, I have been working on this again, and ran into another issue that I need to resolve. Please bear with me, I am trying to make my explanation as clear as I can.

Working with the “Agent without agent channel” from the wiki and suggestions from the forum here, I have gotten dynamic agents to work fairly well.

The problem comes in when the dialplan attempts to “logoff” an agent that rejects or doesn’t answer calls. The dialplan runs RemoveQueueMember, but the caller in the queue hears this run and then gets disconnected.

Is there a way to hangup/disconnect the unanswered call and continue processing items in the dialplan? Perhaps I can initiate the agent “logout” another way preventing the queue caller from getting disconnected?

Here is what I have in extensions.conf. Please excuse the mess :smile:

exten => 8301,1,Goto(agent_login,s,1);
exten => 8302,1,Goto(agent_avail,s,1);
exten => 8303,1,Goto(agent_moretime,s,1);

; Local/${agent}@agent_call are dynamic members
; added to semicolon-separated queues in AGENT1/valid tree
exten => _XXXX,1,SetVar(agent=${EXTEN});
exten => _XXXX,2,noop()
exten => _XXXX,2,ResponseTimeout(1);
exten => _XXXX,3,DBget(mychan=AGENT/${agent}/onChannel);
exten => _XXXX,4,Goto(check,1);
exten => _XXXX,104,Congestion(); oops, we shouldn’‘t be calling this agent
; check if channel in use
exten => check,1,ChanIsAvail(${mychan},s,);
exten => check,102,Goto(busy,1);
exten => check,2,DBget(wrapUpAt=AGENT/${agent}/wrapUpAt);
exten => check,3,GotoIf($[ $[${EPOCH} - ${wrapUpAt}] < 0]?busy,1);
exten => check,4,DBdel(AGENT/${agent}/wrapUpAt); expired, nuke it
exten => check,103,NoOp(agent ${agent} has no wrapUpAt in astdb == call);
exten => check,104,Goto(5);
; check availability
exten => check,5,DBget(avail=AGENT/${agent}/avail);
exten => check,6,GotoIf(${avail}?call,1:busy,1);
exten => check,106,NoOp(agent ${agent} has no avail in astdb == call);
exten => check,107,Goto(call,1);
; call agent
exten => call,1,SetVar(_ALERT_INFO=queue);
exten => call,2,UserEvent(QueueCall|Queue: ${queue});
exten => call,3,Dial(${mychan},15,gM(agent_answered^${agent}));
exten => call,4,Goto(call-${DIALSTATUS},1);
; handle dial results, auto-logout agent under two conditions
exten => call-BUSY,1,DBget(rejects=AGENT/${agent}/rejects);
exten => call-BUSY,102,SetVar(rejects=0);
exten => call-BUSY,103,Goto(2);
exten => call-BUSY,2,SetVar(rejects=$[${rejects} + 1]);
exten => call-BUSY,3,DBput(AGENT/${agent}/rejects=${rejects});
exten => call-BUSY,4,UserEvent(AgentReject,Agent: ${agent});
exten => call-BUSY,5,GotoIf($[${rejects} > 2]?slacker-rejects,1); 3 strikes you’‘re out (on DND?)
exten => call-BUSY,6,Hangup; keep their position
exten => call-NOANSWER,1,DBget(bounces=AGENT/${agent}/bounces);
exten => call-NOANSWER,102,SetVar(bounces=0);
exten => call-NOANSWER,103,Goto(2);
exten => call-NOANSWER,2,SetVar(bounces=$[${bounces} + 1]);
exten => call-NOANSWER,3,DBput(AGENT/${agent}/bounces=${bounces});
exten => call-NOANSWER,4,UserEvent(AgentBounce,Agent: ${agent});
exten => call-NOANSWER,5,GotoIf($[${bounces} > 2]?slacker-bounces,1); 3 strikes you’'re out (bouncing)
exten => call-NOANSWER,6,Hangup; keep their position
exten => slacker-bounces,1,SetVar(reason=bounced 3 calls);
exten => slacker-bounces,2,Goto(slacker,1);
exten => slacker-rejects,1,SetVar(reason=rejected 3 calls);
exten => slacker-rejects,2,Goto(slacker,1);
exten => slacker,1,UserEvent(AgentSlacker,Agent: ${agent});
exten => slacker,n,NoOp(agent ${agent} is a slacker: ${reason});
; res_perl required. this notifies the phone (sip message) and an IRC bot
; exten => slacker,n,Perl(${agent}:${reason}); "not using this"
exten => slacker,n,Goto(agent_login,logout,1); log them off
exten => busy,1,Busy();
exten => t,1,Hangup();
exten => i,1,Congestion();

exten => s,1,DBdel(AGENT/${ARG1}/bounces);
exten => s,2,DBdel(AGENT/${ARG1}/rejects);

; toggle available status
exten => s,1,Answer;
exten => s,2,ResponseTimeout(1);
exten => s,3,Wait(1);
exten => s,4,ResponseTimeout(1);
exten => s,5,Cut(mychan=CHANNEL,-,1);
exten => s,6,DBget(agent=AGENT/channelHasAgent/${mychan});
exten => s,107,Goto(login,1);
exten => s,7,DBget(stat=AGENT/${agent}/avail);
exten => s,8,GotoIf(${stat}?unavail,1:avail,1);
; set unavailable, you slacker
exten => unavail,1,DBput(AGENT/${agent}/avail=0);
; exten => unavail,2,Playback(xm/agent-avail-no);
exten => unavail,2,Playback(abandon-all-hope); 'I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!'
exten => unavail,3,UserEvent(AgentUnavailable,Agent: ${agent});
; set available
exten => avail,1,DBput(AGENT/${agent}/avail=1);
; exten => avail,2,Playback(xm/agent-avail-yes);
exten => avail,2,Playback(welcome); 'I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!'
exten => avail,3,UserEvent(AgentAvailable,Agent: ${agent});
; not logged in
; exten => login,1,Playback(xm/agent-avail-login);
exten => login,1,Playback(airport); 'I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!'
exten => login,2,Goto(agent_login,s,1)
exten => t,1,Hangup();
exten => i,1,Playback(agent-avail-error); “More sounds needed”

exten => s,1,Answer;
exten => s,2,ResponseTimeout(1);
exten => s,3,Wait(1);
exten => s,4,NoOp(agent needs more wrap up time);
exten => s,5,ResponseTimeout(1);
exten => s,6,Cut(mychan=CHANNEL,-,1);
exten => s,7,DBget(agent=AGENT/channelHasAgent/${mychan});
exten => s,108,Goto(login,1);
exten => s,8,DBput(AGENT/${agent}/wrapUpAt=$[${EPOCH} + ${WRAPUPTIME}]);
exten => s,9,Playback(another-time); 'I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!'
exten => s,10,UserEvent(AgentMoreTime,Agent: ${agent}, Until: $[${EPOCH} + ${WRAPUPTIME}]);
exten => login,1,Playback(airport); 'I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!'
exten => login,2,Goto(agent_login,s,1)
exten => t,1,Hangup();
exten => i,1,Playback(an-error-has-occured); ‘I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!’

exten => s,1,Answer();
exten => s,2,Wait(1);
exten => s,3,ResponseTimeout(5);
exten => s,4,Cut(mychan=CHANNEL,-,1);
exten => s,5,DBget(agent=AGENT/channelHasAgent/${mychan});
exten => s,106,Goto(start,1);
exten => s,6,Goto(logout,1);
; a couple of read routines to get agent id
exten => start,1,Read(agent,agent-user);
exten => start,2,VMAuthenticate(${agent}@penzeys)
exten => start,3,Goto(verify,1);
exten => dupe,1,Read(continue,agent-alreadyon,9); 'Press 9 to logout existing connection I REALLY NEED TO CHANGE THIS AFTER I CREA$
exten => dupe,2,GotoIf($["${continue}" = “9”]?3:4);
exten => dupe,3,Goto(logout,1);
exten => dupe,4,Read(agent,agent-user); 'I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!'
exten => dupe,5,Goto(verify,1);
exten => bad,1,Read(agent,agent-incorrect); 'I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!'
exten => bad,2,Goto(verify,1);
; verification
exten => verify,1,DBget(onchan=AGENT/${agent}/onChannel);
exten => verify,102,Goto(3);
exten => verify,2,GotoIf($["${onchan}" = “{mychan}”]?logout,1:dupe,1);
exten => verify,3,DBget(queues=AGENT1/valid/${agent});
exten => verify,104,Goto(bad,1);
exten => verify,4,Goto(login,1);
; login routine
exten => login,1,DBput(AGENT/channelHasAgent/${mychan}=${agent});
exten => login,2,DBput(AGENT/${agent}/onChannel=${mychan});
exten => login,3,DBput(AGENT/${agent}/avail=1);
exten => login,4,SetVar(i=1);
exten => login,5,Cut(j=queues,:,${i});
exten => login,6,While($[${LEN(${j})} > 0]);
exten => login,7,AddQueueMember(${j},Local/${agent}@agent_call);
exten => login,8,SetVar(i=$[${i} + 1]);
exten => login,9,Cut(j=queues,:,${i});
exten => login,10,EndWhile;
exten => login,11,Playback(agent-loginok);
exten => login,12,Goto(t,1);
; logout, leave configured queues
exten => logout,1,playback(wait-moment);
exten => logout,2,DBget(queues=AGENT1/valid/${agent});
exten => logout,3,SetVar(i=1);
exten => logout,4,Cut(j=queues,:,${i});
exten = logout,5,noop(i)
exten = logout,6,noop(j)
exten => logout,7,While($[${LEN(${j})} > 0])
exten => logout,8,RemoveQueueMember(${j},Local/${agent}@agent_call);
exten => logout,9,SetVar(i=$[${i} + 1]);
exten => logout,10,Cut(j=queues,:,${i});
exten => logout,11,EndWhile;
exten => logout,12,DBget(agentchan=AGENT/${agent}/onChannel);
;exten => logout,n,DBdel(AGENT/channelHasAgent/${mychan});
exten = logout,13,noop()
exten => logout,14,DBdel(AGENT/channelHasAgent/${agentchan});
exten => logout,15,DBdeltree(AGENT/${agent});
exten => logout,16,Playback(agent-loggedoff,skip);
exten => logout,17,GotoIf($["${continue}" = “1”]?login,1); logging off a dupe, go back
exten => logout,18,Goto(t,1);

exten => t,1,Hangup;
exten => i,1,Playback(an-error-has-occured); ‘I REALLY NEED TO CHANGE THIS AFTER I CREATE A SOUND FILE FOR IT!’

Well, for now I decided to use system calls to get the job done. Not the cleanest solution, but it works.