extensions.conf2

Продолжаем ковыряться в звездочке, обучим наш диалплан считать, писать, условным переходам и всяким другим хитрым штукам
В планах вот это:
Выражения
Функции
Операторы
Операторы перехода
Макросы
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/[email protected])
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/[email protected]&Local/[email protected],30)
exten => 201,n,Voicemail([email protected],${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 заставляет локальный канал вернуть реальный канал за ним , по требованию:) Используется при трансфере реального, а не локального канала.

Комментарии: