Upgrade 3CX to v18 and get it hosted free!

Agents without agent channel Asterisk 1.4 AEL

Author image

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


Article Reviews

Write a Review

Your email address will not be published. Required fields are marked *

Required Field. Minimum 5 characters.

Required Field. Minimum 5 characters, maximum 50.

Required field.There is an error with this field.

Required Field.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

There are no reviews for this article. Be the first one to write a review.
Get 3CX - Absolutely Free!
Link up your team and customers Phone System Live Chat Video Conferencing

Hosted or Self-managed. Up to 10 users free forever. No credit card. Try risk free.

3CX
A 3CX Account with that email already exists. You will be redirected to the Customer Portal to sign in or reset your password if you've forgotten it.