Перенос соответствия внутренних и внешних номеров в БД MySQL для Asterisk
В материале приведён пример переноса соответствий внутренних и внешних номеров из файла extensions.conf в БД MySQL для максимального упрощения работы по перенастройке таких соответствий.
Имеется Asterisk, с более чем 200 внутренними номерами и несколькими десятками внешних номеров. Соответствие внешних и внутренних номеров(экстеншенов) в нём прописывались «дедовским» методом приведённым ниже. В контексте caller-id все правила имеют приоритет 1. Контексты для исходящей связи, например, mn(международный) организуются по видам связи, могут включать контексты: внутренняя (internal-out), местная (local-out), зоновая (mobile-out), междугородняя (mg-out) и международная (mn-out),входящая связь (RTU-IN), в этих контекстах дайлпланы начинаются с приоритета 2. Внутренним SIP-абонентам, присваивается контекст для исходящей связи, например, mn. Для исходящей связи: «ловим» вызовы по маске А-номера, например, _26[23], и одинаковой маске Б-номеров _9X., затем подставляем соответствующие внешние А-номера и отправляем на внешний SIP-транк с именем RTU.
[caller-id] exten => _9X./_26[2-3],1,Set(CALLERID(num)=78122223344) ;Внешний АОН 78122223344 для внутреннего номера 262 и 263 exten => _9X./_264,1,Set(CALLERID(num)=78122223355) ;Внешний АОН 78122223355 для внутреннего номера 264 exten => _9X./_31X,1,Set(CALLERID(num)=78122223366) [mn] include => caller-id include => RTU-IN include => local-out include => internal-out include => mobile-out include => mg-out include => mn-out
Для входящей связи: в контексте RTU-IN «ловим» Б-номер на который пришёл вызов, затем, подставляем нужное количество внутренних номеров, через запятую, используя функцию массива ARRAY. Затем, переходим в универсальный контекст HG, где вызываем одновременно внутренние номера в группе, полученные из переменных m1-mN, затем переходим в контекст СHECK_BUSY_FORWARD, где проверяем переадресацию по занятости.
[RTU-IN] exten => 78122223377,1,Set(ARRAY(m1,m2,m3,m4)=257,258,259,260) ;Групповой вызов на 257 258 259 260 same => n,Goto(HG,${EXTEN},1) exten => 78122223377,1,Set(ARRAY(m1)=257) ;Групповой вызов на 257 same => n,Goto(HG,${EXTEN},1) exten => 500,1,Set(ARRAY(m1,m2,m3,m4,m5,m6,m7,m8,m9,m10)=200,201,202,203,204,205,206,207,208,209) ;Групповой вызов на 500 группу same => n,Goto(HG,${EXTEN},1) [HG] exten => _X.,1, Answer same => n,Set(_MONITOR_FILENAME=IN-${EXTEN}-${STRFTIME(${EPOCH},,%Y_%m_%d[%H_%M])}-${CALLERID(num)}-${EXTEN}-${UNIQUEID}) same => n, NoOp(!!!!CALLED=${CALLED}) same => n, Set(H1=${IF($["${m1}" != ""]?SIP/${m1})}) same => n, Set(H2=${IF($["${m2}" != ""]?&SIP/${m2})}) same => n, Set(H3=${IF($["${m3}" != ""]?&SIP/${m3})}) same => n, Set(H4=${IF($["${m4}" != ""]?&SIP/${m4})}) same => n, Set(H5=${IF($["${m5}" != ""]?&SIP/${m5})}) same => n, Set(H6=${IF($["${m6}" != ""]?&SIP/${m6})}) same => n, Set(H7=${IF($["${m7}" != ""]?&SIP/${m7})}) same => n, Set(H8=${IF($["${m8}" != ""]?&SIP/${m8})}) same => n, Set(H9=${IF($["${m9}" != ""]?&SIP/${m9})}) same => n, Set(H10=${IF($["${m10}" != ""]?&SIP/${m10})}) same => n,Dial(${H1}${H2}${H3}${H4}${H5}${H6}${H7}${H8}${H9}${H10},40,TtrM(monext)) same => n,Goto(CHECK_BUSY_FORWARD,${EXTEN},1) same => n,Hangup
Если таких записей, как для входящей так и для исходящей связи не много и они не часто меняются, то такой подход вполне оправдан, но становится очень неудобно, если внешних и внутренних номеров много и их привязка постоянно меняется, да ещё и нужно постоянно менять номера для переадресаций из групп. Самый простой вариант – перенести всё эти привязки в БД, тогда сразу же будет видно все соответствия номеров без изучения кучи записей в файле extensions.conf, к тому же, можно будет редактировать записи в БД из внешней системы для повышения автоматизации процесса привязки номеров. В Asterisk отдельные контексты дайлплана можно перенести в БД MySQL при помощи realtime, но для простого восприятия и редактирования это не очень удобно. Необходимо сделать максимально простые таблицы соответствий номеров в БД, которые понятны человеку не знающему дайлплан Asterisk.
Сначала перенесем в БД соответствие внутренних и внешних номеров для исходящей связи, затем для входящей связи.
Перенос соответствия внутренних и внешних номеров для исходящей связи в БД MySQL
Создаём таблицу outbound в БД:
CREATE TABLE `outbound` ( `id` int(11) NOT NULL AUTO_INCREMENT, `internal` int(11) NOT NULL, `callerid` varchar(11) NOT NULL, `Notes` text CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET= utf8;
Столбцы в таблице: id — номер правила, значение инкриминируется автоматически, internal — внутренний номер, callerid — внешний А номер который будет присваиваться при вызове в ТфОП, Notes — заметка о записи. Далее, заполняем соответствиями номеров в таблице outbound.
INSERT INTO `astcdrdb`.`outbound` ( `id` , `internal` , `callerid` , `Notes` ) VALUES (NULL , '200', '78122429945', ''), (NULL , '201', '78122429945', ''), (NULL , '202', '78122429945', ''), (NULL , '203', '78122429945', ''), (NULL , '204', '78122429945', ''), (NULL , '205', '78122429945', '');
Теперь таблица outbound будет выглядеть так:
mysql> select * from outbound; +-----+----------+-------------+-------+ | id | internal | callerid | Notes | +-----+----------+-------------+-------+ | 10 | 200 | 78122429945 | | | 11 | 201 | 78122429945 | | | 12 | 202 | 78122429900 | | | 13 | 203 | 78122429945 | | | 14 | 204 | 78122429945 | | | 15 | 205 | 78122429945 | | +-----+----------+-------------+-------+ 6 rows in set (0.00 sec)
К БД MySQL будем подключаться через ODBC коннектор Asterisk. Предполагается, ODBC с Asterisk уже работает.
Далее, создаём запрос в БД в /etc/asterisk/func_odbc.conf:
[GET_OUTBOUND_CID] dsn=asterisk readsql=SELECT callerid FROM outbound where internal='${ARG1}'
Подгрузим новую функцию ODBC, выполнив команду в bash:
asterisk -rx 'module reload func_odbc.so'
Теперь вместо десятков строк, подставим одну:
exten => _9X.,1,Set(CALLERID(num)=${ODBC_GET_OUTBOUND_CID(${CALLERID(num)})})
Если дополнительно нужно подставлять внешние А-номера через обычную запись в дайлплане, то для соответствующих А-номеров нужно задать точную маску, в нашем случае в строке с запросом CALLERID из БД маска А-номера может быть любая, в другой строке дайлплана маска А-номера проверяется:
exten => _9X./_6[5-9].,1,Set(CALLERID(num)=78122429999)
При равном приоритете двух строк в одном контексте дайлплана будет отрабатывать строка с более точной маской /_6[5-9]. для А-номеров. Это полезно, когда есть десятки или сотни внутренних номеров с одинаковым внешним номером, смысла расписывать каждый номер в БД нет, при условии что внешний номер не будет меняться.
Дополнительно, добавим А-номер по-умолчанию = 7812350000, если внешний А-номер по какой-то причине отсутствует в ответе на запрос ODBC_GET_OUTBOUND_CID или БД MySQL не отвечает на запросы.
exten => _9X.,1,Set(CALLERID(num)=${IF($["${ODBC_GET_OUTBOUND_CID(${CALLERID(num)})}" = "" ]?7812350000:${ODBC_GET_OUTBOUND_CID(${CALLERID(num)})})}) exten => _9X./_6[5-9].,1,Set(CALLERID(num)=78120001122) same => n,Dial(SIP/RTU/${EXTEN:1},,tTr)
Листинг вывода в консоль Asterisk:
-- Executing [981116275136@ caller-id-odbc:1] Set("SIP/248-00014f4f", "CALLERID(num)=78123500000") in new stack -- Executing [981116275136@mn:5] Dial("SIP/248-00014f4f", "SIP/RTU/79627277780,,tT") in new stack == Using SIP RTP CoS mark 5 -- Called SIP/RTU/79627277780 -- SIP/RTU-00014f50 is making progress passing it to SIP/248-00014f4f
Перенос соответствия внутренних и внешних номеров для входящей связи в БД MySQL
Процесс переноса входящей связи в БД сложнее чем исходящей. Входящий вызов приходит с внешнего транка или от внутреннего абонента и направляется в контекст для входящих вызовов RTU-IN, здесь для любого входящего Б-номера делается запрос в БД для определения внутреннего номера или списка внутренних номеров на которые следует направить вызов. Далее, рассмотрим два случая: Первый — самый простой – после таймаута вызова (устанавливается в дайлплане) на группу номеров вызов завершается. Второй случай – после таймаута вызова(устанавливается в БД для каждого внешнего номера) на группу номеров, в БД проверяется разрешена ли переадресация на внешний номер, если, да то на какой внешний номер и каков таймаут вызова на него.
Используем коннектор ODBC для БД MySQL.
Создаём таблицу inbound в БД, куда мы будем помещать соответствие Б-номеров и членов группы, группа может включить от 1 до 9 номеров, их количество можно настроить в дайлплане. Столбец id – порядковый номер записи с авто инкрементом, столбец did – Б-номера соответствующие группам, столбец numbers – номера членов в группе с номером did, столбец notes – опциональное описание строки.
mysql> CREATE TABLE `inbound` ( `id` int(11) NOT NULL AUTO_INCREMENT, `did` varchar(11) CHARACTER SET latin1 NOT NULL, `numbers` text NOT NULL, `Notes` text CHARACTER SET latin1 NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Добавляем тестовую запись – номер группы 630, члены группы 390,391,392,393.
mysql> INSERT INTO `inbound` (`did`, `numbers`, `Notes`) VALUES ('630', '390,391,392,393', '');
Пишем функцию в ODBC:
[GET_INBOUND_NUMERS] dsn=asterisk readsql=SELECT numbers FROM inbound where did='${ARG1}'
Теперь добавляем в дайлплан контекст для получения номеров группы по входящему номеру:
[RTU-IN] exten => _[56]XX,1,Set(ARRAY(m1,m2,m3,m4,m5,m6,m7,m8,m9)=${ODBC_GET_INBOUND_NUMERS(${EXTEN})}) same => n,Goto(HG,${EXTEN},1)
И контекст группы поиска, который использует полученные данным, для одновременного вызова на все номера группы:
[HG] exten => _X.,1, Set(H1=${IF($["${m1}" != ""]?SIP/${m1})}) same => n, Set(H2=${IF($["${m2}" != ""]?&SIP/${m2})}) same => n, Set(H3=${IF($["${m3}" != ""]?&SIP/${m3})}) same => n, Set(H4=${IF($["${m4}" != ""]?&SIP/${m4})}) same => n, Set(H5=${IF($["${m5}" != ""]?&SIP/${m5})}) same => n, Set(H6=${IF($["${m6}" != ""]?&SIP/${m6})}) same => n, Set(H7=${IF($["${m7}" != ""]?&SIP/${m7})}) same => n, Set(H8=${IF($["${m8}" != ""]?&SIP/${m8})}) same => n, Set(H9=${IF($["${m9}" != ""]?&SIP/${m9})}) same => n,Dial(${H1}${H2}${H3}${H4}${H5}${H6}${H7}${H8}${H9},40,Ttr) same => n,Hangup
Теперь проверим результат – сделаем вызов на 630.
Листинг вывода в консоли Asterisk:
== Using SIP RTP CoS mark 5 -- Executing [630@RTU-IN:1] Set("SIP/202-0000011d", "ARRAY(m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12,m13,m14,m15)=390\,391\,392\,393") in new stack -- Executing [630@RTU-IN:2] Goto("SIP/202-0000011d", "HG,630,1") in new stack -- Goto (HG,630,1) -- Executing [630@HG:1] Set("SIP/202-0000011d", "H1=SIP/390,391,392,393") in new stack -- Executing [630@HG:2] Set("SIP/202-0000011d", "H2=") in new stack -- Executing [630@HG:3] Set("SIP/202-0000011d", "H3=") in new stack -- Executing [630@HG:4] Set("SIP/202-0000011d", "H4=") in new stack -- Executing [630@HG:5] Set("SIP/202-0000011d", "H5=") in new stack -- Executing [630@HG:6] Set("SIP/202-0000011d", "H6=") in new stack -- Executing [630@HG:7] Set("SIP/202-0000011d", "H7=") in new stack -- Executing [630@HG:8] Set("SIP/202-0000011d", "H8=") in new stack -- Executing [630@HG:9] Set("SIP/202-0000011d", "H9=") in new stack -- Executing [630@HG:10] Dial("SIP/202-0000011d", "SIP/390,391,392,393,40,TtrM(monext)") in new stack [Jan 12 11:10:23] WARNING[801][C-000000c4]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) == Everyone is busy/congested at this time (1:0/0/1)
При вызове из дайлплана этой строки, драйвер ODBC Asterisk передаёт в переменную с обратными слешами (backslash) — символ (\) для экранирования запятых, вот пример:
-- Executing [630@RTU-IN:1] NoOp("SIP/202-00000117", "Numbers for that HG 630 is 390\,391\,392\,393") in new stack
Поэтому использовать функцию ARRAY для того чтобы присвоить переменным значения из массива не получится.
Каким способом можно убрать символ (\), я так и не нашёл, поэтому самое простое решение, это поменять формат записей в БД:
UPDATE `inbound` SET `numbers` = ' SIP/220&SIP/202&SIP/390&SIP/391&SIP/392&SIP/393' WHERE ` did ` =630;
Модифицируем контекст [RTU-IN], Контекст [HG] уже не нужен.:
[RTU-IN] exten => _6XX.,1,Dial(${ODBC_GET_INBOUND_NUMERS(${EXTEN})},40,TtrM(monext)) same => n,Hangup
Листинг вывода в консоли Asterisk:
-- Executing [630@RTU-IN:1] Dial("SIP/202-00000122", "SIP/220&SIP/202&SIP/390&SIP/391&SIP/392&SIP/393,40,TtrM(monext)") in new stack == Using SIP RTP CoS mark 5 == Using SIP RTP CoS mark 5 [Jan 12 11:25:14] WARNING[829][C-000000c7]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 12 11:25:14] WARNING[829][C-000000c7]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 12 11:25:14] WARNING[829][C-000000c7]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 12 11:25:14] WARNING[829][C-000000c7]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) -- Called SIP/220 -- Called SIP/202 -- SIP/220-00000123 is ringing -- SIP/202-00000124 is ringing -- SIP/202-00000124 is ringing
Но запись в формате SIP/220&SIP/202&SIP/390&SIP/391&SIP/392&SIP/393 не очень понятная, вариант записи 220,202,391,392,393 более читабелен.
Поэтому возвращаем наш пример в первоначальный формат:
mysql>UPDATE `inbound` SET `numbers` = '220,202,391,392,393' WHERE ` did ` =630;
Пример запроса в БД из консоли MySQL:
mysql> select numbers from inbound where did=630; +-------------------------+ | numbers | +-------------------------+ | 220,202,391,392,393 | +-------------------------+ 1 row in set (0.00 sec)
Для решения проблемы с обратными слешами, я решил использовать вместо ODBC запроса AGI скрипт, написанный на PHP, который также делает запрос в БД по номеру группы и возвращает в дайлплан Asterisk список с номерами телефонов в группе.
Создадим в директории /var/lib/asterisk/agi-bin php скрипт с названием get_hg.php. Предполагается, что библиотека phpagi уже установлена, если её нет, то она доступна для свободного скачивания в интернет – архив, например, phpagi-2.20.tgz нужно разархивировать и задать на него ссылку в скрипте, в примере это /usr/share/php5/phpagi-2.20/phpagi.php.
Листинг скрипта get_hg.php:
#!/usr/bin/php -q <?php set_time_limit(0); include ( "/usr/share/php5/phpagi-2.20/phpagi.php" ); $agi = new AGI; $conn = mysql_connect("localhost", "asterisk_user", "_iddqd1"); if (!$conn) { echo "Unable to connect to DB: " . mysql_error(); exit; } if (!mysql_select_db("astcdrdb")) { echo "Unable to select mydbname: " . mysql_error(); exit; } $sql = "SELECT numbers FROM inbound WHERE did = ".$argv[1]; $result = mysql_query($sql); //MYSQL_ASSOC<->Результат возвращается в ассоциативном массиве с индексами под именами колонок $line = mysql_fetch_array($result, MYSQL_ASSOC); foreach ($line as $col_value); $agi->set_variable ("HG_NUMS", $col_value); mysql_free_result($result); mysql_close($conn); ?>
Скрипт, через приложение AGI, вызванное из дайлплана Asterisk, получит входящий Б номер из переменной ${EXTEN} и вернет список номеров в переменную HG_NUMS назад в дайлплан Asterisk.
Работу скрипта можно проверить запуском его с параметром – входящим номером группы 630:
root@aster-test:/var/lib/asterisk/agi-bin# ./get_hg.php 630 SET VARIABLE HG "220,202,391,392,393"
Теперь модифицируем дайлплан Asterisk:
[RTU-IN] exten => _6XX,1, AGI(get_hg.php,${EXTEN}) same => n,NoOp(Reult ${HG_NUMS}) same => n,Set(ARRAY(m1,m2,m3,m4,m5,m6,m7,m8,m9)=${HG_NUMS}) same => n,Goto(HG,${EXTEN},1) [HG] exten => _X.,1, Set(H1=${IF($["${m1}" != ""]?SIP/${m1})}) same => n, Set(H2=${IF($["${m2}" != ""]?&SIP/${m2})}) same => n, Set(H3=${IF($["${m3}" != ""]?&SIP/${m3})}) same => n, Set(H4=${IF($["${m4}" != ""]?&SIP/${m4})}) same => n, Set(H5=${IF($["${m5}" != ""]?&SIP/${m5})}) same => n, Set(H6=${IF($["${m6}" != ""]?&SIP/${m6})}) same => n, Set(H7=${IF($["${m7}" != ""]?&SIP/${m7})}) same => n, Set(H8=${IF($["${m8}" != ""]?&SIP/${m8})}) same => n, Set(H9=${IF($["${m9}" != ""]?&SIP/${m9})}) same => n,Dial(${H1}${H2}${H3}${H4}${H5}${H6}${H7}${H8}${H9},40,Ttr) same => n,Hangup
Здесь максимальное ограничение на 9 членов в группе, но их количество легко увеличить или уменьшить до нужного количества. Вообще, если в группе много участников, то лучше использовать очереди – queue и выставлять в очередях алгоритмы распределения вызовов.
Теперь проверим результат – сделаем вызов на 630.
Листинг вывода в консоли Asterisk:
-- Executing [630@RTU-IN:1] AGI("SIP/202-0000012a", "get_hg.php,630") in new stack -- Launched AGI Script /var/lib/asterisk/agi-bin/get_hg.php -- <SIP/202-0000012a>AGI Script get_hg.php completed, returning 0 -- Executing [630@RTU-IN:2] NoOp("SIP/202-0000012a", "Reult 220,202,393,391,392,393") in new stack -- Executing [630@RTU-IN:3] Set("SIP/202-0000012a", "ARRAY(m1,m2,m3,m4,m5,m6,m7,m8,m9)=220,202,393,391,392,393") in new stack -- Executing [630@RTU-IN:4] Goto("SIP/202-0000012a", "HG,630,1") in new stack -- Goto (HG,630,1) -- Executing [630@HG:1] Set("SIP/202-0000012a", "H1=SIP/220") in new stack -- Executing [630@HG:2] Set("SIP/202-0000012a", "H2=&SIP/202") in new stack -- Executing [630@HG:3] Set("SIP/202-0000012a", "H3=&SIP/393") in new stack -- Executing [630@HG:4] Set("SIP/202-0000012a", "H4=&SIP/391") in new stack -- Executing [630@HG:5] Set("SIP/202-0000012a", "H5=&SIP/392") in new stack -- Executing [630@HG:6] Set("SIP/202-0000012a", "H6=&SIP/393") in new stack -- Executing [630@HG:7] Set("SIP/202-0000012a", "H7=") in new stack -- Executing [630@HG:8] Set("SIP/202-0000012a", "H8=") in new stack -- Executing [630@HG:9] Set("SIP/202-0000012a", "H9=") in new stack -- Executing [630@HG:10] Set("SIP/202-0000012a", "H11=") in new stack -- Executing [630@HG:11] Set("SIP/202-0000012a", "H12=") in new stack -- Executing [630@HG:12] Set("SIP/202-0000012a", "H13=") in new stack -- Executing [630@HG:13] Set("SIP/202-0000012a", "H14=") in new stack -- Executing [630@HG:14] Set("SIP/202-0000012a", "H15=") in new stack -- Executing [630@HG:15] Dial("SIP/202-0000012a", "SIP/220&SIP/202&SIP/391&SIP/392&SIP/393,40,Ttr") in new stack == Using SIP RTP CoS mark 5 == Using SIP RTP CoS mark 5 [Jan 12 12:49:33] WARNING[971][C-000000cb]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 12 12:49:33] WARNING[971][C-000000cb]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 12 12:49:33] WARNING[971][C-000000cb]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) -- Called SIP/220 -- Called SIP/202 -- SIP/220-0000012b is ringing -- SIP/202-0000012c is ringing -- SIP/202-0000012c is ringing
Второй вариант: Реализация входящей связи, если необходимо добавить таймаут вызова в группе и переадресацию на внешний номер, в случае, если в группе никто не ответил.
Также используем скрипт AGI и ODBC функции для БД MySQL.
Создаём таблицу inbound:
CREATE TABLE `inbound` ( `id` int(11) NOT NULL AUTO_INCREMENT, `did` varchar(11) NOT NULL, `numbers` text NOT NULL, `ring_timeout` int(11) NOT NULL DEFAULT '40', `forward_after_enable` int(11) NOT NULL DEFAULT '0', `forward_after_numer` text NOT NULL, `timeout_ring_after` int(11) NOT NULL DEFAULT '30', `Notes` text NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Как и в предыдущем случае, столбец id – порядковый номер записи с авто инкрементом, столбец did – Б-номера соответствующие группам, столбец numbers – номера членов в группе с номером did, столбец ring_timeout – длительность ожидания ответа в группе, forward_after_enable – разрешить переадресацию на внешний номер если в группе никто не ответил, по умолчанию 0 – переадресация выключена, если 1 – передаресация разрешена, forward_after_numer – номер для переадресации, если в группе никто не ответил timeout_ring_after – время таймаута вызова на номер forward_after_numer, столбец notes – опциональное описание строки.
Теперь последнее, можно добавим возможность переадресации на внешний номер.
Добавим в таблицу inbound новую запись – вызов на группу с городским номером 78120001122:
INSERT INTO `inbound` (`did`, `numbers`, `ring_timeout`, `forward_after_enable`, `forward_after_numer`, `timeout_ring_after`, `Notes`) VALUES ('78120001122', '240,250', 30, 1, '79627277780', 30, 'Тестовая запись');
И группу с внутренним номером 631:
INSERT INTO `inbound` (`did`, `numbers`, `ring_timeout`, `forward_after_enable`, `forward_after_numer`, `timeout_ring_after`, `Notes`) VALUES ('631', '240,250', 30, 1, '79627277780', 30, 'Тестовая запись');
Напишем дополнительную функцию ODBC для получения параметров переадресации:
[GET_FORWARD_AFTER_HG] dsn=asterisk readsql=SELECT ring_timeout,forward_after_enable,forward_after_numer,timeout_ring_after from inbound where did='${ARG1}'
Теперь модифицируем дайлпан. Если в случае срабатывания переадресациии на внешний номер в ТфОП(например, 79627277780) номер группы является внутренним(например, 631), его нужно закрывать общим внешним А-номером, в отличие от случая, где номером группы является городской номер(например, 78120001122) и в качестве АОНа для переадресации он же и берется из переменной EXTEN. Ниже пример вызова с листингом вывода в консоль Asterisk.
[RTU-IN] exten => _X.,1, AGI(get_hg.php,${EXTEN}) same => n,NoOp(Reult ${HG_NUMS}) same => n,NoOp(GET FORWARD ring_timeout,forward_after_enable,forward_after_numer,ring_after_timeout == ${ODBC_GET_FORWARD_AFTER_HG(${EXTEN})}) same => n,Set(ARRAY(RING_TIMEOUT,FORWARD_AFTER_ENABLE,FORWARD_AFTER_NUMBER,TIMEOUT_RING_AFTER)=${ODBC_GET_FORWARD_HG(${EXTEN})}) same => n,Set(ARRAY(m1,m2,m3,m4,m5)=${HG_NUMS}) same => n,Goto(HG,${EXTEN},1) [HG] exten => _X.,1, Set(H1=${IF($["${m1}" != ""]?SIP/${m1})}) same => n, Set(H2=${IF($["${m2}" != ""]?&SIP/${m2})}) same => n, Set(H3=${IF($["${m3}" != ""]?&SIP/${m3})}) same => n, Set(H4=${IF($["${m4}" != ""]?&SIP/${m4})}) same => n, Set(H5=${IF($["${m5}" != ""]?&SIP/${m5})}) same => n,Dial(${H1}${H2}${H3}${H4}${H5},${RING_TIMEOUT},TtrM(monext)) same => n,GotoIf($["${FORWARD_AFTER_ENABLE}" != "1"]?end) same => n,Set(CALLERID(num)=${IF($["${REGEX("^7812.......$" ${EXTEN})}"]?${EXTEN}:78122429945)}) same => n,Dial(SIP/RTU/${FORWARD_AFTER_NUMBER},${TIMEOUT_RING_AFTER},TtrM(monext)) same => n(end),Hangup
Вызов на 631. Сначала отработал скрипт, get_hg.php, который вернул список номеров 240 и 250, оказалось, далее, на номера был выполнен групповой вызов, абоненты не зарегиристрированы. Далее сработала переадресация на номер 79627277780 с таймаутом 30 секунд, в качестве А-номера бы присвоен номер по умолчанию равный 78127778899. Ниже пример вызова с листингом вывода в консоль Asterisk.
== Using SIP RTP CoS mark 5 -- Executing [631@RTU-IN:1] AGI("SIP/202-000001bd", "get_hg.php,631") in new stack -- Launched AGI Script /var/lib/asterisk/agi-bin/get_hg.php -- <SIP/202-000001bd>AGI Script get_hg.php completed, returning 0 -- Executing [631@RTU-IN:2] NoOp("SIP/202-000001bd", "Reult 240,250") in new stack -- Executing [631@RTU-IN:3] NoOp("SIP/202-000001bd", "GET FORWARD ring_timeout,forward_after_enable,forward_after_numer,timeout_ring_after == 30,1,SIP/RTU/79627277780,30") in new stack -- Executing [631@RTU-IN:4] Set("SIP/202-000001bd", "ARRAY(RING_TIMEOUT,FORWARD_AFTER_ENABLE,FORWARD_AFTER_NUMBER,TIMEOUT_RING_AFTER)=30,1,SIP/RTU/79627277780,30") in new stack -- Executing [631@RTU-IN:5] Set("SIP/202-000001bd", "ARRAY(m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12,m13,m14,m15)=240,250") in new stack -- Executing [631@RTU-IN:6] Goto("SIP/202-000001bd", "HG,631,1") in new stack -- Goto (HG,631,1) -- Executing [631@HG:1] Answer("SIP/202-000001bd", "") in new stack > 0x7fde046867c0 -- Probation passed - setting RTP source address to 192.168.85.148:40032 -- Executing [631@HG:2] Set("SIP/202-000001bd", "H1=SIP/240") in new stack -- Executing [631@HG:3] Set("SIP/202-000001bd", "H2=&SIP/250") in new stack -- Executing [631@HG:4] Dial("SIP/202-000001bd", "SIP/240&SIP/250,30,TtrM(monext)") in new stack [Jan 18 15:43:42] WARNING[11227][C-00000110]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 18 15:43:42] WARNING[11227][C-00000110]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) == Everyone is busy/congested at this time (2:0/0/2) -- Executing [631@HG:5] GotoIf("SIP/202-000001bd", "0?end") in new stack -- Executing [631@HG:6] Set("SIP/202-000001bd", "CALLERID(num)=78127778899") in new stack -- Executing [631@HG:7] Dial("SIP/202-000001bd", "SIP/RTU/79627277780,30,TtrM(monext)") in new stack == Using SIP RTP CoS mark 5 -- Called SIP/RTU/79627277780
Вызов на 7812000112. Сначала отработал скрипт, get_hg.php, который вернул список номеров 240 и 250, оказалось, далее, на номера был выполнен групповой вызов, абоненты не зарегиристрированы. Далее сработала переадресация на номер 79627277780 с таймаутом 30 секунд, в качестве А-номера (переменная CALLERID(num)) использовалось значение переменной EXTEN. Ниже пример такого вызова с листингом вывода в консоль Asterisk.
-- Executing [78120001122@RTU-IN:1] AGI("SIP/202-000001c1", "get_hg.php,78120001122") in new stack -- Launched AGI Script /var/lib/asterisk/agi-bin/get_hg.php -- <SIP/202-000001c1>AGI Script get_hg.php completed, returning 0 -- Executing [78120001122@RTU-IN:2] NoOp("SIP/202-000001c1", "Reult 240,250") in new stack -- Executing [78120001122@RTU-IN:3] NoOp("SIP/202-000001c1", "GET FORWARD ring_timeout,forward_after_enable,forward_after_numer,timeout_ring_after == 30,1,SIP/RTU/79627277780,30") in new stack -- Executing [78120001122@RTU-IN:4] Set("SIP/202-000001c1", "ARRAY(RING_TIMEOUT,FORWARD_AFTER_ENABLE,FORWARD_AFTER_NUMBER,TIMEOUT_RING_AFTER)=30,1,SIP/RTU/79627277780,30") in new stack -- Executing [78120001122@RTU-IN:5] Set("SIP/202-000001c1", "ARRAY(m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12,m13,m14,m15)=240,250") in new stack -- Executing [78120001122@RTU-IN:6] Goto("SIP/202-000001c1", "HG,78120001122,1") in new stack -- Goto (HG,78120001122,1) -- Executing [78120001122@HG:1] Answer("SIP/202-000001c1", "") in new stack > 0x7fde046867c0 -- Probation passed - setting RTP source address to 192.168.85.148:40046 -- Executing [78120001122@HG:2] Set("SIP/202-000001c1", "H1=SIP/240") in new stack -- Executing [78120001122@HG:3] Set("SIP/202-000001c1", "H2=&SIP/250") in new stack -- Executing [78120001122@HG:4] Set("SIP/202-000001c1", "H3=") in new stack -- Executing [78120001122@HG:5] Set("SIP/202-000001c1", "H4=") in new stack -- Executing [78120001122@HG:6] Set("SIP/202-000001c1", "H5=") in new stack -- Executing [78120001122@HG:7] Dial("SIP/202-000001c1", "SIP/240&SIP/250,30,TtrM(monext)") in new stack [Jan 18 15:50:14] WARNING[11241][C-00000113]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 18 15:50:14] WARNING[11241][C-00000113]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) == Everyone is busy/congested at this time (2:0/0/2) -- Executing [78120001122@HG:8] GotoIf("SIP/202-000001c1", "0?end") in new stack -- Executing [78120001122@HG:9] Set("SIP/202-000001c1", "CALLERID(num)=78120001122") in new stack -- Executing [78120001122@HG:10] Dial("SIP/202-000001c1", "SIP/RTU/79627277780,30,TtrM(monext)") in new stack == Using SIP RTP CoS mark 5 -- Called SIP/RTU/79627277780
Теперь немного подкорректируем нашу конфигурацию для того чтобы можно было переадресовать вызо в произвольный канал или на голосовую почту:
INSERT INTO `inbound` (`did`, `numbers`, `ring_timeout`, `forward_after_enable`, `forward_after_numer`, `timeout_ring_after`, `Notes`) VALUES ('78120001122', '240,250', 30, 1, 'SIP/RTU/79627277780', 30, 'Тестовая запись');
В контексте HG:
1) Меняем строку – в приложении Dial удаляем значение «технология/канал» равное «SIP/RTU», так как теперь мы целиком подставляем конструкцию, которая описывает куда нужно позвонить. Таким образом, мы можем переадресовать вызов параллельно на несколько внешних номеров, используя символ (&), например, SIP/79627277780&SIP/78126470011.
2) Если вызов нужно отправить в другой контекст, например, на голосовую почту, то заменять А-номер – переменную CALLERID(num) не нужно, это легко реализовать проверкой переменной ${FORWARD_AFTER_NUMBER} на наличие символа @, который свидетельствует о использовании канала Local и переводе вызова в другой контекст, например, Local/203@vm.
В итоге, модифицированный контекст:
[HG] exten => _X.,1, Set(H1=${IF($["${m1}" != ""]?SIP/${m1})}) same => n, Set(H2=${IF($["${m2}" != ""]?&SIP/${m2})}) same => n, Set(H3=${IF($["${m3}" != ""]?&SIP/${m3})}) same => n, Set(H4=${IF($["${m4}" != ""]?&SIP/${m4})}) same => n, Set(H5=${IF($["${m5}" != ""]?&SIP/${m5})}) same => n,Dial(${H1}${H2}${H3}${H4}${H5},${RING_TIMEOUT},TtrM(monext)) same => n,GotoIf($["${FORWARD_AFTER_ENABLE}" != "1"]?end) same => n,GotoIf($[${REGEX("@" ${FORWARD_AFTER_NUMBER})}]?nochangecid) same => n,Set(CALLERID(num)=${IF($["${REGEX("^7812.......$" ${EXTEN})}"]?${EXTEN}:78127778899)}) same => n(nochangecid),Dial(SIP/RTU/${FORWARD_AFTER_NUMBER},${TIMEOUT_RING_AFTER},Ttr) same => n(end),Hangup
Теперь вызов на 7812000112 где переменная NUMBER=SIP/RTU/79627277780. Ниже пример такого вызова с листингом вывода в консоль Asterisk.
-- Executing [78120001122@RTU-IN:1] AGI("SIP/202-000001d2", "get_hg.php,78120001122") in new stack -- Launched AGI Script /var/lib/asterisk/agi-bin/get_hg.php -- <SIP/202-000001d2>AGI Script get_hg.php completed, returning 0 -- Executing [78120001122@RTU-IN:2] NoOp("SIP/202-000001d2", "Reult 240,250") in new stack -- Executing [78120001122@RTU-IN:3] NoOp("SIP/202-000001d2", "GET FORWARD ring_timeout,forward_after_enable,forward_after_numer,timeout_ring_after == 30,1,SIP/RTU/79627277780,30") in new stack -- Executing [78120001122RTU-IN:4] Set("SIP/202-000001d2", "ARRAY(RING_TIMEOUT,FORWARD_AFTER_ENABLE,FORWARD_AFTER_NUMBER,TIMEOUT_RING_AFTER)=30,1,SIP/RTU/79627277780,30") in new stack -- Executing [78120001122@RTU-IN:5] Set("SIP/202-000001d2", "ARRAY(m1,m2,m3,m4,m5)=240,250") in new stack -- Executing [78120001122@RTU-IN:6] Goto("SIP/202-000001d2", "HG,78120001122,1") in new stack -- Goto (HG,78120001122,1) -- Executing [78120001122@HG:1] Answer("SIP/202-000001d2", "") in new stack > 0x7fde046867c0 -- Probation passed - setting RTP source address to 192.168.85.148:40026 -- Executing [78120001122@HG:2] Set("SIP/202-000001d2", "_MONITOR_FILENAME=IN-78120001122-2017_01_19[10_29]-202-78120001122-1484810974.920") in new stack -- Executing [78120001122@HG:3] NoOp("SIP/202-000001d2", "!!!!CALLED=") in new stack -- Executing [78120001122@HG:4] Set("SIP/202-000001d2", "H1=SIP/240") in new stack -- Executing [78120001122@HG:5] Set("SIP/202-000001d2", "H2=&SIP/250") in new stack -- Executing [78120001122@HG:6] Dial("SIP/202-000001d2", "SIP/240&SIP/250,30,Ttr") in new stack [Jan 19 10:29:34] WARNING[12800][C-0000011f]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 19 10:29:34] WARNING[12800][C-0000011f]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) == Everyone is busy/congested at this time (2:0/0/2) -- Executing [78120001122@HG:7] GotoIf("SIP/202-000001d2", "0?end") in new stack -- Executing [78120001122@HG:8] GotoIf("SIP/202-000001d2", "0?nochangecid") in new stack -- Executing [78120001122@HG:9] Set("SIP/202-000001d2", "CALLERID(num)=78120001122") in new stack -- Executing [78120001122@HG:10] Dial("SIP/202-000001d2", "SIP/RTU/79627277780,30,Ttr") in new stack == Using SIP RTP CoS mark 5 -- Called SIP/RTU/79627277780
Заменим номер для переадресации с SIP/RTU/79627277780 на голосовую почту в 203 ящик, используем локальный псевдоканал Local:
INSERT INTO `inbound` (`did`, `numbers`, `ring_timeout`, `forward_after_enable`, `forward_after_numer`, `timeout_ring_after`, `Notes`) VALUES ('78120001122', '240,250', 30, 1, 'Local/203@vm', 30, 'Тестовая запись');
Запись Local/203@vm означает, что нужно перенаправить вызов контекст vm плана набора.
Аналогично вызовы на voicemail можно направлять в любые контексты. Сам контекст vm может быть таким:
[vm] exten => _X.,1,VoiceMail(${EXTEN}@voicemail) same => n,Hangup
В файле voicemail.conf описан ящик 203:
[voicemail] ; контекст голосовой почты 203 => 0,Ignat_203,ignat203@gmail.com
Теперь, после вызова в группе поиска, сработает переадресация на голосовой ящик абонента 203 для того чтобы оставить ему сообщение. Ниже пример вызова с листингом вывода в консоль Asterisk.
== Using SIP RTP CoS mark 5 -- Executing [78120001122@RTU-IN:1] AGI("SIP/202-000001d1", "get_hg.php,78120001122") in new stack -- Launched AGI Script /var/lib/asterisk/agi-bin/get_hg.php -- <SIP/202-000001d1>AGI Script get_hg.php completed, returning 0 -- Executing [78120001122@RTU-IN:2] NoOp("SIP/202-000001d1", "Reult 240,250") in new stack -- Executing [78120001122@RTU-IN:3] NoOp("SIP/202-000001d1", "GET FORWARD ring_timeout,forward_after_enable,forward_after_numer,timeout_ring_after == 30,1,Local/203@vm,30") in new stack -- Executing [78120001122@RTU-IN:4] Set("SIP/202-000001d1", "ARRAY(RING_TIMEOUT,FORWARD_AFTER_ENABLE,FORWARD_AFTER_NUMBER,TIMEOUT_RING_AFTER)=30,1,Local/203@vm,30") in new stack -- Executing [78120001122@RTU-IN:5] Set("SIP/202-000001d1", "ARRAY(m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12,m13,m14,m15)=240,250") in new stack -- Executing [78120001122@RTU-IN:6] Goto("SIP/202-000001d1", "HG,78120001122,1") in new stack -- Goto (HG,78120001122,1) -- Executing [78120001122@HG:1] Answer("SIP/202-000001d1", "") in new stack > 0x7fde046867c0 -- Probation passed - setting RTP source address to 192.168.85.148:40020 -- Executing [78120001122@HG:4] Set("SIP/202-000001d1", "H1=SIP/240") in new stack -- Executing [78120001122@HG:5] Set("SIP/202-000001d1", "H2=&SIP/250") in new stack -- Executing [78120001122@HG:6] Set("SIP/202-000001d1", "H3=") in new stack -- Executing [78120001122@HG:7] Set("SIP/202-000001d1", "H4=") in new stack -- Executing [78120001122@HG:8] Set("SIP/202-000001d1", "H5=") in new stack -- Executing [78120001122@HG:9] Dial("SIP/202-000001d1", "SIP/240&SIP/250,30,Ttr") in new stack [Jan 19 10:23:24] WARNING[12770][C-0000011e]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) [Jan 19 10:23:24] WARNING[12770][C-0000011e]: app_dial.c:2431 dial_exec_full: Unable to create channel of type 'SIP' (cause 20 - Subscriber absent) == Everyone is busy/congested at this time (2:0/0/2) -- Executing [78120001122@HG:10] GotoIf("SIP/202-000001d1", "0?end") in new stack -- Executing [78120001122@HG:11] GotoIf("SIP/202-000001d1", "1?nochangecid") in new stack -- Goto (HG,78120001122,13) -- Executing [78120001122@HG:13] Dial("SIP/202-000001d1", "Local/203@vm,30,Ttr") in new stack -- Called Local/203@vm -- Executing [203@vm:1] VoiceMail("Local/203@vm-0000000a;2", "203@voicemail") in new stack -- Local/203@vm-0000000a;1 answered SIP/202-000001d1
Похожие материалы:
Tags: Asterisk