Яндекс.Метрика

Курс Zabbix: мониторинг Asterisk и VoIP

Курс Zabbix: мониторинг Asterisk и VoIP с 10 ноября по 14 ноября

Количество
свободных мест

8 Записаться

Курс по Asterisk

Интенсив-курс по Asterisk с 29 сентября по 3 октября

Количество
свободных мест

5 Записаться

Курсы по Mikrotik MTCNA

Курсы по Mikrotik MTCNA с 13 октября по 16 октября

Количество
свободных мест

3 Записаться
Маршрутизация в Asterisk и во FreePBX (курс молодого бойца)
46
Мастер-класс
Александр Абабий
Маршрутизация в Asterisk и во FreePBX (курс молодого бойца)

Маршрутизация в Asterisk и во FreePBX (курс молодого бойца)

Доклад систематизирует практику построения диалпланов в Asterisk с акцентом на паттерны набора, специальные экстеншены, поведение приложений Dial, Playback/Background, работу с переменными и функциями, а также на типовые приёмы IVR и запись разговоров (включая постобработку).

Паттерны экстеншенов

  • X — любая цифра 0–9. Чаще всего используется в базовых масках.
  • Z — цифра 1–9 (исключает 0).
  • N — цифра 2–9 (традиционно применялось в шаблонах NANP/США).
  • [] — список/диапазон допустимых символов в позиции, например [78] или [3-6].
  • . — «один или более символов» (продолжение набора не ограничивается длиной).
  • ! — «ноль или более символов» c немедленным сопоставлением (early match): Asterisk может принять решение без ожидания дополнительных цифр. Использовать осторожно — может приводить к неоднозначностям.

Примеры:

  • Исходящие: поддержать набор с 8 или 7, затем мобильный префикс 9 и далееexten => _[78]9X.,1,NoOp(Outbound mobile)
  • Любая последовательность из 2+ цифр:
  • X.  => минимум 2 символа (X + как минимум один по ‘.’)
  • X!  => минимум 1 символ (X + возможно пустое продолжение по ‘!’)

Приоритет масок: чем конкретнее экстеншен (например, точное число «1001» против шаблона _1XXX), тем выше его приоритет при выборе.

Специальные экстеншены

  • s — стартовый экстеншен. Исторически пришёл из аналоговых линий (FXO), где нет DID. Все входящие на порт FXO направлялись на s. В IVR s удобно использовать как точку входа, чтобы не попасть в рекурсию при DTMF (нажатия не совпадут со стартовым экстеншеном).
  • i — invalid. Обрабатывает неверный ввод в IVR (например, нажат несуществующий пункт).
  • t — timeout. Срабатывает при отсутствии ввода за отведённое время.
  • h — hangup. Выполняется после завершения вызова; предназначен для постобработки (логирование, конвертация записей и т.д.). Набором с телефона не вызывается.

Поведение Dial() и логика ветвления

Типичный фрагмент:

exten => 1001,1,NoOp(Before Dial)
 same => n,Dial(PJSIP/1001)
 same => n,NoOp(After Dial - executed only if not answered)

exten => h,1,NoOp(Hangup hook - always runs after call ends)

По умолчанию (без опции g у Dial) дальнейшие приоритеты после Dial() выполняются, если вызов не был отвечен (состояния BUSY/NOANSWER/CONGESTION/…). Если вызов был отвечен и затем завершён, выполняется exten => h,1,…. Именно поэтому постобработку (например, архивирование записи) рационально помещать в h.

NoOp и диагностика

Приложение NoOp() (в расшифровке No Operation) — удобный способ вывести в консоль/лог диагностическое сообщение:

same => n,NoOp(UNIQUEID=${UNIQUEID} EXTEN=${EXTEN} DIALSTATUS=${DIALSTATUS})

Используется для трассировки диалплана и значений переменных.

Переменные и функции

Уровни и доступ

  • Создание/присвоение: Set(name=value)
  • Чтение: ${name}

Часто применяемые встроенные переменные:

  • ${EXTEN}, ${CONTEXT}, ${UNIQUEID}, ${DIALSTATUS}, ${CALLERID(num)}, ${CALLERID(name)} и др.

