Voximplant. Blog

Call queue with callback function

ACD Callback

Good customer service is one of the critical components of the successful business these days. Many interactions with customers happen over the phone when they making calls to company’s call center and, since it’s rather expensive to keep a big number of agents in the call center, customers have to spend their time in a queue listening to some music and waiting for the answer. Fortunately, there is a solution for this problem – queue with callback function. IVR tells customer that he can press some key (for example, # or *) to hangup the call, but stay in the queue. After customer’s position in the queue was reached, system connects agent with the customer by calling customer’s number. It’s rather expensive module in case of Enterprise call center solutions, but with VoxImplant any developer can implement this type of queue in a few minutes.
Please notice that you need to read our “Using ACD module for call queuing” post before reading this tutorial, since we will rely on some terms, code and entities described in the post.

First of all, sessions that use ACD module can exists without active calls during 2 hours. Let’s modify our scenario from the “Using ACD module for call queuing” post to enable callback function:

  1. // Enable ACD module
  2. require(Modules.ACD);
  3.  
  4. var request,
  5. originalCall,
  6. callerid,
  7. statusInterval,
  8. callback = false;
  9.  
  10. VoxEngine.addEventListener(AppEvents.CallAlerting, handleInboundCall);
  11.  
  12. // Handle inbound call
  13. function handleInboundCall(e) {
  14. originalCall = e.call;
  15. callerid = e.callerid;
  16. // Add event listeners
  17. originalCall.addEventListener(CallEvents.Connected, handleCallConnected);
  18. originalCall.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  19. originalCall.addEventListener(CallEvents.Failed, cleanup);
  20. originalCall.addEventListener(CallEvents.Disconnected, cleanup);
  21. // Answer call
  22. originalCall.answer();
  23. }
  24.  
  25. // Terminate call and session
  26. function cleanup(e) {
  27. if (request) {
  28. // Remove call from queue
  29. request.cancel();
  30. request = null;
  31. }
  32. // terminate session
  33. VoxEngine.terminate();
  34. }
  35.  
  36. // Play music after TTS finish
  37. function handlePlaybackFinished(e) {
  38. e.call.startPlayback("http://cdn.voximplant.com/toto.mp3");
  39. }
  40.  
  41. // Get suffix for the number
  42. function ordinal_suffix_of(i) {
  43. var j = i % 10,
  44. k = i % 100;
  45. if (j == 1 && k != 11) {
  46. return i + "st";
  47. }
  48. if (j == 2 && k != 12) {
  49. return i + "nd";
  50. }
  51. if (j == 3 && k != 13) {
  52. return i + "rd";
  53. }
  54. return i + "th";
  55. }
  56.  
  57. // Handle callback request
  58. function handleToneReceived(e) {
  59. if (e.tone == "#") {
  60. callback = true;
  61. originalCall.hangup(); // <-- despite there is no calls in the session it will stay alive
  62. }
  63. }
  64.  
  65. // Call connected
  66. function handleCallConnected(e) {
  67. // Enable key input processing
  68. originalCall.handleTones(true);
  69. originalCall.addEventListener(CallEvents.ToneReceived, handleToneReceived);
  70. // Put the call into the queue 'MainQueue'
  71. request = VoxEngine.enqueueACDRequest("MainQueue", callerid);
  72.  
  73. // Get call status in queue after it was put in the queue
  74. request.addEventListener(ACDEvents.Queued, function (acdevent) {
  75. request.getStatus();
  76. });
  77.  
  78. // Notify caller about his position in the queue
  79. request.addEventListener(ACDEvents.Waiting, function (acdevent) {
  80. var minutesLeft = acdevent.ewt + 1;
  81. var minutesWord = " minute.";
  82. if (minutesLeft > 1) {
  83. minutesWord = " minutes.";
  84. }
  85. originalCall.say("You are " + ordinal_suffix_of(acdevent.position) +
  86. " in a queue. Represetative will answer you in " + (acdevent.ewt + 1) + minutesWord + " You can press pound and we will call you back as soon as there is a free operator available.", Language.US_ENGLISH_FEMALE);
  87. });
  88.  
  89. request.addEventListener(ACDEvents.Error, function (acdevent) {});
  90.  
  91. // Connect caller with operator
  92. request.addEventListener(ACDEvents.OperatorReached, function (acdevent) {
  93. if (callback) {
  94. acdevent.operatorCall.say("Please wait while we connecting you with a customer.", Language.US_ENGLISH_FEMALE);
  95. acdevent.operatorCall.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
  96. acdevent.operatorCall.addEventListener(CallEvents.PlaybackFinished, function (callevent) {
  97. // Calling customer
  98. originalCall = VoxEngine.callUser(callerid);
  99. originalCall.addEventListener(CallEvents.Connected, function (callevent) {
  100. VoxEngine.sendMediaBetween(acdevent.operatorCall, originalCall);
  101. clearInterval(statusInterval);
  102. });
  103. originalCall.addEventListener(CallEvents.Failed, cleanup);
  104. originalCall.addEventListener(CallEvents.Disconnected, cleanup);
  105. });
  106. } else {
  107. VoxEngine.sendMediaBetween(acdevent.operatorCall, originalCall);
  108. acdevent.operatorCall.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
  109. clearInterval(statusInterval);
  110. }
  111. });
  112.  
  113. request.addEventListener(ACDEvents.Error, function (acdevent) {
  114. Logger.write("ACD Error " + acdevent.error);
  115. });
  116.  
  117. // No operators are available
  118. request.addEventListener(ACDEvents.Offline, function (acdevent) {
  119. originalCall.say("All operators are currently offline, please try to call again later.", Language.US_ENGLISH_FEMALE);
  120. originalCall.addEventListener(CallEvents.PlaybackFinished, function (e) {
  121. VoxEngine.terminate();
  122. });
  123. });
  124.  
  125. // Get current call status in a queue every 30 seconds
  126. statusInterval = setInterval(request.getStatus, 30000);
  127. }

This scenario processes inbound calls, but sometimes developers would like to put call in a queue from a callback form. It can be done using StartScenarios HTTP API request and additional code in the scenario:

  1. var displayName, callback = true;
  2. // Handle StartScenarios request
  3. VoxEngine.addEventListener(AppEvents.Started, function(e) {
  4. var data = VoxEngine.customData();
  5. // Let's assume customer's phone and name are passed in script_custom_data param in phone:name form
  6. data = data.split(":");
  7. callerid = data[0];
  8. displayName = data[1];
  9. Logger.write("Put "+displayName+" with number "+callerid+" in a queue");
  10. // Put request in queue with name 'MainQueue'
  11. request = VoxEngine.enqueueACDRequest("MainQueue", callerid);
  12. // ... and the same code like in previous example
  13. });

It’s also possible to make requests to the session using media_session_access_url and get queue position updates to display it to the customer.

Tagged in , ,

Comments