Fix a typo in the non-DEBUG_THREADS version of the recently added DEADLOCK_AVOIDANCE()
[asterisk-bristuff.git] / doc / queues-with-callback-members.txt
blobc58c30a28338915a8966e8bf092fc163444b6fb9
2 Setting up Call Queues -- A Tutorial
4 Pardon, but the dialplan in this tutorial will be expressed
5 in AEL, the new Asterisk Extension Language. If you are 
6 not used to its syntax, we hope you will find it to some
7 degree intuitive. If not, there are documents explaining
8 its syntax and constructs.
11 ======  Configuring Call Queues
13 First of all, set up call queues in queue.conf
15 Here is an example:
17    =========== queues.conf ===========
18    | ; Cool Digium Queues            |
19    | [general]                       |
20    | persistentmembers = yes         |
21    |                                 |
22    | ; General sales queue           |
23    | [sales-general]                 |
24    | music=default                   |
25    | context=sales                   |
26    | strategy=ringall                |
27    | joinempty=strict                |
28    | leavewhenempty=strict           |
29    |                                 |
30    | ; Customer service queue        |
31    | [customerservice]               |
32    | music=default                   |
33    | context=customerservice         |
34    | strategy=ringall                |
35    | joinempty=strict                |
36    | leavewhenempty=strict           |
37    |                                 |
38    | ; Support dispatch queue        |
39    | [support-dispatch]              |
40    | music=default                   |
41    | context=dispatch                |
42    | strategy=ringall                |
43    | joinempty=strict                |
44    | leavewhenempty=strict           |
45    ===================================
47 In the above, we have defined 3 separate calling queues: 
48 sales-general, customerservice, and support-dispatch.
50 Please note that the sales-general queue specifies a
51 context of "sales", and that customerservice specifies the
52 context of "customerservice", and the support-dispatch
53 queue specifies the context "dispatch". These three
54 contexts must be defined somewhere in your dialplan.
55 We will show them after the main menu below.
57 <verbage explaining options above>
58 In the [general] section, specifying the persistentmembers=yes,
59 will cause the agent lists to be stored in astdb, and 
60 recalled on startup.
62 The strategy=ringall will cause all agents to be dialed
63 together, the first to answer is then assigned the incoming
64 call. 
66 "joinempty" set to "strict" will keep incoming callers from
67 being placed in queues where there are no agents to take calls.
68 The Queue() application will return, and the dial plan can 
69 determine what to do next.
71 If there are calls queued, and the last agent logs out, the
72 remaining incoming callers will immediately be removed from
73 the queue, and the Queue() call will return, IF the "leavewhenempty" is
74 set to "strict".
77 =====================================
78 |  Routing incoming Calls to Queues |
79 =====================================
82 Then in extensions.ael, you can do these things:
84 ================ The Main Menu 
86 At Digium, incoming callers are sent to the "mainmenu" context, where they
87 are greeted, and directed to the numbers they choose...
89 context mainmenu {
91         includes {
92                 digium;
93                 queues-loginout;
94         }
96         0 => goto dispatch|s|1;
97         2 => goto sales|s|1;
98         3 => goto customerservice|s|1;
99         4 => goto dispatch|s|1;
101         s => {
102                 Ringing();
103                 Wait(1);
104                 Set(attempts=0);
105                 Answer();
106                 Wait(1);
107                 Background(digium/ThankYouForCallingDigium);
108                 Background(digium/YourOpenSourceTelecommunicationsSupplier);
109                 WaitExten(0.3);
110         repeat:
111                 Set(attempts=$[${attempts} + 1]);
112                 Background(digium/IfYouKnowYourPartysExtensionYouMayDialItAtAnyTime);
113                 WaitExten(0.1);
114                 Background(digium/Otherwise);
115                 WaitExten(0.1);
116                 Background(digium/ForSalesPleasePress2);
117                 WaitExten(0.2);
118                 Background(digium/ForCustomerServicePleasePress3);
119                 WaitExten(0.2);
120                 Background(digium/ForAllOtherDepartmentsPleasePress4);
121                 WaitExten(0.2);
122                 Background(digium/ToSpeakWithAnOperatorPleasePress0AtAnyTime);
123                 if( ${attempts} < 2 ) {
124                         WaitExten(0.3);
125                         Background(digium/ToHearTheseOptionsRepeatedPleaseHold);
126                 }
127                 WaitExten(5);
128                 if( ${attempts} < 2 ) goto repeat;
129                 Background(digium/YouHaveMadeNoSelection);
130                 Background(digium/ThisCallWillBeEnded);
131                 Background(goodbye);
132                 Hangup();
133         }
137 ============= The Contexts referenced from the queues.conf file
141 context sales {
143         0 => goto dispatch|s|1;
144         8 => Voicemail(${SALESVM});
146         s => {
147                 Ringing();
148                 Wait(2);
149                 Background(digium/ThankYouForContactingTheDigiumSalesDepartment);
150                 WaitExten(0.3);
151                 Background(digium/PleaseHoldAndYourCallWillBeAnsweredByOurNextAvailableSalesRepresentative);
152                 WaitExten(0.3);
153                 Background(digium/AtAnyTimeYouMayPress0ToSpeakWithAnOperatorOr8ToLeaveAMessage);
154                 Set(CALLERID(name)=Sales);
155                 Queue(sales-general|t);
156                 Set(CALLERID(name)=EmptySalQ);
157                 goto dispatch|s|1;
158                 Playback(goodbye);
159                 Hangup();
160         }
163 Please note that there is only one attempt to queue a call in the sales queue. All sales agents that
164 are logged in will be rung.
167 context customerservice {
169         0 => {
170                 SetCIDName(CSVTrans);
171                 goto dispatch|s|1;
172         }
173         8 => Voicemail(${CUSTSERVVM});
175         s => {
176                 Ringing();
177                 Wait(2);
178                 Background(digium/ThankYouForCallingDigiumCustomerService);
179                 WaitExten(0.3);
180         notracking:
181                 Background(digium/PleaseWaitForTheNextAvailableCustomerServiceRepresentative);
182                 WaitExten(0.3);
183                 Background(digium/AtAnyTimeYouMayPress0ToSpeakWithAnOperatorOr8ToLeaveAMessage);
184                 Set(CALLERID(name)=Cust Svc);
185                 Set(QUEUE_MAX_PENALTY=10);
186                 Queue(customerservice|t);
187                 Set(QUEUE_MAX_PENALTY=0);
188                 Queue(customerservice|t);
189                 Set(CALLERID(name)=EmptyCSVQ);
190                 goto dispatch|s|1;
191                 Background(digium/NoCustomerServiceRepresentativesAreAvailableAtThisTime);
192                 Background(digium/PleaseLeaveAMessageInTheCustomerServiceVoiceMailBox);
193                 Voicemail(${CUSTSERVVM});
194                 Playback(goodbye);
195                 Hangup();
196         }
199 Note that calls coming into customerservice will first be try to queue
200 calls to those agents with a QUEUE_MAX_PENALTY of 10, and if none are available,
201 then all agents are rung.
204 context dispatch
207         s => {
208                 Ringing();
209                 Wait(2);
210                 Background(digium/ThankYouForCallingDigium);
211                 WaitExten(0.3);
212                 Background(digium/YourCallWillBeAnsweredByOurNextAvailableOperator);
213                 Background(digium/PleaseHold);
214                 Set(QUEUE_MAX_PENALTY=10);
215                 Queue(dispatch|t);
216                 Set(QUEUE_MAX_PENALTY=20);
217                 Queue(dispatch|t);
218                 Set(QUEUE_MAX_PENALTY=0);
219                 Queue(dispatch|t);
220                 Background(digium/NoOneIsAvailableToTakeYourCall);
221                 Background(digium/PleaseLeaveAMessageInOurGeneralVoiceMailBox);
222                 Voicemail(${DISPATCHVM});
223                 Playback(goodbye);
224                 Hangup();
225         }
228 And in the dispatch context, first agents of priority 10 are tried, then
229 20, and if none are available, all agents are tried. 
231 Notice that a common pattern is followed in each of the three queue contexts:
233 First, you set QUEUE_MAX_PENALTY to a value, then you call
234 Queue(<queue-name>,option,... (see the documentation for the Queue application));
236 In the above, note that the "t" option is specified, and this allows the 
237 agent picking up the incoming call the luxury of transferring the call to 
238 other parties.
240 The purpose of specifying the QUEUE_MAX_PENALTY is to develop a set of priorities
241 amongst agents. By the above usage, agents with lower number priorities will 
242 be given the calls first, and then, if no-one picks up the call, the QUEUE_MAX_PENALTY
243 will be incremented, and the queue tried again. Hopefully, along the line, someone
244 will pick up the call, and the Queue application will end with a hangup.
246 The final attempt to queue in most of our examples sets the QUEUE_MAX_PENALTY
247 to zero, which means to try all available agents.
250 =========================================
251 |      Assigning agents to Queues       |
252 =========================================
254 In this example dialplan, we want to be able to add and remove agents to 
255 handle incoming calls, as they feel they are available. As they log in,
256 they are added to the queue's agent list, and as they log out, they are
257 removed. If no agents are available, the queue command will terminate, and
258 it is the duty of the dialplan to do something appropriate, be it sending
259 the incoming caller to voicemail, or trying the queue again with a higher 
260 QUEUE_MAX_PENALTY.
262 Because a single agent can make themselves available to more than one queue,
263 the process of joining multiple queues can be handled automatically by the
264 dialplan.
267 ================= Agents Log In and Out 
270 context queues-loginout
272         6092 => {
273                         Answer();
274                         Read(AGENT_NUMBER,agent-enternum);
275                         VMAuthenticate(${AGENT_NUMBER}@default,s);
276                         Set(queue-announce-success=1);
277                         goto queues-manip,I${AGENT_NUMBER},1;
278                 }
280         6093 => {
281                         Answer();
282                         Read(AGENT_NUMBER,agent-enternum);
283                         Set(queue-announce-success=1);
284                         goto queues-manip,O${AGENT_NUMBER},1;
285                 }
289 In the above contexts, the agents dial 6092 to log into their queues,
290 and they dial 6093 to log out of their queues. The agent is prompted
291 for their agent number, and if they are logging in, their passcode, 
292 and then they are transferred to the proper extension in the 
293 queues-manip context.  The queues-manip context does all the 
294 actual work:
297 context queues-manip {
299         // Raquel Squelch
300         _[IO]6121 => {
301                 &queue-addremove(dispatch,10);
302                 &queue-success();
303         }
305         // Brittanica Spears
306         _[IO]6165 => {
307                 &queue-addremove(dispatch,20);
308                 &queue-success();
309         }
311         // Rock Hudson
312         _[IO]6170 => {
313                 &queue-addremove(sales-general,10);
314                 &queue-addremove(customerservice,20);
315                 &queue-addremove(dispatch,30);
316                 &queue-success();
317         }
319         // Saline Dye-on
320         _[IO]6070 => {
321                 &queue-addremove(sales-general,20);
322                 &queue-addremove(customerservice,30);
323                 &queue-addremove(dispatch,30);
324                 &queue-success();
325         }
328 In the above extensions, note that the queue-addremove macro is used
329 to actually add or remove the agent from the applicable queue,
330 with the applicable priority level. Note that agents with a 
331 priority level of 10 will be called before agents with levels
332 of 20 or 30.
334 In the above example, Raquel will be dialed first in the dispatch
335 queue, if she has logged in. If she is not, then the second call of
336 Queue() with priority of 20 will dial Brittanica if she is present,
337 otherwise the third call of Queue() with MAX_PENALTY of 0 will 
338 dial Rock and Saline simultaneously.
340 Also note that Rock will be among the first to be called in the sales-general 
341 queue, and among the last in the dispatch queue. As you can see in
342 main menu, the callerID is set in the main menu so they can tell 
343 which queue incoming calls are coming from.
345 The call to queue-success() gives some feedback to the agent
346 as they log in and out, that the process has completed.
348 macro queue-success()
350         if( ${queue-announce-success} > 0 )
351         {
352                 switch(${MACRO_EXTEN:0:1})
353                 {
354                 case I:
355                         Playback(agent-loginok);
356                         Hangup();
357                                                 break;
358                 case O:
359                         Playback(agent-loggedoff);
360                         Hangup();
361                                                 break;
362                 }
363         }
367 The queue-addremove macro is defined in this manner:
369 macro queue-addremove(queuename,penalty)
371         switch(${MACRO_EXTEN:0:1})
372         {
373         case I:  // Login
374                 AddQueueMember(${queuename},Local/${MACRO_EXTEN:1}@agents,${penalty});
375                                 break;
376         case O:  // Logout
377                 RemoveQueueMember(${queuename},Local/${MACRO_EXTEN:1}@agents);
378                                 break;
379         case P:  // Pause
380                 PauseQueueMember(${queuename},Local/${MACRO_EXTEN:1}@agents);
381                                 break;
382         case U:  // Unpause
383                 UnpauseQueueMember(${queuename},Local/${MACRO_EXTEN:1}@agents);
384                                 break;
385         default: // Invalid
386                 Playback(invalid);
387                                 break;
388         }
391 Basically, it uses the first character of the MACRO_EXTEN variable, to determine the
392 proper actions to take. In the above dial plan code, only the cases I or O are used,
393 which correspond to the Login and Logout actions.
396 =======================================================
397 |    Controlling The Way Queues Call the Agents       |
398 =======================================================
400 Notice in the above, that the commands to manipulate agents in queues have
401 "@agents" in their arguments. This is a reference to the agents context:
403 context agents
405         // General sales queue
406         8010 =>
407         {
408                 Set(QUEUE_MAX_PENALTY=10);
409                 Queue(sales-general|t);
410                 Set(QUEUE_MAX_PENALTY=0);
411                 Queue(sales-general|t);
412                 Set(CALLERID(name)=EmptySalQ);
413                 goto dispatch|s|1;
414         }
415         // Customer Service queue
416         8011 =>
417         {
418                 Set(QUEUE_MAX_PENALTY=10);
419                 Queue(customerservice|t);
420                 Set(QUEUE_MAX_PENALTY=0);
421                 Queue(customerservice|t);
422                 Set(CALLERID(name)=EMptyCSVQ);
423                 goto dispatch|s|1;
424         }
425         8013 =>
426         {
427                 Dial(iax2/sweatshop/9456@from-ecstacy);
429                 Set(CALLERID(name)=EmptySupQ);
430                 Set(QUEUE_MAX_PENALTY=10);
431                 Queue(support-dispatch,t);
432                 Set(QUEUE_MAX_PENALTY=20);
433                 Queue(support-dispatch,t);
434                 Set(QUEUE_MAX_PENALTY=0); // means no max
435                 Queue(support-dispatch,t);
436                 goto dispatch|s|1;
437         }
438         6121 => &callagent(${RAQUEL});
439         6165 => &callagent(${SPEARS});
440         6170 => &callagent(${ROCK});
441         6070 => &callagent(${SALINE});
444 In the above, the variables ${RAQUEL}, etc stand for
445 actual devices to ring that person's
446 phone (like Zap/37).
448 The 8010, 8011, and 8013 extensions are purely for transferring
449 incoming callers to queues. For instance, a customer service 
450 agent might want to transfer the caller to talk to sales. The 
451 agent only has to transfer to extension 8010, in this case.
453 Here is the callagent macro, note that if a person in the
454 queue is called, but does not answer, then they are automatically
455 removed from the queue.
457 macro callagent(device)
459         if( ${GROUP_COUNT(${MACRO_EXTEN}@agents)}=0 )
460         {
461                 Set(OUTBOUND_GROUP=${MACRO_EXTEN}@agents);
462                 Dial(${device}|300|t);
463                 switch(${DIALSTATUS})
464                 {
465                 case BUSY:
466                         Busy();
467                         break;
468                 case NOANSWER:
469                         Set(queue-announce-success=0);
470                         goto queues-manip|O${MACRO_EXTEN}|1;
471                 default:
472                         Hangup();
473                         break;
474                 }
475         }
476         else
477         {
478                 Busy();
479         }
482 In the callagent macro above, the ${MACRO_EXTEN} will
483 be 6121, or 6165, etc, which is the extension of the agent.
485 The use of the GROUP_COUNT, and OUTBOUND_GROUP follow this line
486 of thinking. Incoming calls can be queued to ring all agents in the
487 current priority. If some of those agents are already talking, they
488 would get bothersome call-waiting tones. To avoid this inconvenience,
489 when an agent gets a call, the OUTBOUND_GROUP assigns that 
490 conversation to the group specified, for instance 6171@agents.
491 The ${GROUP_COUNT()} variable on a subsequent call should return
492 "1" for that group. If GROUP_COUNT returns 1, then the busy() 
493 is returned without actually trying to dial the agent.
495 ================ Pre Acknowledgement Message
497 If you would like to have a pre acknowledge message with option to reject the message
498 you can use the following dialplan Macro as a base with the 'M' dial argument.
500 [macro-screen]
501 exten=>s,1,Wait(.25)
502 exten=>s,2,Read(ACCEPT|screen-callee-options|1)
503 exten=>s,3,Gotoif($[${ACCEPT} = 1] ?50)
504 exten=>s,4,Gotoif($[${ACCEPT} = 2] ?30)
505 exten=>s,5,Gotoif($[${ACCEPT} = 3] ?40)
506 exten=>s,6,Gotoif($[${ACCEPT} = 4] ?30:30)
507 exten=>s,30,Set(MACRO_RESULT=CONTINUE)
508 exten=>s,40,Read(TEXTEN|custom/screen-exten|)
509 exten=>s,41,Gotoif($[${LEN(${TEXTEN})} = 3]?42:45)
510 exten=>s,42,Set(MACRO_RESULT=GOTO:from-internal^${TEXTEN}^1)
511 exten=>s,45,Gotoif($[${TEXTEN} = 0] ?46:4)
512 exten=>s,46,Set(MACRO_RESULT=CONTINUE)
513 exten=>s,50,Playback(after-the-tone)
514 exten=>s,51,Playback(connected)
515 exten=>s,52,Playback(beep)
517 ================ Caveats 
519 In the above examples, some of the possible error checking has been omitted,
520 to reduce clutter and make the examples clearer.