CALLERID() — это функция, как и STRFTIME(). Чтение/запись выполняется через синтаксис функций.

STRFTIME() для меток времени и каталогов

  • Формирование даты для имени файла/каталогаSet(DATE=${STRFTIME(${EPOCH},,%Y/%m/%d)})
  • Пример имени записи:Set(REC_BASE=${DATE}/${CALLERID(num)}_${EXTEN}_${UNIQUEID})

Примечание: при записи в подкаталоги следует обеспечить их существование заранее (например, через System(/bin/mkdir -p …) в момент старта).

Переходы: Goto, метки, GotoIf, GotoIfTime

Метки и n

При использовании same => n(…) допускается именовать приоритет и затем переходить к нему:

exten => s,1,NoOp(Start)
 same => n(start),Background(main-menu)
 same => n,WaitExten(5,m)        ; ожидание DTMF + музыка ожидания по опции 'm'
 same => n,Goto(s,start)         ; возврат к началу меню

Важно указывать правильный экстеншен при переходе к метке: Goto(s,start) перейдёт к метке start в exten => s.
Условия

Условный переход

same => n,GotoIf($[ "${CALLERID(num)}" = "303" ]?prio3:next)

В зависимости от времени (рабочее/нерабочее)

same => n,GotoIfTime(09:00-18:00,mon-fri,*,*?open:closed)

Gosub вместо Macro

Рекомендуется применять Gosub() для повторно используемых блоков. В отличие от устаревшего Macro, возврат из подпрограммы осуществляется явным Return().

Основной поток

exten => _X.,1,Gosub(sub-record,${EXTEN},1)
 same => n,Dial(${PJSIP_DIAL_CONTACTS(${EXTEN})})

Подпрограмма записи

[sub-record]
exten => _X.,1,NoOp(Recording bootstrap)
 same => n,Set(DATE=${STRFTIME(${EPOCH},,%Y/%m/%d)})
 same => n,Set(REC_BASE=${DATE}/${CALLERID(num)}_${EXTEN}_${UNIQUEID})
 same => n,Return()

Background vs Playback

  • Background(file) — проигрывает файл и параллельно слушает DTMF; при вводе — управление передаётся на соответствующий экстеншен (нажатую цифру).
  • Playback(file) — проигрывает без прослушивания DTMF.

В IVR чаще применяется Background.

[ivr-main]
exten => s,1,Background(company/welcome)
 same => n,WaitExten(5,m)            ; 5 сек, 'm' — музыка ожидания
exten => 1,1,Goto(ivr-sales,s,1)
exten => 2,1,Goto(ivr-support,s,1)
exten => t,1,Playback(vm-goodbye)    ; timeout
 same => n,Goto(ivr-main,s,1)
exten => i,1,Playback(pbx-invalid)   ; invalid
 same => n,Goto(ivr-main,s,1)

Альтернатива — Read(var,file,timeout,maxdigits,…), когда требуется собрать конкретное количество цифр и сохранить их в переменную.

Проверка статуса устройств

Функция DEVICE_STATE() позволяет проверять состояние устройства/эндпоинта и ветвить логику:

Пример (упрощённо)

exten => 1001,1,Set(STATE=${DEVICE_STATE(PJSIP/1001)})
 same => n,GotoIf($["${STATE}"="NOT_INUSE"]?ring:busy)
 same => n(busy),Playback(line-busy)
 same => n,Hangup()
 same => n(ring),Dial(${PJSIP_DIAL_CONTACTS(1001)})

PJSIP: вызов на все зарегистрированные устройства

Для мульти-регистраций корректнее использовать функцию PJSIP_DIAL_CONTACTS(endpoint), которая возвращает готовую dial-строку на все контактные адреса AOR:

same => n,Dial(${PJSIP_DIAL_CONTACTS(${EXTEN})})

Это гарантирует прозвон всех активных контактов эндпоинта, вместо попыток дозвона только к последнему зарегистрированному.

Запись разговоров и постобработка

Базовый запуск записи

На практике удобнее писать через MixMonitor() (смешивание каналов) и использовать экстеншен h для постобработки:

Подготовка имени

