Howto: Dynamic Agents login/logout + Auto loggoff

-----------------This is a work in progress-------------------

Picture the situation: a rep walks in to the office, pickups up the phone and loges in. They are automatically logged in to their relevant queue. After ten minutes of inactivity they are automatically logged out. Of course the ten minutes is measured from the last activity they did (not form the initial log in).

Advantages:

*Less pressure on the queue as only available agents are in the queue (as apposed to every phone in the call center)

*Ability to track agents active availability, and perhaps only pay them for the time that they where actually logged in

*Improved customer service by auto send callers to voice-mail if there are no agent in the queue (no point in keeping them on hold if there is no one available to answer the call anyway)

*Additional security by not allowing anyone to place a call without logging in first (yet not driving your agents crazy by requiring them to dial a code before every call)

 

------code and instructions below-----------------

Please remember that this is alpha code at best. Test it seriously before using in a production environment

 

 

first add this line to extensions_custom.conf: [code] #include extensions_users.conf [/code] then create a file called extensions_users.conf and add the following: [code] [userlogon] exten => s,1,Read(AMPUSER|vm-extension) ;get users desired extension exten => s,n,Set(DB(AMPUSER/${AMPUSER}/device)=); logoff any other devices exten => s,n,Macro(user-logon,${AMPUSER}) ;log user in to device exten => s,n,Macro(setq,${AMPUSER}) exten => s,n,AddQueueMember(${q}|Local/${AMPUSER}@from-internal) ;add user to queue exten => s,n,Set(DB(AMPUSER/${AMPUSER}/lastactivity)=${EPOCH}) ;set lastactivity flag exten => s,n,Playback(vm-goodbye) exten => s,n,Hangup [userlogoff] exten => s,1,Set(dev=${DB(DEVICE/${CALLERID(num)}/user}) ;set the device user is logged in to exten => s,n,Macro(user-logoff,) ;log off user exten => s,n,Macro(setq,${dev}) exten => s,n,RemoveQueueMember(${q}|Local/${dev}@from-internal) ;remove user from queue ;exten => s,n,Set(DB(DEVICE/${dev}/user)=none) ;clean-up whatever macro-user-logoff didnt do exten => s,n,Set(DB(AMPUSER/${dev}/lastactivity)=none) ;clear lastactivity flag exten => s,n,Playback(vm-goodbye) exten => s,n,Hangup exten => autologoff,1,Goto(s,1) [macro-setq] exten => s,1,Noop(Macro Set Queue Number for ${ARG1}) exten => s,n,Set(AMPUSER=${IF($["${AMPUSER}" = ""]?${ARG1}:${AMPUSER})}) exten => s,n,Set(q=${IF($[$["${AMPUSER}" >= "800"] & $["${AMPUSER}" <= "899"]]?10:${q})}) exten => s,n,Set(q=${IF($[$["${AMPUSER}" >= "900"] & $["${AMPUSER}" <= "989"]]?11:${q})}) exten => s,n,Set(q=${IF($[$["${AMPUSER}" >= "990"] & $["${AMPUSER}" <= "999"]]?12:${q})}) exten => s,n,Set(q=${IF($[$["${AMPUSER}" >= "700"] & $["${AMPUSER}" <= "710"]]?5:${q})}) [users] exten => *46,1,Gotoif($["${DB(DEVICE/${CALLERID(num)}/user)}" = "none"]?dial:nouser,1) exten => *46,n(dial),Dial(Local/${EXTEN}@from-internal) exten => *47,1,Dial(Local/${EXTEN}@from-internal) exten => _.,1,Set(USER=${DB(DEVICE/${CALLERID(num)}/user)}) exten => _.,n,Noop(*******ampuser is ${USER}, callerid id ${CALLERID(num)}) exten => _.,n,Gotoif($["${USER}" = "none"]?nouser,1) exten => _.,n,Gotoif($["${USER}" = ""]?nouser,1) exten => _.,n,Set(callnum=${EXTEN}) exten => _.,n,Macro(user-callerid) exten => _.,n,Set(DB(AMPUSER/${USER}/lastactivity)=${EPOCH}) exten => _.,n,Dial(Local/${EXTEN}@from-internal) exten => nouser,1,Playback(vm-incorrect) exten => nouser,n,Hangup exten => h,1,Hangup [/code] Next add the following to extensions.conf. This line goes under the first two lines of macro-hangupcall [code] exten => s,n,Set(DB(AMPUSER/${CALLERID(num)}/lastactivity)=${IF($["$LEN(${CALLERID(num)})" <= "4"]?${EPOCH})}) ;set last activity flag for extensions [/code] Next add the following script to /var/lib/asterisk/bin, and save it with the name agent_loggoff.sh (hint: make sure its executable) [code] #!/bin/bash #Run this script every x minutes to logoff dynamicaly addd queue memebers. This script is desinged to work with freepbx's users and devices logoff () { file="$RANDOM.call" echo "Channel: $1" >> "$file" echo "CallerID: system <${2}>" >> "$file" echo "MaxRetries: 6" >> "$file" echo "RetryTime: 2" >> "$file" echo "WaitTime: 30" >> "$file" echo "Context: custom-userlogoff" >> "$file" echo "Extension: autologoff" >> "$file" echo "Priority: 1" >> "$file" echo "Set: SIPADDHEADER=Call-Info: \;answer-after=0" >> "$file" echo "Set: ALERT_INFO=Ring Answer" >> "$file" echo "Set: SIP_URI_OPTIONS=intercom=true" >> "$file" chown asterisk:asterisk "$file" mv $file /var/spool/asterisk/outgoing } #define max idle time maxtime=$2 #define queue to monitor qnum=$1 qmembers=`/usr/sbin/asterisk -rx "queue show $qnum" | awk '/Local/ && /\(dynamic\)/{ print $1,$(NF-2) }'` if ! [ "$qmembers" ]; then exit; fi #no q qmemberss - no need to do anything while read qmembers qidletime; do device=`/usr/sbin/asterisk -rx "database showkey AMPUSER/$(sed 's/@from-internal//' <<< ${qmembers:6})/device" | grep -a device | cut -d":" -f2 | sed 's/ //g' ` dial=`/usr/sbin/asterisk -rx "database showkey DEVICE/$device/dial" | grep -a dial | cut -d":" -f2 | sed 's/ //g' ` lastactivity=`/usr/sbin/asterisk -rx "database showkey AMPUSER/$(sed 's/@from-internal//' <<< ${qmembers:6})/lastactivity" | grep -a lastactivity | cut -d":" -f2 | sed 's/ //g' ` now=`date +%s` #echo TIME $time #echo QIDELTIME $qidletime #echo LASTACTIVITY $lastactivity #echo MAXTIME $maxtime #echo $((now - lastactivity)) # qmembers didnt recive a q call in the past maxtime, check the lastactivity and log him off if (( qidletime >= maxtime )) && (( (now - lastactivity) >= maxtime )); then logoff $qmembers $device; echo "no call for $qidletime, lastactivity is $lastactivity (time now is $time)" exit #qmembers hasnt recived ANY q calls (so he has no qidletime), just check lastactivity and log him off elif [[ $qidletime = *[^0-9]* ]] && (( (now - lastactivity) >= maxtime )); then logoff $qmembers $device; echo "no q calls, lastactivity is $(( $now - $lastactivity))" exit #Do nothing else echo "nothing to do for qmember $qmembers" fi done <<< "$qmembers" [/code] Now for the setup: First of all, you need to be running in FreePBX's users and devices mode. To enable this change the AMPEXTENSIONS setting in /etc/amportal.conf to deviceanduser Second, in order to ensure that users don’t make outgoing calls unless there logged in, you'll need to change the context of the device to "users" (no quotes). Next you'll want to use the Misc Applications module to create a custom destination for logging in and logging out. Click on Misc Applications, click Add Misc Applications. In the Description field add: agent-logon. For feature code I use *46. Set the custom destination to userlogon,s,1. Click submit. Now add another Mics App with the description agent-logoff, Feature code *47, and destination userlogoff,s,1 Now edit /etc/crontab and add the following line: [code] * * * * * root /var/lib/asterisk/bin/agent-logoff.sh 10 600 [/code] Where 10 is the desired queue number and 600 is the desired amount of inactivity after which to logoff an agent (in seconds). To make the changes take affect do: [code] service crond restart [/code] Please note: after you switch the context, you will not be able to place any phone calls without logging in first. Also you will need to edit the macro-setq to match your queues. Last but not least, don't forget to do an amportal restart to ensure permissions are set properly and that asterisk re-reads the configs

 

