Asterisk: Автоинформирование вызываемого абонента перед соединением с оператором

Всем привет!
Решил поделиться собственным опытом в некоторых особенностях работы Dialplan’а.

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

Исходные данные:
Сервер с Asterisk 1.8, без Web-интерфейса, настроенный как телефонный шлюз-маршрутизатор.
Конфигурация задаётся редактированием конфигурационных файлов в каталоге /etc/asterisk/
Необходимо воспроизвести сообщение вызываемому абоненту, а затем уведомить вызывающего о готовности его слушать.
Кому интересно, добро пожаловать под кат.
При входящем звонке вызывающий абонент прослушивает приветствие и сообщение IVR (это настраивается на большом Call-центре с Web-интерфейсом, похожим на стандартный FreePBX) в том числе, что «сообщение записывается». Возникла потребность данное же сообщение воспроизвести при звонке от операторов Call-центра к абонентам. В Web-интерфейсе нет возможности добавить подобную функцию, пришлось импровизировать.

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

Кусочек диалплана, отвечающего за сообщение:

Скрытый текст
[call-centr-context]
etxen => _X.,n,Macro(SayAllCallsRec)

[macro-SayAllCallsRec]
exten => s,1,NoOp(${CALLERID(num})
exten => s,n,GotoIf($["${CALLERID(num)}" = "123456789"]?say)
; Other numbers here. NO FULL if-then-else!
exten => s,n(notsay),NoOp()
exten => s,n,Dial(SIP/${MACRO_EXTEN}@TrunkOut,50)
exten => s,n,GoTo(end)
exten => s,n(say),NoOp()
exten => s,n,Set(LIMIT_PLAYAUDIO_CALLER=yes)
exten => s,n,Set(LIMIT_PLAYAUDIO_CALLEE=no)
exten => s,n,Set(LIMIT_CONNECT_FILE=/var/lib/asterisk/sounds/beep)
exten => s,n,Dial(SIP/${MACRO_EXTEN}@TrunkOut,50,L(9999999)A(/var/lib/asterisk/sounds/AllCallsRec))
exten => s,n,Goto(end)
exten => s,n(end),NoOp()
exten => s,n,HangUp()


Данный участок диалплана рабочий и оттестирован. Можно смело удалить все строки, содержащие NoOp, т.к. они выполняют только функции записи в лог консоли.

Теперь пройдёмся по некоторым участкам.
В контексте, которому принадлежит транк Call-центра, общий экстеншн «Для любого номера» (_X.) отправляет вызов в макрос, где происходит:
— Проверка на «белый» список.
— Поднятие трубки,
— Воспроизведение КПВ,
— Сообщение вызываемому участнику,
— Сообщение вызывающему.
К сожалению, описанный ещё в 2007-м году баг (или фича) (пруф) с неодновременным сообщением файла «LIMIT_CONNECT_FILE» всё ещё существует в версии 1.8, а обновлять сервер нет возможности.
Таким образом, если пытаться уведомлять обоих участников вызова, используя флаги LIMIT_PLAYAUDIO_CALLER=yes, LIMIT_PLAYAUDIO_CALLEE=yes, то сообщение будет воспроизведено последовательно. Сначала — абоненту Б, затем — абоненту А, а у другого абонента в этот момент будет тишина.

Теперь разберём диалплан. Начнём с команды вызова, как самой длинной.
Dial (SIP/${MACRO_EXTEN}@TrunkOut — Вызов будет направлен с номерм, переданным в макрос на транк «TrunkOut»
,50 — Ждать ответа будем до 50 секунд
, L (9999999) — лимит времени соединения в милисекундах (не требуется, но иного способа сообщить вызываюшему, что вызываемый прослушал «приветствие» не нашлось.
A (путь-к-файлу) — сообщение вызываемому

exten => s, n, Set (LIMIT_PLAYAUDIO_CALLER=yes) — Воспроизводить вызывающему
exten => s, n, Set (LIMIT_PLAYAUDIO_CALLEE=no) — Воспроизводить вызываемому
exten => s, n, Set (LIMIT_CONNECT_FILE=/var/lib/asterisk/sounds/beep) — Путь к файлу, который нужно будет воспроизвести.

Заданные чуть выше переменные определяют, кому будет воспроизведён звук «beep». В нашем случае, тому, кто звонит ИЗ Call-центра.

И, собственно, как выглядит сам вызов по шагам:
Абонент Call-центра набирает номер. Вызов приходит на шлюз Asterisk. Звонок падает в макрос. Номер А сравнивается со списком. В случае несовпадения, вызов идёт без дополнительных ключей. В случае совпадения, звонку добавляются ключи.
Абонент Б снимает трубку, прослушивает сообщение (Ключ A ()). В этот момент у абонента А будет тишина (после длиных гудков). По окочании сообщения, абонент А услышит звук из файла «beep.gsm» (должен быть в каталоге /var/lib/asterisk/sounds/). По какой причине именно в формате gsm — не ясно. И только после этого, каналы будут соединены.

Так же, можно заменить тишину для абонента А на гудки КПВ. Для этого нужно изменить строки:

exten => s,n(say),NoOp()
exten => s,n,Answer()
exten => s,n,Playtones(420)
exten => s,n,Set(LIMIT_PLAYAUDIO_CALLER=yes)
exten => s,n,Set(LIMIT_PLAYAUDIO_CALLEE=no)
exten => s,n,Set(LIMIT_CONNECT_FILE=/var/lib/asterisk/sounds/beep)
exten => s,n,Dial(SIP/${MACRO_EXTEN}@TrunkOut,50,L(9999999)A(/var/lib/asterisk/sounds/AllCallsRec))


Добавлены Answer () — собирается канал, и Playtones () — воспроизводится КПВ. В таком случае, вызывающий получит генерируемые Asterisk-шлюзом сигналы КПВ.
К сожалению, в этом случае RTP-сообщения перед соединением от вышестоящих серверов будут заменены гудками. Например, сообщение без ответа «Абонент не может быть вызван» от сотовых и им подобные, которые фактически, не приводят к созданию канала (ответ 200 — OK) будут проигнорированы, и вызывающий получит звук вида (длинные гудки, длинные гудки, короткие гудки…), но зато не будет паузы тишины.

Так же, можно добавить к Dial () ключ r — генерация системного КПВ, но то будет выбираться именно из системных звуков и будет (чаще всего) сильно отличаться от классического звука контроля посылки вызова.

На этом всё. Надеюсь, заметка поможет кому-то ещё сэкономить день-другой, пытаясь заставить шлюз выполнять необходимую последовательность действий.

© Habrahabr.ru