artem
05.02.2018
6676

Asterisk — имитация нахождения вызова в очереди для системы статистики

В этой статье будет описан способ имитации нахождения вызова в очереди с помощью диалплана Asterisk — это может понадобится для того, чтобы система статистики, работающая с очередями (например Asternic Call Center Stats Pro), учитывала вызовы, которые на самом деле не попадали в очередь. Например, можно учитывать вызовы, пришедшие на операторов колл-центра в результате донабора и/или маршрутизации входящего вызова на ответственного, как вызовы, прошедшие через очередь.
(приведённые настройки верны для FreePBX 13)

Об имитации нахождения вызова в Asterisk

Контексты для имитации нахождения вызова в очереди

Для имитации нахождения вызова в очереди, в лог очереди будут добавляться записи с помощью приложения диалплана QueueLog. На различных этапах вызова будут вызываться различные контексты, все они приведены ниже (контексты нужно добавить в файл /etc/asterisk/extensions_custom.conf)

[app-fake-queue-enter]
exten => s,1,ExecIf($[«${ARG1}» = «»]?Return)

same  => n,Set(__FAKEQ_${ARG1}_ENTERTIME=${EPOCH})
same  => n,Set(__FAKEQ_${ARG1}_STATUS=IN QUEUE)
same  => n,Set(__FAKEQ_${ARG1}_ANSWERTIME=)
same  => n,Set(__FAKEQ_${ARG1}_UID=${UNIQUEID})
same  => n,Set(__FAKEQ_${ARG1}_CALLER=${CALLERID(num)})
same  => n,Set(__FAKEQ_${ARG1}_HOLDTIME=)
same  => n,Set(__FAKEQ_${ARG1}_RINGSTART=)

same  => n,QueueLog(${ARG1},${FAKEQ_${ARG1}_UID},NONE,DID,${CDR(did)})
same  => n,QueueLog(${ARG1},${FAKEQ_${ARG1}_UID},NONE,ENTERQUEUE,,${FAKEQ_${ARG1}_CALLER},1)
same  => n,Return

Контекст app-fake-queue-enter добавляет в лог очереди записи DID и ENTERQUEUE (запись DID не является стандартной и используется в FreePBX). Единственный аргумент — имя очереди.

[app-fake-queue-connect]
exten => s,1,ExecIf($[«${ARG1}» = «» | «${ARG2}» = «» | «${FAKEQ_${ARG1}_STATUS}» != «IN QUEUE» | «${FAKEQ_${ARG1}_ENTERTIME}» = «» | «${FAKEQ_${ARG1}_UID}» = «» ]?Return)
same  => n,GotoIf($[«${ARG3}» = «callee»]?callee)
same  => n,Set(__FAKEQ_${ARG1}_ANSWERTIME=${EPOCH})
same  => n,Set(__FAKEQ_${ARG1}_HOLDTIME=$[${FAKEQ_${ARG1}_ANSWERTIME} — ${FAKEQ_${ARG1}_ENTERTIME}])
same  => n,ExecIf($[${FAKEQ_${ARG1}_HOLDTIME} < 0]?Set(__FAKEQ_${ARG1}_HOLDTIME=0))
same  => n,Set(__FAKEQ_${ARG1}_STATUS=ANSWERED)
same  => n,Goto(cont1)

same  => n(callee),Set(MASTER_CHANNEL(__FAKEQ_${ARG1}_ANSWERTIME)=${EPOCH})
same  => n,Set(FAKEQ_${ARG1}_HOLDTIME=$[${FAKEQ_${ARG1}_ANSWERTIME} — ${FAKEQ_${ARG1}_ENTERTIME}])
same  => n,ExecIf($[${FAKEQ_${ARG1}_HOLDTIME} < 0]?Set(FAKEQ_${ARG1}_HOLDTIME=0))
same  => n,Set(MASTER_CHANNEL(__FAKEQ_${ARG1}_HOLDTIME)=${FAKEQ_${ARG1}_HOLDTIME})
same  => n,Set(MASTER_CHANNEL(__FAKEQ_${ARG1}_STATUS)=ANSWERED)

same  => n(cont1),QueueLog(${ARG1},${FAKEQ_${ARG1}_UID},${ARG2},CONNECT,${FAKEQ_${ARG1}_HOLDTIME},,1)
same  => n,Return

Контекст app-fake-queue-connect добавляет в лог очереди запись CONNECT. Аргументы контекста:
ARG1 — имя очереди
ARG2 — имя ответившего оператора
ARG3 — должен иметь значение callee, если Gosub в контекст app-fake-queue-connect был выполнен на канале вызванного абонента (например с помощью опции U приложения Dial). Если Gosub был выполнен на канале звонящего, ARG3 должен иметь любое другое значение (в том числе пустое)