Last updated: jan, 10 2008 

Is anyone using this in production?

we have the issue of agents forgetting to log off. this solution would solve that.

its not 100% ready for production. you may want to try it and sort out the last remaning bugs

Hi

A slightly more elegamt way of doing of editing extensions.conf for this part:-

“Next add the following to extensions.conf. This line goes under the first two lines of macro-hangupcall”

Is to copy the whole context into the extensions freepbx override conf file, then add the extra lines, and do the editing.

This way, if FreePBX gets updated, it will not overwrite your customisations. Asterisk loads in the first context it sees, and if there is another context with the same name, it is ignored.

Joe

This is exactly what I need. But im having some troubles… I got all the .conf files edited and did everything stated but what do i need to to to get the device in “users”

Also I can not figure out how to login. I used the same setting as the example but I chaged the Queues and time to log off a agent to match the number Im using for my test Queue. I dial the number to log in and it asks for “extension” then i enter a SIP extension i made? I keep getting erroe messages.

by default FreePBX us in extensions mode. To change it to deviceanduser mode you need to edit the amportal.conf file, you need to chagne the setting for AMPEXTENSIONS. It is documented in the amportal.conf file as to what you need to change it to.

Once that is done you’ll need to do a “amportal restart” at the linux command line.

I got the phones to call each other but when i try to log in it says that the phone I am logging into is not available:

– Executing [*46@from-internal:1] NoOp(“SIP/6003-0a0e4aa0”, “Running miscapp 1: agent-logon”) in new stack
– Executing [46@from-internal:2] Goto(“SIP/6003-0a0e4aa0”, “userlogon|s|1”) in new stack
– Goto (userlogon,s,1)
– Executing [s@userlogon:1] Read(“SIP/6003-0a0e4aa0”, “AMPUSER|vm-extension”) in new stack
– <SIP/6003-0a0e4aa0> Playing ‘vm-extension’ (language ‘en’)
– User entered ‘6003’
– Executing [s@userlogon:2] Set(“SIP/6003-0a0e4aa0”, “DB(AMPUSER/6003/device)=”) in new stack
– Executing [s@userlogon:3] Macro(“SIP/6003-0a0e4aa0”, “user-logon|6003”) in new stack
– Executing [s@macro-user-logon:1] Set(“SIP/6003-0a0e4aa0”, “DEVICETYPE=fixed”) in new stack
– Executing [s@macro-user-logon:2] GotoIf(“SIP/6003-0a0e4aa0”, “1?s-FIXED|1”) in new stack
– Goto (macro-user-logon,s-FIXED,1)
– Executing [s-FIXED@macro-user-logon:1] NoOp(“SIP/6003-0a0e4aa0”, “Device is FIXED and cannot be logged into”) in new stack
– Executing [s-FIXED@macro-user-logon:2] Playback(“SIP/6003-0a0e4aa0”, “ha/phone”) in new stack
– <SIP/6003-0a0e4aa0> Playing ‘ha/phone’ (language ‘en’)
– Executing [s-FIXED@macro-user-logon:3] SayDigits(“SIP/6003-0a0e4aa0”, “6003”) in new stack
– <SIP/6003-0a0e4aa0> Playing ‘digits/6’ (language ‘en’)
– <SIP/6003-0a0e4aa0> Playing ‘digits/0’ (language ‘en’)
– <SIP/6003-0a0e4aa0> Playing ‘digits/0’ (language ‘en’)
– <SIP/6003-0a0e4aa0> Playing ‘digits/3’ (language ‘en’)
– Executing [s-FIXED@macro-user-logon:4] Playback(“SIP/6003-0a0e4aa0”, “is-curntly-unavail&vm-goodbye”) in new stack
– <SIP/6003-0a0e4aa0> Playing ‘is-curntly-unavail’ (language ‘en’)
– <SIP/6003-0a0e4aa0> Playing ‘vm-goodbye’ (language ‘en’)
– Executing [s-FIXED@macro-user-logon:5] Hangup(“SIP/6003-0a0e4aa0”, “”) in new stack
== Spawn extension (macro-user-logon, s-FIXED, 5) exited non-zero on ‘SIP/6003-0a0e4aa0’ in macro ‘user-logon’
== Spawn extension (macro-user-logon, s-FIXED, 5) exited non-zero on ‘SIP/6003-0a0e4aa0’
– Remote UNIX connection
– Remote UNIX connection disconnected
athena
CLI>

6003 is the phones extension.

We are using Polycom Phones sip 301 and 330’s

This script has a long way to go! I doubt it is production ready. I hope to see the script moved to php, and improve the FreePBX integration, amongst other things. If you would like to use it in a production environment, I would consider offering a bounty to get it done.

This is exactly what i am looking for. But my question is that of some of the posters:

  1. “Next add the following to extensions.conf. This line goes under the first two lines of macro-hangupcall”, which line did the author mean?

  2. "you’ll need to change the context of the device to “users” ", Device of which file?

  3. Having followed the steps, how do i configure my IP phone to work with it? If i leave out the authentication id and password on my phone, i won’t have the chance to dial *46 to get registered at all since i’m not connected.

Would really appreciate some updates or pointers.

Cheers,
Soulesss