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 — генерация системного КПВ, но то будет выбираться именно из системных звуков и будет (чаще всего) сильно отличаться от классического звука контроля посылки вызова.

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

    Comments 8

      +1
      Оч интересно, по больше таких реализаций. Спасибо за статью не могу голосовать пока нет кармы ))
        0
        У меня аналогичная ситуация.
        На то, чтобы собрать такой материал ушло без малого 2 дня. Не скажу, что постоянно копался на форумах — тогда бы собрал всё быстрее. Приходилось экспериментировать с рабочим сервером, в том числе.
        +1
        Еще один трюк покажу, может знаете, но все же:

        Здеся, когда кого-то набираем, указиваем опцию: «M(callanswered)», это макро которое тригернеться српазу перед тем когда делаеться бридж:
        [from-inside]
        exten => _X.,1,Noop(DIALING OUT)
        same => n,Set(__CALLEE=${MACRO_EXTEN})
        same => n,Dial(SIP/${MACRO_EXTEN}@TrunkOut,50,M(callanswered))
        

        Здесь, мы делаем ориджинейт, одна нога пойдет в bargein, который врежеться в звонок, вторая нога, проиграет файл:
        [macro-callanswered]
        exten => s,1,Noop(${CALLEE} answered the call)
        same => n,Originate(Local/${CALLEE}@bargein,app,Playback,/var/lib/asterisk/sounds/AllCallsRec) 
        

        Здесь просто врезаемся в звонок:
        [bargein]
        exten => _X.,1,Noop(PLAY MUSIC THROUGH CHANSPY)
        same => n,Answer()
        same => n,ChanSpy(SIP/${EXTEN},BEq)
        same => n,Hangup()
        

        Таким образом сразу после соединения, оба услышат одно и тоже сообщение. Это на половину псевдокод, так что нуждаеться в правках для вашей ситуации.
          0
          Однако, действительно не знал такого трюка. Да и выглядит он чуть более громоздко, но позже постараюсь проверить его работу.
          У меня выход из Dial не происходит, а переход в макрос в собственно, команде Dial() приводил к отбою вызывающего абонента. Плюс, информация по собственно, переброске вызова в макрос несколько противоречива — каналы фактически хоть и соединены, но канал вызывающего попадает в контекст с приоритетом N, а канал вызываемого — N+1. Но у меня воспроизвести не удалось, или мешали другие звонки.

          Позже постараюсь проверить и дополнить статью. Спасибо =)
            0
            Опция: «M(callanswered)», запускает макро отдельно, так сказать в отдельном треде. и не влияет например на продолжение исполнения «дайлплана» в контексте from-inside, но в нем буду доступны «inherited variables», с канала который это тригернул.
          +1
          Вот бы еще такой скрипт, который уведомляет абонента, звонящего в call-центр, что оператор соизволил оторваться от вконтакта и ответить на звонок. :-\ Обычно это происходит минут через 20-30.

          И чтобы он работал без наличия Asterisk, просто на смартфоне.
            0
            Это называется en.wikipedia.org/wiki/Virtual_queue, теоретически удобная штука, хотя почему-то ни разу не встречал на практике
              0
              Мы реализовывали такое для Vicidial и для астериска реализовать можно при отключении пользователя оставив канал в queue и при соединении с оператором подключить к вызову звонившего.

          Only users with full accounts can post comments. Log in, please.