From dbddd24443348c19c79e9d85f40d2aa429d0d885 Mon Sep 17 00:00:00 2001 From: russell Date: Fri, 16 Mar 2007 01:41:00 +0000 Subject: Making these documentation changes in the 1.4 branch upset various people, so these chanes will only be done in the trunk. git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.4@58955 f38db490-d61c-443f-a65b-d21fe96a405b --- doc/queues-with-callback-members.txt | 524 +++++++++++++++++++++++++++++++++++ 1 file changed, 524 insertions(+) create mode 100644 doc/queues-with-callback-members.txt (limited to 'doc/queues-with-callback-members.txt') diff --git a/doc/queues-with-callback-members.txt b/doc/queues-with-callback-members.txt new file mode 100644 index 000000000..3a2c3e785 --- /dev/null +++ b/doc/queues-with-callback-members.txt @@ -0,0 +1,524 @@ + +Setting up Call Queues -- A Tutorial + +Pardon, but the dialplan in this tutorial will be expressed +in AEL, the new Asterisk Extension Language. If you are +not used to its syntax, we hope you will find it to some +degree intuitive. If not, there are documents explaining +its syntax and constructs. + + +====== Configuring Call Queues + +First of all, set up call queues in queue.conf + +Here is an example: + + =========== queues.conf =========== + | ; Cool Digium Queues | + | [general] | + | persistentmembers = yes | + | | + | ; General sales queue | + | [sales-general] | + | music=default | + | context=sales | + | strategy=ringall | + | joinempty=strict | + | leavewhenempty=strict | + | | + | ; Customer service queue | + | [customerservice] | + | music=default | + | context=customerservice | + | strategy=ringall | + | joinempty=strict | + | leavewhenempty=strict | + | | + | ; Support dispatch queue | + | [support-dispatch] | + | music=default | + | context=dispatch | + | strategy=ringall | + | joinempty=strict | + | leavewhenempty=strict | + =================================== + +In the above, we have defined 3 separate calling queues: +sales-general, customerservice, and support-dispatch. + +Please note that the sales-general queue specifies a +context of "sales", and that customerservice specifies the +context of "customerservice", and the support-dispatch +queue specifies the context "dispatch". These three +contexts must be defined somewhere in your dialplan. +We will show them after the main menu below. + + +In the [general] section, specifying the persistentmembers=yes, +will cause the agent lists to be stored in astdb, and +recalled on startup. + +The strategy=ringall will cause all agents to be dialed +together, the first to answer is then assigned the incoming +call. + +"joinempty" set to "strict" will keep incoming callers from +being placed in queues where there are no agents to take calls. +The Queue() application will return, and the dial plan can +determine what to do next. + +If there are calls queued, and the last agent logs out, the +remaining incoming callers will immediately be removed from +the queue, and the Queue() call will return, IF the "leavewhenempty" is +set to "strict". + + +===================================== +| Routing incoming Calls to Queues | +===================================== + + +Then in extensions.ael, you can do these things: + +================ The Main Menu + +At Digium, incoming callers are sent to the "mainmenu" context, where they +are greeted, and directed to the numbers they choose... + +context mainmenu { + + includes { + digium; + queues-loginout; + } + + 0 => goto dispatch|s|1; + 2 => goto sales|s|1; + 3 => goto customerservice|s|1; + 4 => goto dispatch|s|1; + + s => { + Ringing(); + Wait(1); + Set(attempts=0); + Answer(); + Wait(1); + Background(digium/ThankYouForCallingDigium); + Background(digium/YourOpenSourceTelecommunicationsSupplier); + WaitExten(0.3); + repeat: + Set(attempts=$[${attempts} + 1]); + Background(digium/IfYouKnowYourPartysExtensionYouMayDialItAtAnyTime); + WaitExten(0.1); + Background(digium/Otherwise); + WaitExten(0.1); + Background(digium/ForSalesPleasePress2); + WaitExten(0.2); + Background(digium/ForCustomerServicePleasePress3); + WaitExten(0.2); + Background(digium/ForAllOtherDepartmentsPleasePress4); + WaitExten(0.2); + Background(digium/ToSpeakWithAnOperatorPleasePress0AtAnyTime); + if( ${attempts} < 2 ) { + WaitExten(0.3); + Background(digium/ToHearTheseOptionsRepeatedPleaseHold); + } + WaitExten(5); + if( ${attempts} < 2 ) goto repeat; + Background(digium/YouHaveMadeNoSelection); + Background(digium/ThisCallWillBeEnded); + Background(goodbye); + Hangup(); + } +} + + +============= The Contexts referenced from the queues.conf file + + + +context sales { + + 0 => goto dispatch|s|1; + 8 => Voicemail(${SALESVM}); + + s => { + Ringing(); + Wait(2); + Background(digium/ThankYouForContactingTheDigiumSalesDepartment); + WaitExten(0.3); + Background(digium/PleaseHoldAndYourCallWillBeAnsweredByOurNextAvailableSalesRepresentative); + WaitExten(0.3); + Background(digium/AtAnyTimeYouMayPress0ToSpeakWithAnOperatorOr8ToLeaveAMessage); + Set(CALLERID(name)=Sales); + Queue(sales-general|t); + Set(CALLERID(name)=EmptySalQ); + goto dispatch|s|1; + Playback(goodbye); + Hangup(); + } +} + +Please note that there is only one attempt to queue a call in the sales queue. All sales agents that +are logged in will be rung. + + +context customerservice { + + 0 => { + SetCIDName(CSVTrans); + goto dispatch|s|1; + } + 8 => Voicemail(${CUSTSERVVM}); + + s => { + Ringing(); + Wait(2); + Background(digium/ThankYouForCallingDigiumCustomerService); + WaitExten(0.3); + notracking: + Background(digium/PleaseWaitForTheNextAvailableCustomerServiceRepresentative); + WaitExten(0.3); + Background(digium/AtAnyTimeYouMayPress0ToSpeakWithAnOperatorOr8ToLeaveAMessage); + Set(CALLERID(name)=Cust Svc); + Set(QUEUE_MAX_PENALTY=10); + Queue(customerservice|t); + Set(QUEUE_MAX_PENALTY=0); + Queue(customerservice|t); + Set(CALLERID(name)=EmptyCSVQ); + goto dispatch|s|1; + Background(digium/NoCustomerServiceRepresentativesAreAvailableAtThisTime); + Background(digium/PleaseLeaveAMessageInTheCustomerServiceVoiceMailBox); + Voicemail(${CUSTSERVVM}); + Playback(goodbye); + Hangup(); + } +} + +Note that calls coming into customerservice will first be try to queue +calls to those agents with a QUEUE_MAX_PENALTY of 10, and if none are available, +then all agents are rung. + + +context dispatch +{ + + s => { + Ringing(); + Wait(2); + Background(digium/ThankYouForCallingDigium); + WaitExten(0.3); + Background(digium/YourCallWillBeAnsweredByOurNextAvailableOperator); + Background(digium/PleaseHold); + Set(QUEUE_MAX_PENALTY=10); + Queue(dispatch|t); + Set(QUEUE_MAX_PENALTY=20); + Queue(dispatch|t); + Set(QUEUE_MAX_PENALTY=0); + Queue(dispatch|t); + Background(digium/NoOneIsAvailableToTakeYourCall); + Background(digium/PleaseLeaveAMessageInOurGeneralVoiceMailBox); + Voicemail(${DISPATCHVM}); + Playback(goodbye); + Hangup(); + } +} + +And in the dispatch context, first agents of priority 10 are tried, then +20, and if none are available, all agents are tried. + +Notice that a common pattern is followed in each of the three queue contexts: + +First, you set QUEUE_MAX_PENALTY to a value, then you call +Queue(,option,... (see the documentation for the Queue application)); + +In the above, note that the "t" option is specified, and this allows the +agent picking up the incoming call the luxury of transferring the call to +other parties. + +The purpose of specifying the QUEUE_MAX_PENALTY is to develop a set of priorities +amongst agents. By the above usage, agents with lower number priorities will +be given the calls first, and then, if no-one picks up the call, the QUEUE_MAX_PENALTY +will be incremented, and the queue tried again. Hopefully, along the line, someone +will pick up the call, and the Queue application will end with a hangup. + +The final attempt to queue in most of our examples sets the QUEUE_MAX_PENALTY +to zero, which means to try all available agents. + + +========================================= +| Assigning agents to Queues | +========================================= + +In this example dialplan, we want to be able to add and remove agents to +handle incoming calls, as they feel they are available. As they log in, +they are added to the queue's agent list, and as they log out, they are +removed. If no agents are available, the queue command will terminate, and +it is the duty of the dialplan to do something appropriate, be it sending +the incoming caller to voicemail, or trying the queue again with a higher +QUEUE_MAX_PENALTY. + +Because a single agent can make themselves available to more than one queue, +the process of joining multiple queues can be handled automatically by the +dialplan. + + +================= Agents Log In and Out + + +context queues-loginout +{ + 6092 => { + Answer(); + Read(AGENT_NUMBER,agent-enternum); + VMAuthenticate(${AGENT_NUMBER}@default,s); + Set(queue-announce-success=1); + goto queues-manip,I${AGENT_NUMBER},1; + } + + 6093 => { + Answer(); + Read(AGENT_NUMBER,agent-enternum); + Set(queue-announce-success=1); + goto queues-manip,O${AGENT_NUMBER},1; + } +} + + +In the above contexts, the agents dial 6092 to log into their queues, +and they dial 6093 to log out of their queues. The agent is prompted +for their agent number, and if they are logging in, their passcode, +and then they are transferred to the proper extension in the +queues-manip context. The queues-manip context does all the +actual work: + + +context queues-manip { + + // Raquel Squelch + _[IO]6121 => { + &queue-addremove(dispatch,10); + &queue-success(); + } + + // Brittanica Spears + _[IO]6165 => { + &queue-addremove(dispatch,20); + &queue-success(); + } + + // Rock Hudson + _[IO]6170 => { + &queue-addremove(sales-general,10); + &queue-addremove(customerservice,20); + &queue-addremove(dispatch,30); + &queue-success(); + } + + // Saline Dye-on + _[IO]6070 => { + &queue-addremove(sales-general,20); + &queue-addremove(customerservice,30); + &queue-addremove(dispatch,30); + &queue-success(); + } +} + +In the above extensions, note that the queue-addremove macro is used +to actually add or remove the agent from the applicable queue, +with the applicable priority level. Note that agents with a +priority level of 10 will be called before agents with levels +of 20 or 30. + +In the above example, Raquel will be dialed first in the dispatch +queue, if she has logged in. If she is not, then the second call of +Queue() with priority of 20 will dial Brittanica if she is present, +otherwise the third call of Queue() with MAX_PENALTY of 0 will +dial Rock and Saline simultaneously. + +Also note that Rock will be among the first to be called in the sales-general +queue, and among the last in the dispatch queue. As you can see in +main menu, the callerID is set in the main menu so they can tell +which queue incoming calls are coming from. + +The call to queue-success() gives some feedback to the agent +as they log in and out, that the process has completed. + +macro queue-success() +{ + if( ${queue-announce-success} > 0 ) + { + switch(${MACRO_EXTEN:0:1}) + { + case I: + Playback(agent-loginok); + Hangup(); + case O: + Playback(agent-loggedoff); + Hangup(); + } + } +} + + +The queue-addremove macro is defined in this manner: + +macro queue-addremove(queuename,penalty) +{ + switch(${MACRO_EXTEN:0:1}) + { + case I: // Login + { + AddQueueMember(${queuename},Local/${MACRO_EXTEN:1}@agents,${penalty}); + } + case O: // Logout + { + RemoveQueueMember(${queuename},Local/${MACRO_EXTEN:1}@agents); + } + case P: // Pause + { + PauseQueueMember(${queuename},Local/${MACRO_EXTEN:1}@agents); + } + case U: // Unpause + { + UnpauseQueueMember(${queuename},Local/${MACRO_EXTEN:1}@agents); + } + default: // Invalid + { + Playback(invalid); + } + } +} + +Basically, it uses the first character of the MACRO_EXTEN variable, to determine the +proper actions to take. In the above dial plan code, only the cases I or O are used, +which correspond to the Login and Logout actions. + + +======================================================= +| Controlling The Way Queues Call the Agents | +======================================================= + +Notice in the above, that the commands to manipulate agents in queues have +"@agents" in their arguments. This is a reference to the agents context: + +context agents +{ + // General sales queue + 8010 => + { + Set(QUEUE_MAX_PENALTY=10); + Queue(sales-general|t); + Set(QUEUE_MAX_PENALTY=0); + Queue(sales-general|t); + Set(CALLERID(name)=EmptySalQ); + goto dispatch|s|1; + } + // Customer Service queue + 8011 => + { + Set(QUEUE_MAX_PENALTY=10); + Queue(customerservice|t); + Set(QUEUE_MAX_PENALTY=0); + Queue(customerservice|t); + Set(CALLERID(name)=EMptyCSVQ); + goto dispatch|s|1; + } + 8013 => + { + Dial(iax2/sweatshop/9456@from-ecstacy); + + Set(CALLERID(name)=EmptySupQ); + Set(QUEUE_MAX_PENALTY=10); + Queue(support-dispatch,t); + Set(QUEUE_MAX_PENALTY=20); + Queue(support-dispatch,t); + Set(QUEUE_MAX_PENALTY=0); // means no max + Queue(support-dispatch,t); + goto dispatch|s|1; + } + 6121 => &callagent(${RAQUEL}); + 6165 => &callagent(${SPEARS}); + 6170 => &callagent(${ROCK}); + 6070 => &callagent(${SALINE}); +} + +In the above, the variables ${RAQUEL}, etc stand for +actual devices to ring that person's +phone (like Zap/37). + +The 8010, 8011, and 8013 extensions are purely for transferring +incoming callers to queues. For instance, a customer service +agent might want to transfer the caller to talk to sales. The +agent only has to transfer to extension 8010, in this case. + +Here is the callagent macro, note that if a person in the +queue is called, but does not answer, then they are automatically +removed from the queue. + +macro callagent(device) +{ + if( ${GROUP_COUNT(${MACRO_EXTEN}@agents)}=0 ) + { + Set(OUTBOUND_GROUP=${MACRO_EXTEN}@agents); + Dial(${device}|300|t); + switch(${DIALSTATUS}) + { + case BUSY: + Busy(); + break; + case NOANSWER: + Set(queue-announce-success=0); + goto queues-manip|O${MACRO_EXTEN}|1; + default: + Hangup(); + break; + } + } + else + { + Busy(); + } +} + +In the callagent macro above, the ${MACRO_EXTEN} will +be 6121, or 6165, etc, which is the extension of the agent. + +The use of the GROUP_COUNT, and OUTBOUND_GROUP follow this line +of thinking. Incoming calls can be queued to ring all agents in the +current priority. If some of those agents are already talking, they +would get bothersome call-waiting tones. To avoid this inconvenience, +when an agent gets a call, the OUTBOUND_GROUP assigns that +conversation to the group specified, for instance 6171@agents. +The ${GROUP_COUNT()} variable on a subsequent call should return +"1" for that group. If GROUP_COUNT returns 1, then the busy() +is returned without actually trying to dial the agent. + +================ Pre Acknowledgement Message + +If you would like to have a pre acknowledge message with option to reject the message +you can use the following dialplan Macro as a base with the 'M' dial argument. + +[macro-screen] +exten=>s,1,Wait(.25) +exten=>s,2,Read(ACCEPT|screen-callee-options|1) +exten=>s,3,Gotoif($[${ACCEPT} = 1] ?50) +exten=>s,4,Gotoif($[${ACCEPT} = 2] ?30) +exten=>s,5,Gotoif($[${ACCEPT} = 3] ?40) +exten=>s,6,Gotoif($[${ACCEPT} = 4] ?30:30) +exten=>s,30,Set(MACRO_RESULT=CONTINUE) +exten=>s,40,Read(TEXTEN|custom/screen-exten|) +exten=>s,41,Gotoif($[${LEN(${TEXTEN})} = 3]?42:45) +exten=>s,42,Set(MACRO_RESULT=GOTO:from-internal^${TEXTEN}^1) +exten=>s,45,Gotoif($[${TEXTEN} = 0] ?46:4) +exten=>s,46,Set(MACRO_RESULT=CONTINUE) +exten=>s,50,Playback(after-the-tone) +exten=>s,51,Playback(connected) +exten=>s,52,Playback(beep) + +================ Caveats + +In the above examples, some of the possible error checking has been omitted, +to reduce clutter and make the examples clearer. + -- cgit v1.2.3