exten => _X.,1,Set(DATE=${STRFTIME(${EPOCH},,%Y/%m/%d)})
 same => n,Set(REC_BASE=${DATE}/${CALLERID(num)}_${EXTEN}_${UNIQUEID})

Запуск записи (микшированный WAV)

same => n,MixMonitor(${REC_BASE}.wav,b)  ; 'b' — писать только в бридже
 same => n,Dial(${PJSIP_DIAL_CONTACTS(${EXTEN})})

Постобработка по завершению

exten => h,1,NoOp(Post-process ${REC_BASE})
 ; пример: конвертация в mp3
 same => n,System(/usr/bin/ffmpeg -y -i /var/spool/asterisk/monitor/${REC_BASE}.wav \
                  -codec:a libmp3lame -qscale:a 4 \
                  /var/spool/asterisk/monitor/${REC_BASE}.mp3)

Раздельные дорожки и сборка в стерео

При необходимости получить раздельные направления «вход»/«выход» и затем собрать их в стерео:

Дополнительно попросить MixMonitor сохранить «rx/tx» дорожки: в современных версиях используются опции вида r(filename) и t(filename) в параметре options.

Пример (схематично):

same => n,MixMonitor(,r(${REC_BASE}-in.wav)t(${REC_BASE}-out.wav))

Аудиофайлы будут закрыты на момент остановки MixMonitor.

Сборка в стерео командой post-process на MixMonitor (3-й параметр):

same => n,MixMonitor(${REC_BASE}.wav,b,/usr/bin/sox -M \
     /var/spool/asterisk/monitor/${REC_BASE}-in.wav \
     /var/spool/asterisk/monitor/${REC_BASE}-out.wav \
     /var/spool/asterisk/monitor/${REC_BASE}-stereo.wav)

Опция «команда» у MixMonitor выполняется после остановки записи, поэтому безопаснее, чем запуск внешних утилит через System() ещё до закрытия файлов.

Частые утилиты и справка по месту

CLI-справочник:

  • core show applications — список приложений.
  • core show application <name> — подробности по приложению.
  • core show functions — список функций.
  • core show function <name> — подробности по функции.

Для русских голосовых наборов звуков выбирать русские промты при сборке/установке (sounds с ru).

Пример простого IVR с возвратом к началу и прямым набором

[ivr-main]
exten => s,1,NoOp(IVR start)
 same => n(start),Background(custom/ivr-welcome) ; приветствие
 same => n,WaitExten(5,m)

Разделы

exten => 1,1,Goto(ivr-sales,s,1)
exten => 2,1,Goto(ivr-support,s,1)

Прямой набор внутренних

exten => _1XXX,1,Goto(from-internal,${EXTEN},1)e

обработка ошибок

exten => i,1,Playback(pbx-invalid)
 same => n,Goto(s,start)

exten => t,1,Playback(vm-goodbye)
 same => n,Goto(s,start)

Примечания по структуре контекстов

  • Входящие от провайдера: обычно from-trunk.
  • Внутренние: from-internal.
  • Рекомендуется держать «тонкую» логику в подпрограммах (Gosub sub-*) — проверка прав на исходящие, запуск записи, антисдвой и т.п. Это повышает читаемость и повторное использование.

Распространённые мелочи и подводные камни

  • В WaitExten(seconds,options) опция m включает музыку ожидания, это не минуты.
  • Для каталогов с датами в путях (%Y/%m/%d) следует заранее обеспечить существование директорий.
  • При множественных регистрациях PJSIP предпочтительно использовать PJSIP_DIAL_CONTACTS() вместо «голого» PJSIP/endpoint.
  • Макросы Macro() считаются устаревшей практикой; предпочтителен Gosub()/Return().
  • После Dial() без g не стоит ожидать продолжения приоритетов в случае успешного ответа: для постдействий использовать exten => h.

Заключение

Описанные приёмы покрывают основные паттерны набора, механику специальных экстеншенов, ветвление и повторное использование логики, а также практику записи/постобработки разговоров. Приведённые фрагменты диалплана можно использовать как каркас для построения внутренних и входящих маршрутов, IVR и политики записи с учётом особенностей PJSIP.
Ежегодная конференция по Asterisk 2025!

Билеты уже в продаже!

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

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

Наши
клиенты

Посмотреть все