Agents without agent channel Asterisk 1.4 AEL

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








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








Created by: konabi, Last modification: Tue 11 of Sep, 2012 (00:46 UTC) by admin
Please update this page with new information, just login and click on the "Edit" or "Discussion" tab. Get a free login here: Register Thanks! - Find us on Google+