Asterisk. Разгружаем секретаря/диспетчера/первую линию тех. поддержки

в 11:24, , рубрики: asterisk, ip-телефония, voip, конфигурирование, настройка, метки: , , , ,

Надеюсь, что этой статьей получится превратить статьи по конфигурации Asterisk в целый цикл статей, начало которому я, для себя положил около 2-х лет назад в этой статье.

Я стараюсь описывать свою разработку (а возможно кому-то и приподниму завесу тайны) нетривиальных, или просто интересных, по моему мнению, диалпланов.

Я сразу оговорюсь, что не буду описывать настройку модулей, конфигурационных файлов подключения к базе данных и тому подобного, так как это не цель статьи, тем более все это есть в wiki и в книгах по Asterisk. Так же я сразу скажу что делаю все это на голом Asterisk без freePBX так как считаю реализацию этого веб-интерфейса неправильной, нелогичной и ущербной.

Начну я с одного из сценариев в организациях — разгрузки секретаря. Эту же логику будет возможно применить и к Call центрам, и диспетчерам такси (немного изменив Dialplan).

Сценарий:
-Звонок на секретаря.
-Сектретарь поднимает трубку -просят соединить с отделом продаж.
-Секретарь переводит звонок на группу менеджеров.
-Менеджер разговаривает с клиентом.
-При повторном звонке в течении 24 часов клиент попадает сразу к принявшему его впервые менеджеру.

Кому интересно прошу под кат.

Отступление:
Предугадывая вопрос Зачем? в комментариях:
Как правило у клиентов с менеджерами достаточно тесное общение, но не всегда удобно давать свой личный номер телефона- он может забыться или потеряться. Менеджер может забыть и не дать его ну или еще что-то приключится с ним и клиент останется без прямой связи. У секретаря так же есть ряд обязанностей и работать телефонистом полный рабочий день ему/ей накладно. Даже при наличии внутреннего номера у каждого менежера всегда проще запомнить 6-10 цифр телеонного номера вместо 13-15.

Задача:
— Избавить секретаря от перевода одного и того же клиента на менеджеров в течение определенного промежутка времени.
— Привязать клиента к отпределенному менеджеру

Логика реализации:
После того как менеджер взял трубку Asterisk «запомниает» номер клиента и при повторном звонке в ближайшие 24 часа соединяет клиента именно с тем менеджером который с ним общался.

Реализация.

Asterisk должен как то узнавать клиента который ему звонит, а так же должен запоминать менеджера, который поднял трубку. Эта информация будет храниться в базе данных и будет доступна астериску по запросу.

Создаем базу данных или заводим таблицу в уже имеющейся (Я использовал mysql).

Cозданная таблица имеет 4 поля:
id Уникальное значение строки
number Номер клиента
date Дата в UTC
agent Номер агента принявшего вызов

Все нужные обращения к таблице я уложил в 4 запроса:

GET_DATA Как видно из названия: получаем данные о позвонимшем клиенте ориентируясь на его номер
SELECT agent, date, number FROM dbname.clients WHERE number=Номер клиента.

SET_DATA Записываем данные в базу
INSERT INTO dbname.clients (number,date,agent) VALUES ('Номер клиента','Дата в UTC','Номер агента принявшего вызов')

UPDATE_TIME Обновляем время при повторном звонке
UPDATE dbname.clients SET date=Текущая дата WHERE number=Номер клиента

DELETE_DATA Удаляем клиента из базы
DELETE FROM dbname.clients WHERE number=Номер клиента AND date=Дата в UTC

Теперь, кода инструмент для работы с базой готов осталось написать dialplan который будет обрабатывать это соединение.

Шаг первый- опознаем клиента:
Все входящие звонки изначально приходят к секретарю (контекст from-external). Каждый такой звонок- потенциальный клиент, поэтому мы будем проверять — а не зонил ли он нам уже?
Для этого сначала запросим в таблице значения номера агента, даты и номера звонящего с помощью запроса GET_DATA

