Робимо Новорічний колл-центр

Наближається новий 2015 рік і ми, трохи поміркувавши, вирішили розважити людей і заодно реалізувати ідею новорічного колл-центру з блекджек Дідами Морозами та Снігуроньками, що відповідають на дзвінки охочих поспілкуватися у передноворічний час. Кожен бажаючий може стати оператором цього колл-центру, вибравши підлогу оператора, так само як кожен бажаючий може зателефонувати в цей кол-центр прямо з браузера (потрібен мікрофон) або просто набравши номер телефону. Операторське місце Діда Мороза/Снігуроньки буде працювати прямо в браузері (привіт WebRTC) і тут без мікрофона вже ніяк не обійтися. Для реалізації такого сервісу може знадобитися досить багато часу, якщо все робити з нуля, але ми скористаємося платформою VoxImplant, яка нам значно полегшить і прискорить весь процес. Отже, потрібно зробити веб-сервіс для реєстрації бажаючих стати операторами, а також 2 веб-додатки — дзвонилку та операторське місце + написати парочку сценаріїв javascript. Ми сподіваємося, що знайдеться достатньо охочих виступити оператори нашого колл-центру, а то дзвоном доведеться довго чекати розмови в черзі. Щоб було цікавіше ми організуємо рейтинг найбільш балакучих операторів і дамо їм подарункові сертифікати VoxImplant, щоб вони могли потім самі реалізувати свій власний сервіс з блекджек… ну, загалом, ви зрозуміли. Все найцікавіше, як завжди, під катом!

Результат
Для тих кому не терпиться спробувати сервіс відразу даю посилання http://demos.zingaya.com/newyear/, думаю що інтерфейс не вимагає особливих коментарів. Вибираємо свою сторону і дзвонимо/приймаємо дзвінки.
Реєструємо аккаунт оператора, логинимся і ставимо статус «Готовий приймати дзвінки»

Як тільки який-небудь з вхідних викликів розподілиться на оператора, то заграє мелодія і з'явиться спливаюче вікно, де можна відповісти на дзвінок.


