Делаем очередь входящих звонков с функцией callback

в 10:19, , рубрики: callback, diy или сделай сам, ip-телефония, ivr, queue, voip, voximplant, Блог компании Zingaya, Веб-разработка

Делаем очередь входящих звонков с функцией callback - 1 При звонках в колл-центр компаний часто приходится сталкиваться с большим временем ожидания на линии, многие из нас слушали надоедливую мелодию в течение десятков минут хотя бы раз в жизни. Самое интересное заключается в том, что это совершенно невыгодно компании в колл-центр которой вы звоните, особенно если вы звоните на номер 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

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js