[app-fake-queue-noanswer]
exten => s,1,ExecIf($[«${ARG1}» = «» | «${ARG2}» = «» | «${FAKEQ_${ARG1}_STATUS}» != «IN QUEUE» | «${FAKEQ_${ARG1}_ENTERTIME}» = «» | «${FAKEQ_${ARG1}_UID}» = «» | «${FAKEQ_${ARG1}_RINGSTART}» = «» ]?Return)
same  => n,Set(RINGNOANSWER_TIME=$[(${EPOCH} — ${FAKEQ_${ARG1}_RINGSTART})*1000])
same  => n,ExecIf($[${RINGNOANSWER_TIME} < 0]?Set(RINGNOANSWER_TIME=0))
same  => n,QueueLog(${ARG1},${FAKEQ_${ARG1}_UID},${ARG2},RINGNOANSWER,${RINGNOANSWER_TIME})
same  => n,Return

Контекст app-fake-queue-noanswer добавляет в лог очереди запись RINGNOANSWER. Аргументы контекста:
ARG1 — имя очереди
ARG2 — имя оператора, пропустившего звонок

[app-fake-queue-leave]
exten => s,1,ExecIf($[«${ARG1}» = «» | «${FAKEQ_${ARG1}_ENTERTIME}» = «» | «${FAKEQ_${ARG1}_UID}» = «»]?Return)
same  => n,Set(NOW=${EPOCH})
same  => n,GotoIf($[«${FAKEQ_${ARG1}_STATUS}» = «ANSWERED»]?answered)
; no answer
same  => n,GotoIf($[«${ARG2}» = «» | «${FAKEQ_${ARG1}_RINGSTART}» = «»]?no-ring)
same  => n,Gosub(app-fake-queue-noanswer,s,1(${ARG1},${ARG2}))
same  => n(no-ring),Set(ABANDON_TIME=$[${NOW} — ${FAKEQ_${ARG1}_ENTERTIME}])
same  => n,ExecIf($[${ABANDON_TIME} < 0]?Set(ABANDON_TIME=0))
same  => n,QueueLog(${ARG1},${FAKEQ_${ARG1}_UID},NONE,ABANDON,1,1,${ABANDON_TIME})
same  => n,Goto(cont1)
; answered
same  => n(answered),ExecIf($[«${ARG2}» = «» | «${FAKEQ_${ARG1}_ANSWERTIME}» = «» | «${FAKEQ_${ARG1}_HOLDTIME}» = «»]?Return)
same  => n,Set(QUEUE_EVENT=${IF($[«${ARG3}» = «COMPLETEAGENT»]?${ARG3}:COMPLETECALLER)})
same  => n,Set(CALL_TIME=$[${NOW} — ${FAKEQ_${ARG1}_ANSWERTIME}])
same  => n,ExecIf($[${CALL_TIME} < 0]?Set(CALL_TIME=0))
same  => n,QueueLog(${ARG1},${FAKEQ_${ARG1}_UID},${ARG2},${QUEUE_EVENT},${FAKEQ_${ARG1}_HOLDTIME},${CALL_TIME},1)

same  => n(cont1),Set(__FAKEQ_${ARG1}_ENTERTIME=)
same  => n,Set(__FAKEQ_${ARG1}_STATUS=)
same  => n,Set(__FAKEQ_${ARG1}_ANSWERTIME=)
same  => n,Set(__FAKEQ_${ARG1}_UID=)
same  => n,Set(__FAKEQ_${ARG1}_CALLER=)
same  => n,Set(__FAKEQ_${ARG1}_HOLDTIME=)
same  => n,Set(__FAKEQ_${ARG1}_RINGSTART=)
same  => n,Return

Контекст app-fake-queue-leave добавляет в лог очереди запись COMPLETECALLER/COMPLETEAGENT, если был ответ на вызов, или ABANDON, если ответа не было (также вызывает app-fake-queue-noanswer при необходимости). Аргументы контекста:
ARG1 — имя очереди
ARG2 — имя оператора, принявшего или пропустившего звонок
ARG3 — должен иметь значение COMPLETEAGENT, если вызов завершил оператор (иначе в лог будет записано COMPLETECALLER)

Применение сценария на примере прямого донабора в IVR

Ниже приведены параметры тестового меню IVR.

Параметры текстового меню IVR

Вызовы из IVR идут в очередь 10000, сделаем так, чтобы при наборе в IVR номера оператора очереди 10000, в лог очереди добавились все необходимые сообщения для системы статистики (для этого опция Enable Direct Dial должна быть отключена).
Порядок вызова контекстов следующий:
1) app-fake-queue-enter для записи ENTERQUEUE
2) app-fake-queue-connect для записи CONNECT (если был ответ)
3) app-fake-queue-leave для записи COMPLETECALLER/COMPLETEAGENT или RINGNOANSWER и ABANDON
(app-fake-queue-noanswer в данном сценарии вызывается только из app-fake-queue-leave)

