login | register
Mon 13 of Oct, 2008 [04:56 UTC]

voip-info.org

History

Agents without agent channel

Created by: xkev,Last modification on Thu 31 of Aug, 2006 [15:19 UTC] by JustRumours
Here are two examples (one shorter, one long) how to establish queues without using chan_agent.

Example 1

Desgined for Asterisk 1.2

 [agent-login]
 exten => s,1,NoOp(${AgentUser})
 exten => s,2,AddQueueMember(${AgentContext}|${AgentChannel}|${AgentPenalty})
 exten => s,3,Wait(1)
 exten => s,4,Playback(agent-loginok)
 exten => s,5,Hangup
 exten => s,103,RemoveQueueMember(${AgentContext}|${AgentChannel})
 exten => s,104,Wait(1)
 exten => s,105,Playback(agent-loggedoff)
 exten => s,106,Hangup

 [tax-line]
 exten => s,1,Macro(dnv-messagebox-setup)
 exten => s,n,Set(AgentContext=${CONTEXT})
 exten => s,n,Set(AgentChannel=${CHANNEL})
 exten => s,n,Set(AgentChannel=${CUT(AgentChannel,-,-2)})
 exten => s,n,Set(AgentUser=${CUT(AgentChannel,/,2)})
 exten => s,n,NoOp(${AgentUser})
 ; tax-queue agents
 exten => s,n,GotoIf($"${AgentUser}" = "2488-tessmanl"?:macdonap)
 exten => s,n,Set(AgentPenalty=1)
 exten => s,n,Goto(agent-login,s,1)
 exten => s,n(macdonap),GotoIf($["${AgentUser}" = "2488-macdonap"]?:chengb)
 exten => s,n,Goto(agent-login,s,1)
 exten => s,n(chengb),GotoIf($["${AgentUser}" = "2488-chengb"]?:listhael)
 exten => s,n,Set(AgentPenalty=2)
 exten => s,n,Goto(agent-login,s,1)
 exten => s,n(listhael),GotoIf($["${AgentUser}" = "2488-listhael"]?:nguyent)
 exten => s,n,Set(AgentPenalty=3)
 exten => s,n,Goto(agent-login,s,1)
 exten => s,n(nguyent),GotoIf($["${AgentUser}" = "2488-nguyent"]?:NonAgentStart)
 exten => s,n,Set(AgentPenalty=4)
 exten => s,n,Goto(agent-login,s,1)
 exten => s,n(NonAgentStart),BackGround(call-processors/2488)

Example 2

The chan_agent doesn't behave well in some cases. Here is a large dialplan to emulate agent channels and even more. It uses dynamic members, and requires CVS after about Feb 2005. Under the 'slacker' exten, I have a res_perl sub that notifies the agent of their logoff. You can nuke this or replace it with something to suit you.

Features:
  • Will not try agents already on any call (depends on CVS ChanIsAvail() 's' option)
  • Login will give option to kick off same agent already logged in on another channel
  • Logout/Login auto-detect
  • Auto-logoff of agents not answering calls
  • Agent can request more wrap-up time (accurate wrap-ups depend on CVS app_queue with state thread)
  • Availability toggle (todo: use new PauseQueueMember app)
This does not implement passwords, but that's easy enough to add with some DBget() and another Read();

Create an entry for each agent like so:

/AGENT/valid/n == (colon-separated queues)
e.g.
cli> database put AGENT valid/101 tech:noc:hosting
cli> database put AGENT valid/102 sales:service
cli> database put AGENT valid/103 billing


; magic to circumvent chan_agent's brokenness
; agents are stored in the astdb AGENT tree

[agent_features]
; include this context somewhere, and map buttons to it
exten => 8301,1,Goto(agent_login,s,1);
exten => 8302,1,Goto(agent_avail,s,1);
exten => 8303,1,Goto(agent_moretime,s,1);

