Все сервисы стремительно уходят в digital — мобильные приложения, чат-боты и онлайн-чаты с поддержкой. У маркетплейса Flowwow они тоже есть, однако телефоном до сих пор пользуются. В день мы получаем около 90 звонков — для их обработки нецелесообразно содержать отдельный штат операторов, но и отказываться от этого канала связи мы не хотим.
Чтобы наши операторы поддержки, работающие по четыре человека в смену, смогли взять на себя звонки плюсом к тысячам сообщений в почте и чатах, нужна грамотная автоматизация. Мы написали Flowwow API для телефонии, чтобы соединять клиентов напрямую с магазинами-исполнителями по всему миру и брать только те обращения, где без наших экспертов не обойтись.
В этой статье пройдем пути звонящего в зависимости от того, кто он и какой у него запрос. (кат)
Мы собрали свой сценарий на платформе Voximplant и настроили его с нашими внутренними системами.
Кто может позвонить:
-
клиент с активным заказом;
-
клиент с новым заказом.
Или это могут быть наши коллеги из магазина-партнера, про такой сценарий тоже расскажем.
Для начала приветствие
Поздороваться и объяснить, куда клиент попал, — стандартная история. Пока мы маршрутизируем звонок, вот что происходит на бэкстейдже:
/** * Класс со сценариями * */ class FlowwowScenario { /** * Создание объекта и инициализация сценариев от входных данных * */ constructor(data = {}, incomingCall) { //Данные из api flowwow this.data = data; //Входящий звонок this.call = incomingCall; //Объект с api this.api = new FlowwowAPI(); this.api.phone = this.call.callerid(); //Очередь активных звонков this.callQueue = []; //Очередь событий по времени this._timers = []; //Храним состояние, кому сейчас перенаправляется звонок this._state = ''; //Ссылка на запись звонка this._record = ''; //Активный сценарий this._scenario = ''; //Кто ответил this._respondent = ''; //При сбрасывании звонка скидываем всё очереди на звонок this.call.addEventListener(CallEvents.Disconnected, e => { this.cleanTimers(); this.dropAllCalls(); });
Теперь посмотрим подробнее сценарии для каждого типа пользователя.
Определяем звонящего: клиент с активным заказом
После приветствия нам нужно понять, кто наш пользователь? В данном сценарии мы получаем звонок от клиента с уже существующим заказом:
_orderScenario() { let api = this.api; let user = this.data.order.user; this.api.sendMessageInSlack(`Звонок от ${user.name} ${this.call.callerid()} по заказу ${FlowwowAPI.getOrderLink(this.data.order.id)}`, 'order'); this.call.handleTones(true); this.call.addEventListener(CallEvents.ToneReceived, e => { this.call.removeEventListener(CallEvents.ToneReceived, null); this.cleanTimers(); this.call.handleTones(false); switch (e.tone) { case '1': this.pressButtonOne(); break; case '2': this.pressButtonTwo(); break; default: this.api.sendMessageInSlack(`Клиент зачем-то нажал другую кнопку`); break; } }); //Проигрываем приветствие this.call.startPlayback(getSound('FW1')); let nextSound = setTimeout(() =>
После приветствия для клиента с активным заказом включается запись, где он попадает на развилку — можно связаться напрямую с магазином, в котором был сделан этот заказ, или обратиться в службу поддержки Flowwow:
{ //Проигрываем обращение к пользователю с активным заказом this.call.startPlayback(getSound('FW3')); }, 28000); this._timers.push(nextSound); }
Допустим, клиенту хочется узнать, что там с заказом. Для этого он нажимает кнопку с номером 1 на экране pressButtonOne() и переключается напрямую к магазину-партнеру.
pressButtonOne() { let shop = this.data.order.shop; let link = FlowwowAPI.getShopLink(shop.id);
Тут важно, чтобы магазин был на связи. Если звонок происходит уже во внерабочее время или коллеги не отвечают в течение 15 секунд, то запрос перехватим мы в службе поддержки Flowwow:
// Если магазин открыт (рабочее время) if(shop.is_opened){ this.callShop(); /// Объявляем о переключении на магазин this.call.startPlayback(getSound('FW6')); var playSound8 = setTimeout(() => { /// Называем имя клиента и заказ this.call.startPlayback(getSound('FW8')); }, 4000); this._timers.push(playSound8); var playSoundDouble8 = setTimeout(() => { /// Называем имя клиента и заказ this.call.startPlayback(getSound('FW8')); }, 14000); this._timers.push(playSoundDouble8);
Время в 15 секунд истекло, поэтому забираем заявку мы — в офис или на колл-центр:
var playSound4 = setTimeout(() => { /// Включаем запись о переводе на службу поддержки this.call.startPlayback(getSound('FW4')); }, 24000); this._timers.push(playSound4); var playSoundTwo8 = setTimeout(() => { this.api.sendMessageInSlack(`Исполнитель(${shop.name} ${shop.shop_phone} ${link} ${shop.city}) не ответил - перевод на офис`); this.call.startPlayback(getSound('FW8')); this.callFlowwow(); }, 32000); this._timers.push(playSoundTwo8); var tryCallCallCenter = setTimeout(() => { if(this._isReportingToday()) { this.api.sendMessageInSlack('Офис не ответил - перевод на кц'); } this.callCallCenter(); }, 42000); this._timers.push(tryCallCallCenter); } else { this.call.startPlayback(getSound('FW5')); this.callCallCenter(); } }
Колл-центр на аутсорсе в данном случае – это просто страховка, чтобы не оставить клиента без ответа, даже если все менеджеры в офисе заняты.
Отдельная история, как устроен перевод заявки напрямую в магазин или колл-центр Flowwow. Понять, куда ушел запрос, нам поможет функция _noticeAboutWhoAnswer
_call(phones) { } _noticeAboutWhoAnswer(phoneReceiver) { let api = this.api; let msg = ''; if (this._state == SHOP) { let shop = this.data.order.shop; let link = FlowwowAPI.getShopLink(shop.id); this._respondent = link; msg = `На звонок ответил ${this._state} ${link} ${shop.city} ${phoneReceiver}`; api.sendMessageInSlack(msg);
Если магазин не отвечает (как мы говорили, причины две — внерабочее время или просто невозможность ответить на звонок в данный момент), то происходит переключение заявки на офис Flowwow:
} else { api.log(phoneReceiver) .then(data => { let answerData = JSON.parse(data.text); let msg = ''; if (answerData.result.error == 0) { this._respondent = answerData.result.manager_name; msg = this._state == OFFICE ? `На звонок ответила ${answerData.result.manager_name} (${phoneReceiver})` : `На звонок номер звонящего ответил ${answerData.result.manager_name} (${phoneReceiver})`; } else { msg = `${answerData.result.description}` } api.sendMessageInSlack(msg); }) .catch(err => { Logger.write('Api error'); }); } }
Звонок принят, и нам отвечает менеджер офиса. Теперь нужно обнулить счетчик 15 секунд:
_callStart(e) { this.cleanTimers(); let api = this.api; let incomingCall = this.call; let outCall = this.dropAllCalls(e.call.number()); let time = 0;
И зафиксировать все данные по заказу — имя пользователя, номер заказа и имя менеджера:
this._noticeAboutWhoAnswer(e.call.number()); if (this._isOrderScenario() && this._state == SHOP) { let order_id = this.data.order.id.split('').join(' '); let user_name = this.data.order.user.name; outCall.say( `Вам звонит клиент ${user_name} Флаувау. Заказ ${order_id}`, Language.RU_RUSSIAN_FEMALE, { rate: 'slow', } ); time = 9000; } outCall.addEventListener(CallEvents.TransferComplete, function (e) { let apiTransfer = new FlowwowAPI(); apiTransfer.log(e.call.number()) .then(data => { let answerData = JSON.parse(data.text); let msg = ''; if (answerData.result.error == 0) { this._respondent = answerData.result.manager_name; msg = `Звонок перенаправлен на ${answerData.result.manager_name}` } else { msg = `${answerData.result.description}` } apiTransfer.sendMessageInSlack(msg); }) .catch(err => { Logger.write('Api error'); }); });
Соединение пользователей происходит несколькими методами:
VoxEngine.sendMediaBetween(incomingCall, outCall); VoxEngine.easyProcess(incomingCall, outCall); VoxEngine.addEventListener(AppEvents.Terminating, () => { recorder.stop(); this._sendCallRecord(); }); outCall.addEventListener(CallEvents.Disconnected, () => { incomingCall.hangup(); outCall.hangup(); }); }, time); }
На этом пути все: клиент позвонил по текущему заказу, выбрал возможность соединиться с магазином напрямую, и если не удалось до него дозвониться, то все равно получил ответ — в офисе Flowwow или колл-центре.
Вернемся на развилку, когда обращается клиент по текущему заказу. Выбор такой — звонить напрямую в магазин или в поддержку Flowwow? Когда у клиента, например, возник спор с магазином, он обращается к сотрудникам Flowwow. Нажимаем на экране кнопку с номером 2 pressButtonTwo()— это запрос в поддержку Flowwow, перенаправляем звонок в офис:
pressButtonTwo() { this.call.startPlayback(getSound('FW7')); if(this._isReportingToday()) { this.api.sendMessageInSlack('Перевод на офис'); } this.callFlowwow(); var soundFW8 = setTimeout(() => { this.call.startPlayback(getSound('FW8')); }, 4000); this._timers.push(soundFW8); var soundFW8Two = setTimeout(() => { this.call.startPlayback(getSound('FW8')); }, 14000); this._timers.push(soundFW8Two); var soundFW8Three = setTimeout(() => { this.call.startPlayback(getSound('FW8')); }, 24000); this._timers.push(soundFW8Three);
Прошло 15 секунд и нет ответа? Тогда берем запрос в колл-центр.
var callCallCenter = setTimeout(() => { this.call.handleTones(false); if(this._isReportingToday()) { this.api.sendMessageInSlack('Перевод на кц'); } this.callCallCenter(); }, 34000); this._timers.push(callCallCenter); }
Еще один вариант звонка от клиента: создание нового заказа
В этом случае мы проверяем, что у пользователя нет активных заказов, и после записи приветствия сразу переключаем звонок на офис Flowwow:
_otherScenario() { let data = this.data; let msg = ''; if (!data.unknown) { let link = ''; if (data.user.hasOwnProperty('orders_link')) { link = FlowwowAPI.slackLink(data.user.orders_link, 'все заказы пользователя '); } msg = `Звонок без заказа ${data.user.name} ${this.call.callerid()} ${link}`; } else { msg = `Звонок без заказа ${this.call.callerid()}`; } this.api.sendMessageInSlack(msg, 'other'); //Проигрываем запись приветствия this.call.startPlayback(getSound('FW1')); var tryCallFlowwow = setTimeout(() => { this.call.startPlayback(getSound('FW8')); // Переводим звонок на офис this.callFlowwow(); }, 28000); this._timers.push(tryCallFlowwow);
Так, 15 секунд истекло. Значит, переключаем заявку на колл-центр:
var tryCallCallCenter = setTimeout(() => { // Переводим звонок на call center this.callCallCenter(); }, 38000); this._timers.push(tryCallCallCenter); }
Если звонит магазин-партнер, что в этом случае?
Мы узнаем магазин по номеру телефона и коллеги услышат запись, в которой мы попросим их перейти в более оперативный канал связи — чат поддержки на сайте или в мобильном приложении. Поскольку взаимодействие с каждым партнером длится годами, важно хранить всю историю общения в текстовом виде. Кроме того, иногда вопрос не удается решить при первом обращении, тогда удобнее вернуться с ответом к чату, чем дополнительно фиксировать в CRM, с каким вопросом звонил партнер.Так сработает в коде:
_shopScenario() { //++++++++++++ Logger.write('Logger: shop scenario start'); let api = this.api; let shop = this.data.shop; let call = this.call.callerid(); if(this._isReportingToday()) { api.sendMessageInSlack(`Звонок из магазина номер ${call} id ${FlowwowAPI.getShopLink(shop.id)} город ${shop.city}`, 'shop'); } //Проигрываем запись для магазина-партнера this.call.startPlayback(getSound('FW2')); this.call.addEventListener(CallEvents.PlaybackFinished, call => { this.call.hangup(); }); }
Все статусы автоматически падают в один из чатов саппорта в Slack’е, чтобы команда могла оперативно среагировать, если что-то пошло не по плану. В эти чаты также стекаются репорты по всем остальным каналам с автоматическим распределением тикетов по сотрудникам. Наша внутренняя хелпдеск-система устроена так, чтобы каждый менеджер видел только тикеты, назначенные ему. Получив сообщение, менеджер по поддержке видит номер и статус заказа, название магазина-исполнителя, адрес получателя и все, что нужно, чтобы быстро разобраться, в чем дело.
Мы рассказали о нескольких сценариях, которые придумали для приема звонков в Flowwow. Возможно, наш опыт станет для кого-то примером, что автоматизация полезна не только при тысячах звонков, но и при сравнительно маленьких объемах обращений.
И еще лайфхак: занимаясь любой автоматизацией, мы отталкиваемся от проблем тех, кто в этом процессе участвует. Так, именно менеджеры саппорта на 80% выступают в роли заказчиков своей системы: объясняют, как им будет удобнее работать, и передают фидбек команде разработчиков.
ссылка на оригинал статьи https://habr.com/ru/articles/580528/
Добавить комментарий