Донабор для IVR лучше реализовать в виде отдельного контекста, чтобы можно было включать его в различные IVR (если в IVR используются разные очереди, нужно устанавливать переменную CUR_QUEUE до входа в IVR, например, через Custom Destination).

[ext-direct-dial-fakeq]
exten => _XXX,1,Set(DIAL_STR=${DB(DEVICE/${EXTEN}/dial)})
same  => n,GotoIf($[${DIALPLAN_EXISTS(ext-local,${EXTEN},1)} = 0]?invalid)
same  => n,GotoIf($[«${DIAL_STR}» = «»]?invalid)
same  => n,GotoIf($[«${EXTENSION_STATE(${EXTEN}@ext-local)}» != «NOT_INUSE»]?continue)
same  => n,Set(CUR_QUEUE=10000)
same  => n,GotoIf($[${REGEX(«(^|,)Local/${EXTEN}@from-queue/n($|,)» ${QUEUE_MEMBER_LIST(${CUR_QUEUE})})} = 0]?skip_fake_queue)
same  => n,Set(FAKEQ_NUM=${CUR_QUEUE})
same  => n,Set(FAKEQ_AGENT_NUM=${EXTEN})
same  => n,Set(FAKEQ_AGENT_NAME=${DB(AMPUSER/${FAKEQ_AGENT_NUM}/cidname)})
same  => n,Set(FAKEQ_RESULT=COMPLETECALLER)
same  => n,ExecIf($[«${FAKEQ_AGENT_NAME}» = «»]?Set(FAKEQ_AGENT_NAME=${FAKEQ_AGENT_NUM}))
same  => n,ExecIf($[«${FAKEQ_${FAKEQ_NUM}_STATUS}» = «»]?Gosub(app-fake-queue-enter,s,1(${FAKEQ_NUM})))
same  => n,Set(CHANNEL(hangup_handler_push)=app-fake-queue-leave,s,1(${FAKEQ_NUM},${FAKEQ_AGENT_NAME},${FAKEQ_RESULT}))
same  => n,Set(__FAKEQ_${FAKEQ_NUM}_RINGSTART=${EPOCH})
same  => n(skip_fake_queue),Gosub(sub-record-check,s,1(exten,${EXTEN},yes))
same  => n,Set(DIALSTATUS=)
same  => n,Dial(${DIAL_STR},15,tgU(app-fake-queue-connect^${FAKEQ_NUM}^${FAKEQ_AGENT_NAME}^callee))
same  => n,Set(FAKEQ_RESULT=COMPLETEAGENT)
same  => n,ExecIf($[«${DIALSTATUS}» = «ANSWER»]?Hangup)
same  => n(continue),Gosub(app-fake-queue-leave,s,1(${FAKEQ_NUM},${FAKEQ_AGENT_NAME}))
same  => n,Goto(t,1)
same  => n(invalid),Set(INVALID_EXTEN=${EXTEN})
same  => n,Goto(i,1)

Теперь нужно найти в файле /etc/asterisk/extensions_additional.conf имя контекста созданного IVR, в данном примере это контекст ivr-2. В файле /etc/asterisk/extensions_override_freepbx.conf нужно включить контекст ext-ivr-direct-dial в ivr-2.

[ivr-2]
include => ext-direct-dial-fakeq

Настройка завершена.

Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Остались вопросы?

Я - Кондрашин Игорь, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.

VoIP оборудование


ближайшие курсы

10 доводов в пользу Asterisk

Распространяется бесплатно.

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

Безопасен в использовании.

Любое программное обеспечение может стать объектом интереса злоумышленников, в том числе телефонная станция. Однако, сам Asterisk, а также операционная система, на которой он работает, дают множество инструментов защиты от любых атак. При грамотной настройке безопасности у злоумышленников нет никаких шансов попасть на станцию.

Надежен в эксплуатации.

Время работы серверов некоторых наших клиентов исчисляется годами. Это значит, что Asterisk работает несколько лет, ему не требуются никакие перезагрузки или принудительные отключения. А еще это говорит о том, что в районе отличная ситуация с электроэнергией, но это уже не заслуга Asterisk.

Гибкий в настройке.

Зачастую возможности Asterisk ограничивает только фантазия пользователя. Ни один конструктор шаблонов не сравнится с Asterisk по гибкости настройки. Это позволяет решать с помощью Asterisk любые бизнес задачи, даже те, в которых выбор в его пользу не кажется изначально очевидным.

Имеет огромный функционал.

Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.

Интегрируется с любыми системами.

То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.

Позволяет телефонизировать офис за считанные часы.

В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.

Отличная масштабируемость.

Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.

Повышает управляемость бизнеса.

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

Снижает расходы на связь.

Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.