[from-external]
exten=>_X.,1,Set(ARRAY(AGENT,DATE,NUMBER)=${ODBC_GET_DATA(${CALLERID(num)})")

Если в базе такого номера нет то переменная number останется пустой- это и будет нашим основным параметром сравнения для того чтобы понять в какую сторону пойдет диалплан далее:

exten=>_X.,n,GotoIf($[${NUMBER}!=""]?comparedate:dialexten)

Если переменная number не пуста, значит клиент уже звонил нам, следующим шагом нужно узнать как давно он нам звонил. Для этого нужно сравнить дату в таблице с датой текущей, для этого идем по ветке comparedate, вычитанем из текущей даты значение переменной date полученное на самом первом шаге и сохраняем все это в переменной DATERESULT:

exten=>_X.,n(comparedate),Set(DATERESULT=${MATH(${EPOCH}-${DATE},i)})

Затем сравниваем ее с магическим числом 86400 (не буду раскрывать секрет этого числа, знающие сами поймут, а незнающие посмотрят в википедии, что такое Unix Time Stamp, расширят свой кругозор и вспомнят математику), и исходя из того какой результат получили уходим по веткам dialagent и deleteagent. Здесь важно обратить внимание на синтаксис — без кавычек и с проблеом между знаком "<" и значениями, если написать по-другому GotoIf отработает некорректно:

exten=>_X.,n,GotoIf($[${DATERESULT} < 86400]?dialagent:deleteagent)

Метка dialagent отвечает за вызов менеджера к которому прикреплен клиент. После вызова мы обновляем значение времени, так как сотрудничество продолжается:


exten=>_X.,n(dialagent),Dial(SIP/${AGENT},,Ttg)

exten=>_X.,n,Set(ODBC_UPDATE_TIME()=${EPOCH},${NUMBER})

exten=>_X.,n,Hangup()

Метка deleteagent переводит диалплан в ветку удаления вызова секретаря с предварительным удалением из базы данных пользователя, происходит это когда пользователь не звонил в компанию более 24 часов.

exten=>_X.,n(deleteagent),Set(ODBC_DELETE_DATA()=${NUMBER},${DATE})
exten=>_X.,n(dialexten),Dial(SIP/${EXTEN},,Ttg)

Примечание:
Я не стал писать удаление записи из базы по истечению времени через диалплан asterisk потому что в данной реализации это костыль и гораздо правильнее было бы запускать раз в сутки по cron скрипт хотя бы на том же php и чистить базу от посроченных записей

На этом определение статуса звонящего пользователя закончивается и происходит перевод пользователя в очередь.
Важно при переводе использовать blindtransfer, чтобы CALLERID звонящего клиента высветился у менеджера. Если же ну очень надо использовать extended transfer то стоит перед переводом установить CALLERID секретаря в значение CALLERID клиента, чтобы в таблицу в поле number попал именно номер клиента, а не секретаря. Это первое решение коорое мне пришло в голову. если у кого то возникнут еще идеи — буду рад услышать.

И так, при переводе по blindtransfer на номер очереди нужно учесть 1 момент:
Нам понадобится номер менеджера, поднявшего трубку. Здесь я отойду от правила не углубляться в конфиги, потому, что чтобы получить это значение в конфигурационном файле очереди необходимо выствить значение setinterfacevar=yes. Эта настройка позволит видеть значение переменной MEMBERINTERFACE, которая в себе как раз и хранит номер члена очереди, поднявшего трубку.
Чтобы ее обработать можно пойти 2-мя путями:

1. Использовать макрос в вызове очереди:

exten=>500,1,Queue(Queue1,t,,,,,queue-answer)

[macro-queue-answer]

exten => s,1,Set(AGENT=${CUT(MEMBERINTERFACE,/,2)})

2. Использовать hangup экстеншн

exten=>500,1,Queue(Queue1,t)

exten => h,1,Set(AGENT=${CUT(MEMBERINTERFACE,/,2)})

Проблема в том, что по оканчанию вызова, чтобы занести менеджера в базу данных необходимо получить его CALLERID. Способ 2 не пробрасывает CALLERID менеджера в макорс, а передача параметра в макрос при вызове его из очереди невозможна — как я не пытался, пробросить его у меня не получилось. Поэтому способ был обматерен и проклят, и я остановился на способе 1.

С этим способом так же есть нюанс- если я буду отлавилвать событие hangup в контексте from-external, то любой hangup (будь то менеджер или секретарь) попадут под обработку, а этого не нужно. Поэтому при переводе звонка в очередь, сама очередь переводится в новый контекст, в котором и обрабатывается:


exten=>500,1,Answer()

exten=>500,n,Goto(queue-answering,s,1)

[queue-answering]

exten=>s,1,Queue(Queue1,t)

exten => h,1,Set(AGENT=${CUT(MEMBERINTERFACE,/,2)})

После того как получен удобочитаемый номер менеджера, осталось записать данные в базу:

exten => h,n,Set(ODBC_SET_DATA()="${CALLERID(num)}","${EPOCH}","${AGENT}")

На этом диалплан закончен. его можно улучшить и усовершенствовать добавив IVR при обработке вызова уже звонившего клиента и предлагая ему разные варианты совершения вызова. Так же можно добавить анонс звонка для менеджера, когда он берет трубку, чтобы он знал что, это звонок от нового клиента, или просто от клиента.

В общем и целом данный диалплан это базис, на основе которого можно развернуть очень удобное и недорогое для производительности asterisk решение (не прибегая к AGI например), которое используют как фирмы такси, так и некоторые call-центры.

Автор: Ovoshlook

Источник

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


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