Створення програми
Авторізуємось в панелі управління VoxImplant (https://manage.voximplant.com) і створюємо в розділі Applications додаток newyear, це просто віртуальна сутність, до якої ми будемо чіпляти юзерів-операторів, а також опишемо правила обробки дзвінків (який сценарій повинен обробляти будь дзвінки).

Створення сценарію VoxEngine
Тепер треба написати сценарій, який буде обробляти вхідні дзвінки і розподіляти їх по операторам. Сценарії для VoxImplant пишуться на JS. Наш буде виглядати так:
// Підключаємо модулі ACD (розподіл викликів) і ASR (розпізнавання мови)
require(Modules.ACD);
require(Modules.ASR);

var request,
originalCall,
callerid,
statusInterval,
asrTimeout,
asr, 
queueName = 'MainQueue';

// Вішаємо обробник вхідного дзвінка
VoxEngine.addEventListener. (AppEvents.CallAlerting, handleInboundCall);

// Функція-обробник вхідного дзвінка
function handleInboundCall(e) {
originalCall = e.call; // зберігаємо примірник вхідного дзвінка
callerid = e.callerid; // зберігаємо caller id
// Вішаємо обробники
originalCall.addEventListener. (CallEvents.Connected, handleCallConnected);
originalCall.addEventListener. (CallEvents.Failed, cleanup);
originalCall.addEventListener. (CallEvents.Disconnected, cleanup);
// Відповідаємо на вхідні дзвінки
originalCall.answer();
}

// Завершуємо дзвінок і вбиваємо сесію
function cleanup(e) {
if (request) {
// Якщо поставили дзвінок в чергу - скасовуємо
request.cancel();
request = null;
}
// Вбиваємо сесію
VoxEngine.terminate();
}

// Програти музику після того, як відпрацює TTS
function handlePlaybackFinished(e) {
e.call.startPlayback("http://cdn.voximplant.com/newyear.mp3");
}

// Функція для перетворення закінчень часу очікування для TTS
function getNumEnding(iNumber, aEndings) {
var sEnding, i;
iNumber = iNumber % 100;
if (iNumber >= 11 && iNumber <= 19) {
sEnding = aEndings[2];
} else {
i = iNumber % 10;
switch (i) {
case (1):
sEnding = aEndings[0];
break;
case (2):
case (3):
case (4):
sEnding = aEndings[1];
break;
default:
sEnding = aEndings[2];
}
}
return sEnding;
}

// Дзвінок з'єднаний
function handleCallConnected(e) {
// Програємо повідомлення
e.call.say("Новорічна колл-центр вітає васс!!! " +
"Ви хочете поговорити зі снігуронькою або з дідом морозом?", Language.RU_RUSSIAN_FEMALE);
e.call.addEventListener. (CallEvents.PlaybackFinished, handleIntroPlayed);
}

// Після програвання інтро
function handleIntroPlayed(e) {
e.call.removeEventListener(CallEvents.PlaybackFinished, handleIntroPlayed);
// Створюємо інстанси для розпізнавання мовлення із зазначеним словником
asr = VoxEngine.createASR(ASRLanguage.RUSSIAN_RU, ["зі снігуронькою",
"снігуронькою",
"снігуронька",
"з дідом морозом",
"дідом морозом",
"дід мороз"
]);
// Якщо почалося захоплення мовлення
asr.addEventListener. (ASREvents.CaptureStarted, function (e) {
clearTimeout(asrTimeout);
});
// Результат розпізнавання
asr.addEventListener. (ASREvents.Result, function (e) {
// Виключити розпізнавання
asr.stop();
// Якщо вибрали снігуроньку, то міняємо назву черги на SnegurQueue
if ((e.text == 'зі снігуронькою' || e.text == 'снігуронькою' || e.text == 'снігуронька') && e.confidence >= 50) {
originalCall.say("Відмінно! Перша вільна снігуронька відповість на ваш дзвінок.", Language.RU_RUSSIAN_FEMALE);
queueName = 'SnegurQueue';
originalCall.addEventListener. (CallEvents.PlaybackFinished, addToQueue);
} else if ((e.text == 'з дідом морозом' || e.text == 'дідом морозом' || e.text == 'дід мороз') && e.confidence >= 50) {
// Якщо вибрали діда мороза, то черга - MorozQueue
originalCall.say("Відмінно! Перший вільний дід мороз відповість на ваш дзвінок.", Language.RU_RUSSIAN_FEMALE);
queueName = 'MorozQueue';
originalCall.addEventListener. (CallEvents.PlaybackFinished, addToQueue);
} else {
// Якщо немає впевненості (точність < 50%), то відправляємо в загальну чергу - MainQueue
originalCall.say("Перший вільний дід мороз або снігуронька дадуть відповідь на ваш дзвінок.", Language.RU_RUSSIAN_FEMALE);
originalCall.addEventListener. (CallEvents.PlaybackFinished, addToQueue);
}
});
// Відправляємо звук в інстанси ASR
originalCall.sendMediaTo(asr);
// Якщо протягом 3 секунд не сказали з ким хочуть поговорити, то відправляємо в загальну чергу
asrTimeout = setTimeout(function () {
asr.stop();
originalCall.say("Перший вільний дід мороз або снігуронька дадуть відповідь на ваш дзвінок.", Language.RU_RUSSIAN_FEMALE);
originalCall.addEventListener. (CallEvents.PlaybackFinished, addToQueue);
}, 3000);
}

// Додаємо дзвінок у певну чергу в залежності від queueName
function addToQueue(e) {
Logger.write('Adding call to queue: '+queueName);
originalCall.removeEventListener(CallEvents.PlaybackFinished, addToQueue);
// Після завершення TTS включаємо програвання музички
originalCall.addEventListener. (CallEvents.PlaybackFinished, handlePlaybackFinished);
// Додаємо в чергу
request = VoxEngine.enqueueACDRequest(queueName, callerid);
// Отримуємо статус дзвінка в черзі
request.addEventListener. (ACDEvents.Queued, function (acdevent) {
request.getStatus();
});
// Повідомити про становище дзвінка в черзі
request.addEventListener. (ACDEvents.Waiting, function (acdevent) {
var minutesLeft = acdevent.ewt + 1,
txt = 'Дід мороз або снігуронька відповість вам через';
if (queueName == 'SnegurQueue') txt = "Снігуронька відповість вам через";
else if (queueName == 'MorozQueue') txt = "Дід мороз відповість вам через";
originalCall.say("Ви у черзі під номером " + acdevent.position +
". " + txt + " " + (acdevent.ewt + 1) + getNumEnding(minutesLeft, ['хвилину', 'хвилини', 'хвилин']), Language.RU_RUSSIAN_FEMALE);
});
// Дійшла черга - з'єднуємо абонента з оператором
request.addEventListener. (ACDEvents.OperatorReached, function (acdevent) {
VoxEngine.sendMediaBetween(acdevent.operatorCall, originalCall);
acdevent.operatorCall.sendMessage(JSON.stringify({
number: originalCall.callerid()
}));
acdevent.operatorCall.addEventListener. (CallEvents.Disconnected, VoxEngine.terminate);
clearInterval(statusInterval);
});
// Немає доступних операторів
request.addEventListener. (ACDEvents.Offline, function (acdevent) {
clearInterval(statusInterval);
// Якщо були в черзі до снегуркам або дідам морозом, то намагаємося перевизначити в загальну чергу
if (queueName == 'SnegurQueue') {
originalCall.say("На жаль, немає жодної снігуроньки відповідає на дзвінки. Спробуємо знайти діда мороза!", Language.RU_RUSSIAN_FEMALE);
queueName = 'MainQueue';
originalCall.addEventListener. (CallEvents.PlaybackFinished, addToQueue);
} else if (queueName == 'MorozQueue') {
originalCall.say("На жаль, немає жодного діда мороза відповідає на дзвінки. Спробуємо знайти снігуроньку!", Language.RU_RUSSIAN_FEMALE);
queueName = 'MainQueue';
originalCall.addEventListener. (CallEvents.PlaybackFinished, addToQueue);
} else {
// Якщо жодного діда мороза і снігурки немає, то пропонуємо стати оператором і завершуємо дзвінок
originalCall.say("На жаль, немає жодного діда мороза або снігуроньки відповідають на дзвінки. Спробуйте зателефонувати пізніше " +
"або самі станьте оператором нашого новорічного колл-центру! Спасибі за дзвінок!", Language.RU_RUSSIAN_FEMALE);
originalCall.addEventListener. (CallEvents.PlaybackFinished, VoxEngine.Terminate);
}
});

// Оновлювати інформацію про становище в черзі кожні 30 секунд
statusInterval = setInterval(request.getStatus, 30000);
}


Створенням правил обробки
Вхідні дзвінки з номера і з web sdk треба направити на обробку нашим сценарієм. У веб-додатку для вихідних дзвінків ми зашили номер newyearcall, а в розділі з номерами телефонів підключили номер 74951330204 до нашого додатком. Створюємо 2 правила:


Організація черг
В контексті даного проекту нам потрібно створити 3 різних черги (Settings -> Queues) — MainQueue (все), MorozQueue (тільки діди морози) і SnegurQueue (тільки снігуроньки). Клієнтам пропонується вибрати з ким він хоче поговорити зі снігуронькою або з дідом морозом, вибір здійснюється за допомогою системи розпізнавання голосу, яка доступна в VoxImplant, з заздалегідь описаного словника. Якщо протягом 3 секунд вибір не був озвучений або система не впевнена в розпізнаному варіанті (ймовірність < 50%), то дзвінок направляється в загальну чергу, яку обслуговую і діди морози і снігуроньки. Якщо вибір був зроблений успішно, то йдіть в конкретну чергу. У разі якщо дану чергу не обслуговує ні один оператор, то перевизначаємо виклик в загальну чергу, а якщо і там нікого з операторів немає — програємо повідомлення і пропонуємо самому стати оператором.
Крім створення черг потрібно ще створити відповідні скілл-групи, так як прив'язка користувачів програми (операторів) до черг робиться саме через скіли (Settings -> Skills). Створюємо 3 скілла: NewYearSkillAll, NewYearSkillMoroz та NewYearSkillSnegur, відповідно кожному скиллу задаємо свою чергу — MainQueue, MorozQueue і SnegurQueue. При створенні юзерів-операторів, ми будемо їх чіпляти відразу до 2 скілл-груп — NewYearSkillAll + NewYearSkillMoroz (діди морози) або NewYearSkillSnegur (снігурки).


Операторське місце
Операторське місце, як і веб-дзвонилка, робляться за допомогою Web SDK VoxImplant. З важливих моментів можна відзначити реалізацію перемикача статусу оператора за допомогою функції setOperatorACDStatus.

vox.setOperatorACDStatus(VoxImplant.OperatorACDStatuses.Ready); // де vox - інстанси VoxImplant.Client

Дзвінки розподілятимуться на оператора, тільки коли він знаходиться в стані Ready. Знаходження в інших станах відображається в статистиці по роботі оператора.

Загалом, цього достатньо, решта прикручування (віддалене створення юзерів програми і т. д.) вже робляться на базі HTTP API VoxImplant.

Ще раз посилання на результат http://demos.zingaya.com/newyear/ або можна просто набрати +74951330204

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.