[agent_call]
; Local/${agent}@agent_call are dynamic members
; added to semicolon-separated queues in AGENT/valid tree
exten => _XXX,1,SetVar(agent=${EXTEN});
exten => _XXX,2,ResponseTimeout(1);
exten => _XXX,3,DBget(mychan=AGENT/${agent}/onChannel);
exten => _XXX,4,Goto(check,1);
exten => _XXX,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,M(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(notify_slacker:${agent}:${reason});
exten => slacker,n,Goto(agent_login,logout,1); log them off
;
exten => busy,1,Busy();
exten => t,1,Hangup();
exten => i,1,Congestion();

[macro-agent_answered]
exten => s,1,DBdel(AGENT/${ARG1}/bounces);
exten => s,2,DBdel(AGENT/${ARG1}/rejects);

[agent_avail]
; 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,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,3,UserEvent(AgentAvailable,Agent: ${agent});
; not logged in
exten => login,1,Playback(xm/agent-avail-login);
exten => login,2,Goto(agent_login,s,1)
;
exten => t,1,Hangup();
exten => i,1,Playback(xm/agent-avail-error);

[agent_moretime]
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(xm/agent-moretime-ok);
exten => s,10,UserEvent(AgentMoreTime,Agent: ${agent}, Until: $[${EPOCH} + ${WRAPUPTIME}]);
;
exten => login,1,Playback(xm/agent-moretime-login);
exten => login,2,Goto(agent_login,s,1)
;
exten => t,1,Hangup();
exten => i,1,Playback(xm/agent-moretime-error);

[agent_login]
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,xm/agent-login-user);
exten => start,2,Goto(verify,1);
exten => dupe,1,Read(continue,xm/agent-login-already-continue,1);
exten => dupe,2,GotoIf($["${continue}" = "1"]?3:4);
exten => dupe,3,Goto(logout,1);
exten => dupe,4,Read(agent,xm/agent-login-user);
exten => dupe,5,Goto(verify,1);
exten => bad,1,Read(agent,xm/agent-login-incorrect);
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=AGENT/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(xm/agent-login-ok);
exten => login,12,Goto(t,1);
; logout, leave configured queues
exten => logout,1,DBget(queues=AGENT/valid/${agent});
exten => logout,2,SetVar(i=1);
exten => logout,3,Cut(j=queues,:,${i});
exten => logout,4,While($[${LEN(${j})} > 0]);exten => logout,5,RemoveQueueMember(${j},Local/${agent}@agent_call);
exten => logout,6,SetVar(i=$[${i} + 1]);
exten => logout,7,Cut(j=queues,:,${i});
exten => logout,8,EndWhile;
exten => logout,9,DBget(agentchan=AGENT/${agent}/onChannel);
;exten => logout,n,DBdel(AGENT/channelHasAgent/${mychan});
exten => logout,10,DBdel(AGENT/channelHasAgent/${agentchan});
exten => logout,11,DBdeltree(AGENT/${agent});
exten => logout,12,Playback(xm/agent-login-off,skip);
exten => logout,13,GotoIf($["${continue}" = "1"]?login,1); logging off a dupe, go back
exten => logout,14,Goto(t,1);
;
exten => t,1,Hangup;
exten => i,1,Playback(xm/agent-login-error);


You'll need these sound files (allison edits):
http://orbit.xmission.com/~kevin/agent-sounds.tar.gz

Comments

Comments Filter
222

333Re: Re: notify_slacker.pl script?

by xkev, Monday 21 of March, 2005 [20:22:50 UTC]
there's a few typos in this code, but you get the jist of it.
222

333Re: notify_slacker.pl script?

by xkev, Monday 21 of March, 2005 [20:21:51 UTC]
sub notify_slacker() {
       require IOSocket
INET;

       my $myip = "5.6.7.8";
       my $bothost = "1.2.3.4";
       my $botport = "8765";
      
       my ($chan,$you,$reason) = @_;

       my $astchan = asterisk_get_channel_by_name($chan);
       my $agentchan = asterisk_dbget($astchan, "AGENT", "$you/onChannel");
       my $reg = (split '/', $agentchan)1;
       my $epoch = time;
       my $sock;

       $sock = IOSocket
INET->new(Proto => 'udp', PeerAddr => $mybothost, PeerPort => $mybotport, 
LocalPort => 65535);
       $sock->send("Agent '
B$you
B' $reason:
Bagent set unavailable
B");
       $sock->close;

       return 0 unless (defined $reg && length($reg));

       $sock = IOSocket
INET->new(Proto => 'udp', PeerAddr => $myproxy, PeerPort => 5060, LocalPort => 65535);
       my $message = 
               "MESSAGE sip:$reg\@$myip:5060 SIP/2.0\r\n".
               "Via: SIP/2.0/UDP $myip:65535\r\n".
               "From: \"pbx\" \r\n".
               "To: sip:$reg\@pbx\r\n".
               "CSeq: 1 MESSAGE\r\n".
               "Call-ID: $epoch-slacker\@$myip\r\n".
               "User-Agent: Slacker Notifier\r\n".
               "Max-Forwards: 70\r\n".
               "Content-Type: text/plain\r\n".
               "Content-Length: 10\r\n\r\n".
               "logged off";
       $sock->send($message);
       $sock->close();
       return 0;
}

222

333notify_slacker.pl script?

by agentcooper, Wednesday 09 of March, 2005 [18:44:35 UTC]
Wow, very impressive. It's going to take me a while to go through this.

You mention the Res_Perl module and your use of the notify_slacker routine, but there is no code for this. Would you be willing to put that up as well?

Great job.

John