Продолжаем ковыряться в звездочке, обучим наш диалплан считать, писать, условным переходам и всяким другим хитрым штукам
В планах вот это:
Выражения
Функции
Операторы
Операторы перехода
Макросы
GoSub()
Local_chan
В Астериске выражение всегда принимает вид:
$[expression]
Получить значение переменной, можно практически аналогично:
${COUNT}
Теперь объединим эти два действия:
Сделаем COUNT +1, в диалплане это будет смотреться вот так:
$[${COUNT} + 1]
В результате сложение выглядело так
Set(COUNT=$[${COUNT} + 1])
конструкция типа $[${COUNT} + 1] всегда доставляла, хочешь изменить значение, будь бобр используй конструкцию [ ]
теперь все стало проще(Передавать ИМЯ, а не ЗНАЧЕНИЕ!)
INC(COUNT) – возвращает значение COUNT+1
DEC(COUNT) – возвращает значение COUNT-1
эти Функции НЕ ИЗМЕНЯЮТ значение переменной, для того чтобы поменять значение
Set(CounterVariable=${INC(CounterVariable)})
Функции имеют вид
Func_name(arg) – в скобках можем передавать аргументы функции
чтобы получить результат работы функции к ней нужно применить те же операции что и для переменной
${Func_nam(arg)} Пример: exten => 123,1,Set(TEST=example) same => n,SayNumber(${LEN(${TEST})})
В качестве еще одного примера рассмотрим функцию
RAND(0,1) выдает целое случайное число в заданном диапазоне
для записи значения как всегда используем Set
Set(IncrementValue=${RAND(0,1)})
Как и присвоение значения переменной, присвоение значения функции выполняется без использования символов ${ }.
exten => s,1,Set(TIMEOUT(digit)=30)
Операторы:
expr1 | expr2
Логическое ИЛИ, в случае истинности выражения expr1 (не пустая строка и не нуль) возвращает результат его вычисления. В противном случае он возвращает результат вычисления выражения expr2.
expr1 & expr2
Логическое И, возвращает значение expr1 если оба выражения истинны (то есть если ни одно из выражений не дает в результате пустой строки или нуля). В противном случае возвращается нуль.
expr1 {=, >, >=, <, <=, !=} expr2
Эти операторы возвращают результаты сравнения целых чисел, если оба аргумента являются целыми числами; в противном случае возвращаются результаты сравнения строк. В результате сравнения получаем 1, если заданное отношение выполняется, или 0, если отношение не выполняется. (Сравнение строк выполняется соответственно текущим локальным настройкам операционной системы.)
expr1 {+, -, *, /, %} expr2
поддерживаются и такие математические выражения (прибавить, отнять, умножения, целочисленного деления или остаток от деления целочисленных аргументов соответственно.)
expr1 : expr2
Этот оператор сравнивает выражение expr1 с expr2, где последнее должно быть регулярным выражением. Регулярное выражение привязывается к началу строки посредством явного задания ^.
Если соответствие установлено и шаблон содержит по крайней мере одну подстроку регулярного выражения, \( ... \), возвращается строка, соответствующая \1; в противном случае оператор сопоставления возвращает число совпавших символов. Если соответствия не выявлено и шаблон не содержит подстроку регулярного выражения, возвращается нулевая строка; в противном случае, возвращается 0.
Условные операторы:
${IF($[…conditional statement…]?true_return_value:false_return_value)}
Пример
Set(CounterVariable=${IF($[${IncrementValue} = 1]?${INC(CounterVariable)}:${CounterVariable})
Условие While, EndWhile()
Пример
[IteratingLoop] exten => start,1,Verbose(2,Looping through an action five times.) same => n,Set(X=1) same => n,Verbose(2,Starting the loop) same => n,While($[${X} <= 5]) same => n,Verbose(2,Current value of X is: ${X}) same => n,Set(X=${INC(X)}) same => n,EndWhile() same => n,Verbose(2,End of the loop) same => n,Hangup()
While + CUT
[ LoopWithCut] exten => start,1,Verbose(2,Example of a loop using the CUT function.) same => n,Set(Fruits=Apple-Orange-Banana-Pineapple-Grapes) same => n,Set(FruitWeWant=Pineapple) same => n,Set(X=1) same => n,Set(thisFruit=${CUT(Fruits,-,${X})}) same => n,While($[${EXISTS(${thisFruit})}]) same => n,GotoIf($[${thisFruit} = ${FruitWeWant}]?GotIt,1) same => n,Set(X=${INC(X)}) same => n,Set(thisFruit=${CUT(Fruits,-,${X})}) same => n,EndWhile() We got to the end of the loop without finding what we were looking for. same => n,Verbose(2,Exiting the loop without finding our fruit.) same => n,Hangup() ; If we found the fruit, then the GotoIf() will get us here. exten => GotIt,1,Verbose(2,We matched the fruit we were looking for.) same => n,Hangup()
CUT выдает часть строки, соотв определенному критерию, например слова записанные через – и нам нужно выбрать 3 слово в CUT передается (переменная-которую-проверим,разделитель,номерслова)
в нашем примере из Fruits CUT выдаст Banana
Условие GotoIf()
Синтаксис:
GotoIf(expression?destination1:destination2)
Вычисляет выражение и отправляет абонента в соответствующее место назначения в зависимости от истинности или ложности выражения.
В качестве места назначения может быть задано следующее:
• Метка приоритета в рамках того же добавочного номера, например weasels.
• Добавочный номер и метка приоритета в рамках того же контекста, например 123,weasels.
• Контекст, добавочный номер и метка приоритета, например incoming,123,weasels.
Лучше всего использовать не метки а другие добавочные номера, это упростит чтение нашего диалплана.
exten => 345,1,Set(TEST=1) same => n,GotoIf($[${TEST} = 1]?weasels:iguanas) same => n(weasels),Playback(weasels-eaten-phonesys) same => n,Hangup() same => n(iguanas),Playback(office-iguanas) same => n,Hangup()
Также можно использовать конструкцию только с ложным условием GotoIf(expression?:destination2) НО это усложняет понимание!
А теперь немного посчитаем:
[IteratingLoop] exten => start,1,Verbose(2,Looping through an action five times.) same => n,Set(X=1) same => n,Verbose(2,Starting the loop) same => n(top),NoOp() same => n,Verbose(2,Current value of X is: ${X}) same => n,Set(X=${INC(X)}) same => n,GotoIf($[${X} <= 5]?top) same => n,Verbose(2,End of the loop) same => n,Hangup()
GotoIfTime переход по времени
Синтаксис:
GotoIfTime(time range,days of week,days of month,months?label)
Пример:
same => n,GotoIfTime(0900-1700,mon-fri,*,*?label)
параметры могут быть:
названия дней и месяцев не чувствительны к регистру
можно использовать *, что соответствует выбору всех.
можно использовать -, что соответствует выбору диапазону значений
можно использовать &, что соответствует И(выбору нескольких значений).
В качестве метки(label) может быть задано следующее:
• Метка приоритета в рамках того же добавочного номера, например time_has_passed.
• Добавочный номер и метка приоритета в рамках того же контекста, например 123,time_has_passed.
• Контекст, добавочный номер и метка приоритета, например incoming,123,time_has_passed.
Следует учесть, что степень детализации GotoIfTime составляет 2 минуты, поэтому задав время 18:00 не удивляйтесь если действовать правило будет до 18:01:59
exten => s,1,GotoIfTime(*,*,28,jun?closed,s,1)
; В рабочее время перенаправляем звонки в контекст open
same => n,GotoIfTime(09:00-17:59,mon-fri,*,*?open,s,1)
same => n,GotoIfTime(09:00-11:59,sat,*,*?open,s,1)
; Во всех остальных случаях закрыто
same => n,Goto(closed,s,1)
Макросы
Если проводить аналогию, то это подпрограмма, которую можно использовать много раз, а не копипастить все это дело по диалплану. У макросов есть небольшие проблемы со стабильностью, нужно проверить наш макрос если все гуд, то используйте, если появились сложности, то используйте GoSub() и Return()
Для описания макроса используется служебное слово macro-, за которым следует имя макроса, и вся эта конструкция заключается в квадратные скобки:
[macro-voicemail]
В описании макроса можно использовать только добавочный номер s.
Вызов макроса
exten => 101,1,Macro(voicemail)
Приложение Macro() определяет также несколько специальных переменных. К ним относятся:
${MACRO_CONTEXT} Исходный контекст, в котором был вызван макрос.
${MACRO_EXTEN} Исходный добавочный номер, в котором был вызван макрос.
${MACRO_PRIORITY} Исходный приоритет, в котором был вызван макрос.
${ARG n} n-ный аргумент, передаваемый в макрос. Например, первым был бы аргумент ${ARG1}, вторым – ${ARG2} и т. д.
[macro-voicemail] exten => s,1,Dial(${ARG1},10) exten => s,n,GotoIf($["${DIALSTATUS}" = "BUSY"]?busy:unavail) exten => s,n(unavail),Voicemail(${MAСRO_EXTEN}@default,u) exten => s,n,Hangup() exten => s,n(busy),VoiceMail(${MCARO_EXTEN}@default,b) exten => s,n,Hangup() Применяем для доступа в голосовой почте exten => 101,1,Macro(voicemail,${JOHN}) exten => 102,1,Macro(voicemail,${JANE}) exten => 103,1,Macro(voicemail,${JACK})
Аналогичным по назначению, но работающим по другому принципу GoSub(). Похоже на Goto(), но с памятью откуда был вызван и возможностью вернуть значение.
Не требуется специальное слово в описании саброутов, как это было с макросами, но для простоты понимания желательно добавлять sub перед названием саброута, например вот так:
[subVoicemail]
Поскольку GoSub() работает по другому чем макрос, поэтому передавать аргументы мы должны другим способом
Вызов из диалплана:
exten => 101,1,GoSub(subVoicemail,start,1())
Пустые скобки для передачи аргументов:
[LocalSets] exten => 101,1,GoSub(subVoicemail,start,1(${JOHN},${EXTEN})) exten => 102,1,GoSub(subVoicemail,start,1(${JANE},${EXTEN})) exten => 103,1,GoSub(subVoicemail,start,1(${JACK},${EXTEN}))
а сам саброут принимает вот такой вид
[subVoicemail] exten => start,1,Dial(${ARG1},10) same => n,VoiceMail(${ARG2}@default,${IF($[${DIALSTATUS} = BUSY]?b:u)}) same => n,Hangup()
Но, как я заметил ранее, GoSub() сам не возвращается в место диалплана откуда был вызван, поэтому ему нужно помочь с помощью функции Return()
[subDialer] exten => start,1,Dial(${ARG1},${ARG2}) same => n,Return()
GoSub() может вернуть в диалплан какое нибудь значение, для этого используется глобальная переменная ${GOSUB_RETVAL}
Пример:
[subDialer] exten => start,1,Dial(${ARG1},${ARG2}) same => n,Return(${DIALSTATUS}) [subVoicemail] exten => start,1,VoiceMail(${ARG1}@${ARG2},${ARG3}) same => n,Hangup() А теперь используем это в диалплане exten => 101,1,GoSub(subDialer,start,1(${JOHN},30)) same => n,Set(VoicemailMessage=${IF($[${GOSUB_RETVAL} = BUSY]?b:u)}) same => n,GoSub(subVoicemail,start,1(${EXTEN},default,${VoicemailMessage}))
После вызова Gosub() он нам вернет статус звонка, абонент занят или недоступен, в следующей строке переменной VoicemailMessage задаем значение равное этому статусу, и в последней строке мы переходим в саброут голосовой почты, где на основании этого статуса нам скажут абонент занят или недоступен
Local channel – в Асетриске это метод с помощью которого мы можем рассматривать добавочный номер в диалплане как внешнее устройство.
Обычно используется при построении очередей, при работе с call файлами и при одновременном звонке на разные направления с разной служебной информацией. Рассмотрим Local на примерерах.
[devices] exten => 201,1,Verbose(2,Dial another part of the dialplan via the Local chan) exten => 201,n,Verbose(2,Outside channel: ${CHANNEL}) exten => 201,n,Dial(Local/201@extensions) exten => 201,n,Hangup() [extensions] exten => 201,1,Verbose(2,Made it to the Local channel) exten => 201,n,Verbose(2,Inside channel: ${CHANNEL}) exten => 201,n,Dial(SIP/some-named-extension,30) exten => 201,n,Hangup()
ЧТо полезного здесь происходит? ровным счетом ничего, просто тут показано как из простого диала можно развернуть огого штуку)
Пример2: При звонке на номер 201, мы позвоним на стационарный телефон и на мобильный(с задержкой 6 секунд), и если не получим ответ отправим на голосовую почту
[devices] exten => 201,1,Verbose(2,Call desk phone and cellphone but with delay) exten => 201,n,Dial(Local/deskphone-201@extensions&Local/cellphone-201@extensions,30) exten => 201,n,Voicemail(201@default,${IF($[${DIALSTATUS} = BUSY]?b:u)}) exten => 201,n,Hangup() [extensions] ; Dial the desk phone exten => deskphone-201,1,Verbose(2,Dialing desk phone of extension 201) exten => deskphone-201,n,Dial(SIP/0004f2040001) ; SIP device with MAC address ; of 0004f2040001 ; Dial the cellphone exten => cellphone-201,1,Verbose(2,Dialing cellphone of extension 201) exten => cellphone-201,n,Verbose(2,-- Waiting 6 seconds before dialing) exten => cellphone-201,n,Wait(6) exten => cellphone-201,n,Dial(DAHDI/g0/14165551212)
При использовании Local есть важная особенность, по умолчанию он оптимизирует себя, при установки связи между астериском и назначением он уходит в зомби, все параметры Dial и переменные ассоциированные с локальным каналом уничтожаются, чтобы этого не происходило используем /n – мы оставляем наш Local канал на плаву. Необходимо например для ограничения продолжительности звонка с помощью параметра L, Dial’a. Без /n наши разговоры будут бесконечно долгими:)
Также есть опция /j которая используется в паре с /n, которая позволяет использовать джитбуффер при входящих звонках и их обращении к приложениям астериска. Например при звонке с SIP в голосовую почту.
/m Позволяет прокидывать запросы MoH через локальный канал, вместо обычного действия(старт и стоп при создании/удалении канала)
/b заставляет локальный канал вернуть реальный канал за ним , по требованию:) Используется при трансфере реального, а не локального канала.