Motivated from Agents without agent channel I build a script to make agents available without agent channels working in Asterisk > 1.4.
Features:
- agent login / logout
- agent pause /unpause
- answering a call sets agent in wrapuptime
- agent can manuell exit wrapuptime when calling context agent_stats
- automatic agent logoff after bouncing or rejecting calls
- support for Flash Operation Panel (op_astdb.cfg)
put agents in queues like this on Asterisk CLI:
CLI> database put AGENT queues/111 consumer-pc:consumer-nb:akku
CLI> database put AGENT queues/222 consumer-pc:consumer-nb:akku
setting up some global variables
globals {
// how often a gent trys to login with wrong agentnumber
LoginAttempts = 2;
// how long a agent phone is ringing befor jump to the other agent (set timeout in queues.conf to 0)
CallSeconds = 10;
// how often a agent can bounce a call bevor he gets kicked of the queue
AllowedBounces = 3;
// how often a agent can reject a call bevor he gets kicked of the queue
AllowedRejects = 3;
}
sample extensions for agents to login/logout or set status of agent (pause unpause avail)
change extensionsto what ever you want
context agent_stuff {
**1 => { goto agent_loginout|s|1; }
**2 => { goto agent_stat|s|1; }
}
context for agents to login or logout if they already logged in
context agent_loginout {
s => {
Wait(1);
Answer();
Set(TIMEOUT(response)=5);
Set(mychan=${CUT(CHANNEL,-,1)});
// if on this extension a agent already logged in -> logout
if( ${DB_EXISTS(AGENT/channelHasAgent/${mychan})} )
{
Set(agent=${DB(AGENT/channelHasAgent/${mychan})});
NoOp(Agent schon angemeldet);
&AgentLogout(${agent});
}
// if not go ahead with login
else
{
NoOp(Kein Agent an Nebenstelle angemeldet);
SET(LOOPCOUNT=1);
SET(soundfile="agent-user");
begin: Read(agent,${soundfile});
// when agent is already logged in on other extensions -> logout
if( ${DB_EXISTS(AGENT/${agent}/onChannel)})
{
NoOp(Agent ist an Nebenstelle ${alreadyon} angemeldet);
Playback(custom/agent-alreadyon-getoff);
&AgentLogout(${agent});
}
// go ahead with agent login
else
{
// if agent is member of queue
if( ${DB_EXISTS(AGENT/queues/${agent})})
{
&AgentLogin(${mychan},${agent});
}
// if agent is not member of queue (give him ${LoginAttemps})
else
{
NoOp(Agent ist kein Agent);
while (${LOOPCOUNT} < ${LoginAttempts}) {
LOOPCOUNT=${LOOPCOUNT}+1;
SET(soundfile="agent-incorrect");
goto begin;
}
Playback(pbx-invalid);
}
}
goto t|1;
}
}
t => Hangup;
i => Playback(pbx-invalid);
}
context to set Status of agent (pause or avail)
context agent_stat {
s => {
Wait(1);
Answer();
Set(TIMEOUT(response)=5);
Set(mychan=${CUT(CHANNEL,-,1)});
// if valid agent is on extension logged in
if( ${DB_EXISTS(AGENT/channelHasAgent/${mychan})} )
{
Set(agent=${DB(AGENT/channelHasAgent/${mychan})});
&AgentStat(${agent});
}
// if no agent is logged in on extension
else
{
NoOp (Error);
goto i|1;
}
goto t|1;
}
t => Hangup;
i => Playback(pbx-invalid);
}
context to call agent
context agent_call {
_X. => {
Set(agent=${EXTEN});
Set(mychan=${DB(AGENT/${agent}/onChannel)});
if(${DB(AGENT/${agent}/stat)} = avail )
{
Set(_ALERT_INFO=queue);
UserEvent(QueueCall|Queue: ${queue});
Dial(${mychan}|${CallSeconds}|M(AgentAnswered^${agent}));
&AgentSlacker(${agent},${DIALSTATUS});
}
else
{
Busy();
goto t|1;
}
}
t => Hangup();
i => Congestion();
}
macro for agents to login
macro AgentLogin(mychan,agent) {
Set(queues=${DB(AGENT/queues/${agent})});
Set(DB(AGENT/channelHasAgent/${mychan})=${agent});
Set(DB(AGENT/${agent}/onChannel)=${mychan});
Set(DB(AGENT/${agent}/bounces)=0);
Set(DB(AGENT/${agent}/rejects)=0);
Set(DB(AGENT/${agent}/stat)=avail);
Set(i=1);
Set(j=${CUT(queues,:,${i})});
NoOP(Start);
while ( ${LEN(${j})} > 0 ) {
AddQueueMember(${j},Local/${agent}@agent_call);
Set(i=$[${i} + 1]);
Set(j=${CUT(queues,:,${i})});
}
// UserEvent for Flash Operation Panel
UserEvent(ASTDB|Channel: ${mychan}^Family: AgentAvail^Value: ${agent} avail);
Playback(agent-loginok);
}
macro for agents to logout
macro AgentLogout(agent) {
Set(queues=${DB(AGENT/queues/${agent})});
Set(i=1);
Set(j=${CUT(queues,:,${i})});
while ( ${LEN(${j})} > 0 ) {
RemoveQueueMember(${j},Local/${agent}@agent_call);
Set(i=$[${i} + 1]);
Set(j=${CUT(queues,:,${i})});
}
Set(agentchan=${DB(AGENT/${agent}/onChannel)});
Set(oldval=${DB_DELETE(AGENT/channelHasAgent/${agentchan})});
DBdeltree(AGENT/${agent});
Set(number=${CUT(agentchan,/,2)});
UserEvent(ASTDB|Channel: ${agentchan}^Family: AgentLogout^Value: ${number});
Playback(agent-loggedoff);
Hangup;
}
macro to put agent in stat pause and avail
macro AgentStat(agent) {
set(stat=${DB(AGENT/${agent}/stat)});
switch (${stat})
{
case pause: // if agent is in pause set him to avail
set(DB(AGENT/${agent}/stat)=avail);
UnPauseQueueMember(|Local/${agent}@agent_call);
UserEvent(ASTDB|Channel: ${mychan}^Family: AgentAvail^Value: ${agent} avail);
Playback(custom/online);
break;
case avail: // if agent is avail set him in pause
set(DB(AGENT/${agent}/stat)=pause);
PauseQueueMember(|Local/${agent}@agent_call);
UserEvent(ASTDB|Channel: ${mychan}^Family: AgentPause^Value: ${agent} pause);
Playback(custom/offline);
break;
case wrapup: // if agent is in wrapup set him to avail
set(DB(AGENT/${agent}/stat)=avail);
UserEvent(ASTDB|Channel: ${mychan}^Family: AgentAvail^Value: ${agent} avail);
Playback(custom/online);
break;
}
}
this macro is called from context [agent_call]
it handle agents who are not answering calls or reject calls
after ${AllowedBounce} times, a agent get kicked of the queues
after ${AllowedRejects} times, a agent get kicked of the queues
macro AgentSlacker(agent,dialstatus) {
switch (${dialstatus})
{
case BUSY: // agent rejects a call
set(rejects=${DB(AGENT/${agent}/rejects)});
set(rejects=$[${rejects} + 1]);
set(DB(AGENT/${agent}/rejects)=${rejects});
if(${rejects} >= ${AllowedRejects}) {
set(reason= 3 Rufe abgewiesen );
break;
}
else Hangup;
break;
case NOANSWER: // agent bounced a call
set(bounces=${DB(AGENT/${agent}/bounces)});
set(bounces=$[${bounces} + 1]);
set(DB(AGENT/${agent}/bounces)=${bounces});
if(${bounces} >= ${AllowedBounces}) {
set(reason= 3 Rufe nicht beantwortet);
break;
}
else Hangup;
break;
}
UserEvent(AgentSlacker,Agent: ${agent});
NoOp(agent ${agent} is a slacker: ${reason});
Set(queues=${DB(AGENT/queues/${agent})});
Set(i=1);
Set(j=${CUT(queues,:,${i})});
while ( ${LEN(${j})} > 0 ) {
RemoveQueueMember(${j},Local/${agent}@agent_call);
Set(i=$[${i} + 1]);
Set(j=${CUT(queues,:,${i})});
}
Set(agentchan=${DB(AGENT/${agent}/onChannel)});
Set(oldval=${DB_DELETE(AGENT/channelHasAgent/${agentchan})});
DBdeltree(AGENT/${agent});
}
this macro is called when a agent is answering a call
it sets ${AllowedBounces} and ${AllowedRejects} back to 0
the agent is set to stat wrapup and can not be called even he has finish the called
to get stat avail again call macro AgentStat
macro AgentAnswered(agent) {
set(DB(AGENT/${ARG1}/bounces)=0);
set(DB(AGENT/${ARG1}/rejects)=0);
set(DB(AGENT/${ARG1}/stat)=wrapup);
set(mychan=${DB(AGENT/${ARG1}/onChannel)});
UserEvent(AgentUnavailable|Agent: ${ARG1});
UserEvent(ASTDB|Channel: ${mychan}^Family: AgentWrapup^Value: ${agent} Nacharbeit);
}
the following file sets up the Flash Operation Panel to shpw status of Agents
op_astdb.cfg:
[AgentAvail]
settext= ${value}
flip=1
fopledcolor=0xD0d020
setlabel=Agent
[AgentLogout]
settext= ${value}
flip=1
fopledcolor=0x00A000
setlabel= ${value}
[AgentWrapup]
settext= ${value}
fopledcolor=0xA01020
[AgentPause]
settext= ${value}
flip=1
fopledcolor=0x0000c0
[AgentSlacker]
settext= ${value}
flip=1
fopledcolor=0x000000