При звонках в колл-центр компаний часто приходится сталкиваться с большим временем ожидания на линии, многие из нас слушали надоедливую мелодию в течение десятков минут хотя бы раз в жизни. Самое интересное заключается в том, что это совершенно невыгодно компании в колл-центр которой вы звоните, особенно если вы звоните на номер 8-800. К счастью, уже давно придуман способ, позволяющий решить данную проблему — это callback или обратный вызов. Суть этого способа очень простая: позвонившему предлагают отключиться от колл-центра при этом его номер так и остается в очереди на обслуживание и как только его очередь подойдет, то ему автоматически наберут и соединят с оператором, на которого распределился его звонок. Таким образом убиваем сразу несколько зайцев: не занимаются линии колл-центра, не тратятся дорогостоящие минуты 8-800, а клиенты не испытывают лишнего раздражения в ожидании ответа. Вендоры колл-центрового ПО хотят за такую функцию весьма приличных денег, а мы под катом расскажем как данный функционал достаточно просто и быстро реализуется с помощью платформы VoxImplant.
Так как мы уже писали про организацию колл-центра с помощью VoxImplant, то повторять эту часть в данной статье не будем, а сосредоточимся на изменениях, которые необходимо сделать в сценариях для реализации коллбэк-функции. Сразу замечу, что данная функциональность доступна только для сценариев, в которых подключается модуль ACD для работы с очередями. Сценарий, отвечающий за обработку входящих звонков с учетом нашей коллбэк-функции будет выглядеть следующим образом:
// Подключаем модуль ACD
require(Modules.ACD);
var request, // <-- тут будем хранить экземпляр ACDRequest
originalCall, // <-- входящий звонок
callerid,
statusInterval,
callback = false; // <-- флаг для коллбэк-режима
// Вешаем обработчик входящего вызова
VoxEngine.addEventListener(AppEvents.CallAlerting, handleInboundCall);
// Обрабатываем входящий вызов
function handleInboundCall(e) {
originalCall = e.call;
callerid = e.callerid;
// Вешаем обработчики
originalCall.addEventListener(CallEvents.Connected, handleCallConnected);
originalCall.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
originalCall.addEventListener(CallEvents.Failed, cleanup);
originalCall.addEventListener(CallEvents.Disconnected, cleanup);
// Отвечаем на звонок
originalCall.answer();
}
// Завершаем сессию
function cleanup(e) {
if (request) {
// Если звонок в очереди - удаляем
request.cancel();
request = null;
}
// Закончить сессию
VoxEngine.terminate();
}
// Играем музыку после окончания проигрывания голоса или музыки
function handlePlaybackFinished(e) {
e.call.startPlayback("http://cdn.voximplant.com/toto.mp3");
}
// Обработчик нажатий на кнопки
function handleToneReceived(e) {
if (e.tone == "#") {
callback = true;
originalCall.hangup(); // <-- несмотря на отсутствие звонков сессия не завершится
}
}
// Звонок соединен
function handleCallConnected(e) {
// Включаем обработку нажатий на кнопки телефона
originalCall.handleTones(true);
originalCall.addEventListener(CallEvents.ToneReceived, handleToneReceived);
// Отправляем звонок в очередь 'MainQueue', которую мы создали в панели управления
request = VoxEngine.enqueueACDRequest("MainQueue", callerid);
// Получаем статус после того как звонок поставлен в очередь
request.addEventListener(ACDEvents.Queued, function (acdevent) {
request.getStatus();
});
// Сообщаем звонящему о примерном времени ожидания и месте в очереди
request.addEventListener(ACDEvents.Waiting, function (acdevent) {
var minutesLeft = acdevent.ewt + 1;
var minutesWord = " минуты.";
if ((minutesLeft > 10 && minutesLeft < 20) || (minutesLeft % 10 > 4 || minutesLeft % 10 == 0)) {
minutesWord = " минут.";
} else if (minutesLeft % 10 == 1) {
minutesWord = " минуту.";
}
originalCall.say("Вы находитесь в очереди под номером " + acdevent.position +
". Оператор ответит Вам менее чем через " + (acdevent.ewt + 1) + minutesWord +
" Вы также можете нажать решетку и мы сами перезвоним вам как только оператор будет готов обслужить ваш вызов.", Language.RU_RUSSIAN_FEMALE);
});
// Отправляем звонок оператору
request.addEventListener(ACDEvents.OperatorReached, function (acdevent) {
if (callback) {
// В случае если клиент выбрал коллбэк, то после распределения вызова на оператора сообщаем оператору, что нужно дождаться соединения с клиентом
acdevent.operatorCall.say("Пожалуйста, дождитесь соединения с клиентом.", Language.RU_RUSSIAN_FEMALE);
acdevent.operatorCall.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
acdevent.operatorCall.addEventListener(CallEvents.PlaybackFinished, function (callevent) {
// Делаем коллбэк
originalCall = VoxEngine.callUser(callerid);
originalCall.addEventListener(CallEvents.Connected, function (callevent) {
VoxEngine.sendMediaBetween(acdevent.operatorCall, originalCall);
clearInterval(statusInterval);
});
originalCall.addEventListener(CallEvents.Failed, cleanup);
originalCall.addEventListener(CallEvents.Disconnected, cleanup);
});
} else {
VoxEngine.sendMediaBetween(acdevent.operatorCall, originalCall);
acdevent.operatorCall.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
clearInterval(statusInterval);
}
});
// Нет доступных операторов - ни один оператор не обслуживает очередь
request.addEventListener(ACDEvents.Offline, function (acdevent) {
originalCall.say("К сожалению, сейчас нет доступных операторов. Пожалуйста, попробуйте позвонить позднее.", Language.RU_RUSSIAN_FEMALE);
originalCall.addEventListener(CallEvents.PlaybackFinished, function (e) {
VoxEngine.terminate();
});
});
// Получаем и сообщаем статус каждые 30 секунд
statusInterval = setInterval(request.getStatus, 30000);
}
Вот собственно и все. Немного подправив сценарий из предыдущего туториала про ACD мы получили очередь с коллбэком. Базовый сценарий, который мы меняли, доступен на github.com/voximplant/acd, также как и простейший веб-фон для операторов.
Работа с очередью через HTTP API
Предыдущий пример был сделан для случая когда сначала есть входящий звонок, в реальной жизни могут встречаться случаи, когда клиент сразу заказывает на сайте коллбэк и нам нужно его разместить в очереди на обработку. В целом, очередь — это абстрактная сущность, туда на обработку можно кидать не только звонки, но и, например, emailы, сообщения и т.д. В такой ситуации сценарий надо запускать через метод StartScenarios HTTP API, а сам сценарий немного трансформируется:
var displayName, callback = true;
// Обработка заупуска сессии через StartScenarios
VoxEngine.addEventListener(AppEvents.Started, function(e) {
var data = VoxEngine.customData();
// Будем в script_custom_data передавать имя клиента и его номер в виде номер:имя
data = data.split(":");
callerid = data[0];
displayName = data[1];
Logger.write("Put "+displayName+" with number "+callerid+" in a queue");
// Ставим запрос в очередь 'MainQueue'
request = VoxEngine.enqueueACDRequest("MainQueue", callerid);
// ... дальше все то же самое, что и в предыдущем сценарии
});
P.S. А что если...
Нас иногда спрашивают: «а что если мы хотим использовать SIP-телефоны вместо веб-телефонов, сделанных с помощью VoxImplant Web SDK в нашем колл-центре?»
Отвечаем: «как ни странно, это возможно, но подробнее об этом мы расскажем в отдельной статье».
Автор: aylarov