Предисловие


Добрый день.

На написание данной статьи меня сподвигло 2 вещи: малое количество или вовсе отсутствие современных рабочих примеров по «фишкам» Asterisk, а так же нежелание специалистов делиться этими самыми «фишками» с остальными. Это я сейчас про РУ-комьюнити. Всякие «Деды» на форумах скорее обольют тебя помоями и отправят читать книжки десятилетней давности, чем дадут мало мальски полезную информацию. Сами же темы форумов, созданные в 2005-2010 годах, сильно устарели и иногда что то уже выпилено из текущей версии астериска, а что то надо очень сильно переделать, чтобы заработало.

Так вот.
В следствие отказа от CUCM в пользу Астериска, руководством была поставлена задача сохранить особо популярные у пользователей сервисы в максимально первозданном виде, дабы не третировать людей. Одним из таких было и создание конференций. К тому моменту с Астериском я уже был знаком, но не столь глубоко, по этому на перебор всевозможных вариантов конференции у меня ушло около полутора недель, а на окончательное решение натолкнула вообще другая задача.

Проблема заключается в том, что из похожего на решение есть статья с устаревшей meetme, а так же какой то монстр, которого у меня так и не удалось заставить работать. Я же предлагаю кое что не настолько громоздкое.

Мякотка


Описывать что такое confbridge, за что отвечают секции в том или ином конфиге и что это за опция такая, я не буду, эта информация как раз имеется и актуальна. Сейчас про решение в целом.

Задача: сделать так, чтобы конференцию можно было создать во время разговора, а затем пригласить туда еще абонентов. Основная проблема, что функция channelredirect работает не так как хотелось бы. То есть если выполнять ее из диалплана во время разговора, то один из каналов улетит куда надо, а второй разрушится, а лазать по всему диалплану на 2к строк и прописывать на Dial'ах опцию g было лениво. И мне совершенно не понятно, почему в большинстве мануалов все пытаются решить задачу только через диалплан, игнорируя возможность астериска работать с внешними скриптами и ami.

Итак. Астериск 14.4.0

Скрипт конференции на 2 варианта(c комментариями):

conference.php
<?php

//Готовим коннект к астеру
$host = "192.168.1.1";
$port = "5038";
$timeout = "10";

$user = "conference";
$pass = "1111";



//для того, чтобы не разбивать 1 задачу на 2 скрипта
$kusok = $argv[1];

//Кусок для создания конференции во время разговора
if ($kusok == 1){

//Получаем переменные
$channel = $argv[2];
$bridgepeer = $argv[3];
$confnum = $argv[4];

print_r($bridgepeer);
print_r($confnum);

                //Коннект
                $sconn = fsockopen ($host, $port, $timeout) or die ("Connection to $host:$port failed!");


                                        fputs ($sconn, "Action: Login\r\n");
                                        fputs ($sconn, "Username: $user\r\n");
                                        fputs ($sconn, "Secret: $pass\r\n\r\n");
                                        //Задаем переменные канала
                                        fputs ($sconn, "Action: Setvar\r\n");
                                        fputs ($sconn, "Channel: $channel\r\n");
                                        fputs ($sconn, "Variable: CONFNUM\r\n");
                                        fputs ($sconn, "Value: $confnum\r\n\r\n");


                                        fputs ($sconn, "Action: Setvar\r\n");
                                        fputs ($sconn, "Channel: $bridgepeer\r\n");
                                        fputs ($sconn, "Variable: CONFNUM\r\n");
                                        fputs ($sconn, "Value: $confnum\r\n\r\n");
                                        //Редиректим
                                        fputs ($sconn, "Action: Redirect\r\n");
                                        fputs ($sconn, "Channel: $bridgepeer\r\n");
                                        fputs ($sconn, "ExtraChannel: $channel\r\n");
                                        fputs ($sconn, "Context: service_code-ael\r\n");
                                        fputs ($sconn, "Exten: conference\r\n");
                                        fputs ($sconn, "Priority: 1\r\n\r\n");

                                        fputs($sconn, "Action: Logoff\r\n\r\n");
sleep(2);
fclose ($sconn);
}

//Кусок для добавления нового участника
if ($kusok == 2) {

//Получаем переменные
$confnum = $argv[2];
$inviten = $argv[3];

                $sconn = fsockopen ($host, $port, $errno, $errstr, $timeout) or die ("Connection to $host:$port failed!");


                                        //Подключаемся
                                        fputs ($sconn, "Action: Login\r\n");
                                        fputs ($sconn, "Username: $user\r\n");
                                        fputs ($sconn, "Secret: $pass\r\n\r\n");

                                        //Звоним и закидываем в конфу
                                        fputs ($sconn, "Action: Originate\r\n");
                                        fputs ($sconn, "Channel: Local/".$inviten."@out-ael\r\n");
                                        fputs ($sconn, "Context: service_code-ael\r\n");
                                        fputs ($sconn, "Exten: conference\r\n");
                                        fputs ($sconn, "Priority: 1\r\n");
                                        fputs ($sconn, "Variable: CONFNUM=".$confnum."\r\n\r\n");



                                        fputs($sconn, "Action: Logoff\r\n\r\n");
sleep(2);
fclose ($sconn);
}


}


Гуру программирования могут исправить код, сделав из него конфетку, я писал как умел.
Далее начинаем использовать данный скрипт непосредственно в самом Астериске.

Для того, чтобы создать конференцию, я выбрал комбинацию *1. Коротко и не пересекается с основной нумерацией.

Добавляем в features.conf вызов скрипта с передачей в него требуемых переменных

[applicationmap]
conference => *1,self,System(/usr/bin/php /home/script/conference.php 1 ${CHANNEL} ${BRIDGEPEER} ${CALLERID(num)})

Затем, чтобы это срабатывало, создаем в диалплане в секции [globals] переменную и добавляем нашу фичу

DYNAMIC_FEATURES=conference

Для добавления в уже созданную конференцию новых участников, потребуется прописать код в confbridge.conf

[default_menu]
type = menu
*1=dialplan_exec(service_code-ael,conference_add,1)

Ну а теперь самое вкусное, extensions.ael:

Для создание конференции (сюда адресует php скрипт оба разговорных канала):

        conference => {
                ConfBridge(${CONFNUM},,,default_menu);
        }

Для добавления нового пользователя (сюда адресует dialplan_exec):

conference_add => {
                Read(INVITEN,dial,11,i);
                System(/usr/bin/php /home/script/conference.php 2 ${CALLERID(num)} ${INVITEN});
        }

Все. Никаких килотонн кода в диалплане. Все емко. *1 в разговоре и вы в конфе, еще раз *1 гудок и набор номера, кого добавить.

Наросты


Гонимый пожеланиями пользователей, я стал развивать данную фичу.

Следующим стала возможность создавать конференции с нуля (не из разговора), а так же заходить в уже созданные конференции по их номеру, а не ждать приглашающего звонка

Добавляем в диалплан:

_*1XXXX => {
                NoOp(${CONFCHAN});
                Set(__CONFNUM=${EXTEN:2});
                System(/usr/bin/php /home/script/conference.php 3 ${CONFCHAN} ${CONFNUM} );
        }

Добавляем в скрипт:

conference.php
//Для создание конференций с нуля
if ($kusok == 3){

//Получаем переменные
$channel = $argv[2];
$confnum = $argv[3];


                //Коннект
                $sconn = fsockopen ($host, $port, $timeout) or die ("Connection to $host:$port failed!");


                                        fputs ($sconn, "Action: Login\r\n");
                                        fputs ($sconn, "Username: $user\r\n");
                                        fputs ($sconn, "Secret: $pass\r\n\r\n");
                                        //Задаем переменные канала
                                        fputs ($sconn, "Action: Setvar\r\n");
                                        fputs ($sconn, "Channel: $channel\r\n");
                                        fputs ($sconn, "Variable: CONFNUM\r\n");
                                        fputs ($sconn, "Value: $confnum\r\n\r\n");

                                        //Редиректим
                                        fputs ($sconn, "Action: Redirect\r\n");
                                        fputs ($sconn, "Channel: $channel\r\n");
                                        fputs ($sconn, "Context: service_code-ael\r\n");
                                        fputs ($sconn, "Exten: conference\r\n");
                                        fputs ($sconn, "Priority: 1\r\n\r\n");

                                        fputs($sconn, "Action: Logoff\r\n\r\n");
sleep(2);
fclose ($sconn);


Так же пришлось доработать строчку _*X.

        _*X. => {
                set(__CONFCHAN=${CHANNEL});
                Dial(Local/${EXTEN}@service_code-ael);

Теперь чтобы войти в конференцию или создать ее с нуля, просто совершается звонок на *1 и номер, например *15234.

Финальной мутацией этого сервиса пока является так называемая «групповая конференция». Это когда большим начальникам лень всех добавлять вручную, а хочется нажать одну кнопку и все в сборе. Для этого я решил сделать отдельные сервис коды (*XXX), чтобы людям и самому не путаться. Для нашей организации вряд ли в ближайшие 100 лет потребуется over1000 конференц групп, так что запаса нумерации должно хватить. У себя вы можете добавить как другой префикс, так выделить другую номерную емкость.

Диалплан:

        _*XXX=> {
                Set(CONFNUM=${CALLERID(num)});
                System(/usr/bin/php /home/script/groups.php ${EXTEN:1} ${CONFNUM});
                ConfBridge(${CONFNUM},,,default_menu);
        }

Сам скрип сбора участников:

groups.php
//Функция звонка
function call ($group, $confnum) {

        $many = count($group);
                //Цикл разбора массива номеров группы на номера
                for ($i=0; $many>$i; $i++) {

                        //достаем из массива номера
                        $num = trim(array_shift($group[$i]));
                        //Звоним
                        system("asterisk -rx \"channel originate Local/$num@out-ael application ConfBridge $confnum\"");
                }
}


//функция получения массива группы

function conf_group ($groupid) {

                //Коннектим в базу
                $opt = array(
                    PDO::ATTR_ERRMODE  => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
                );

                $pdo = new PDO("odbc:mssql_asterisk, "asterisk, "121212", $opt);


                        //Селектим из базы
                          $sql = "SELECT extension FROM [asterisk].[dbo].[conf_groups] where groupid = $groupid";

                          $select = $pdo->query($sql);
                          $result = $select->fetchAll();

                //Дисконенктим
                $pdo = NULL;
                return $result;
}

//Получаем значение группы, которой надо позвонить
$groupid = $argv[1];

if ($groupid == 0){

        //Получаем номер конференции
        $confnum = $argv[2];


        //Получаем массив номеров
        $group=conf_group($groupid);

        //Звоним
        call($group, $confnum);

}


Все группы хранятся в базе данных по структуре «Группы, номер, имя, описание». Если появляется новая группа, то просто добавляем ее в базу.

Теперь для сбора, например, всех директоров на совещание, генеральному всего лишь потребуется набрать *100. А как правило у больших боссов большие телефоны. Следовательно биндим *100 на любую клавишу speeddial'а, подписываем как «директора» и пользователь вообще не заморачивается, что ему набрать. Кнопку нажал — совещание собрал.

Теперь предвосхищая ваши вопросы:

Почему скрипты и ami? Потому что средствами диалплана у меня так и не получилось сделать вменяемый редирект обоих каналов, не растеряв их по дороге. В ami же в функции redirect можно прицепить доп канал + задать ему переменную (например номер конференции, чтобы он тоже мог кого то в нее добавить).

Так же вы могли заметить, что я вынес фичи в отдельный контекст service_code-ael. Это удобно, когда всяких фич у вас становится больше пары штук. Я решил делать их через *, следовательно в любом контексте я просто пишу _*X. и адресую в этот контекст. Возможно, кто то найдет решение изящнее, но я за несколько месяцев так и не нашел. А данный функционал пришелся пользователям по душе.

Почему ael, а не conf? Ну потому что он структурированней и читать его легче
и понятнее. Одна функция gotoif чего стоит. До lua я еще не дорос.

Почему в примере массового сбора originate сделаны через bash, а не через AMI? Проблема в том, что выполняя кучу originate подряд через ami, система ждет, пока завершится предыдущий, чтобы дать следующий. А если никто не берет трубку, а там 20 секунд no_ans и таких 5 штук? Можно будет до вечера ждать сбора.

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

Комментарии (48)


  1. JC_IIB
    20.10.2018 13:35
    +1

    Всякие «Деды» на форумах скорее обольют тебя помоями и отправят читать книжки десятилетней давности


    Кажется, я знаю, про кого идет речь :-D


    1. Mobile1
      20.10.2018 15:52

      Кажется, я знаю, про кого идет речь :-D


      Это который оверквоттинг не любит?
      О да, это известная в узких кругах личность ;)


  1. willyd
    20.10.2018 18:17

    Да. Дед иногда пишет, когда лучше промолчать. Кстати, рекомендую #asterisk там вполне вменяемо отвечают, и зачастую, если кто-то сталкивался с проблемой ответят.

    Проблема в том, что выполняя кучу originate подряд через ami, система ждет, пока завершится предыдущий, чтобы дать следующий. А если никто не берет трубку, а там 20 секунд no_ans и таких 5 штук? Можно будет до вечера ждать сбора.

    Так ведь async есть.
    Async — Set to true for fast origination.

    А так все как вроде красиво. У меня управление сделано через веб интерфейс, всякие drag'n'drop, dropdown… Так что функций слегка больше.


    1. willyd
      20.10.2018 23:42

      Небольшое дополнение. Не сочтите за критику.
      1. Вместо Action SetVar на канале, делайте Rdirect и Originate на Exten: CONFNUM.
      2. В конференцию напрямую можно дозвонится и так. Скрипты и AMI излишние.
      3. Чтобы одной кнопкой просто позвонить всем можно использовать Page, он правда закроет все каналы если инициатор выйдет.
      4. Проверьте открытые соединения AMI. Вызов скрипта у вас прерывается, когда вы отрываете канал редиректом, это может работать, а может и нет, но скорее всего у вам часть соединений будет болтатся открытыми. Правильно бы было вызов скрипта оторвать от треда астериска, каким-нибудь daemonize.


      1. MerZoTTa Автор
        21.10.2018 00:24

        1. Я подумал, что раз уже залез в AMI, то почему бы и не сделать там SetVar.
        2. Можно, но почему то тогда по *1 срабатывает не dialplan_exec, а DYNAMIC_FEATURES.
        3. Этого хотелось избежать, так как инициатор может по каким то причинам выйти из конфы, например чтобы пригласить в кого то, сидящего за IVR.
        4. Вроде не болтается, но спасибо.


        1. willyd
          21.10.2018 01:10

          2. Можно, но почему то тогда по *1 срабатывает не dialplan_exec, а DYNAMIC_FEATURES.

          В смысле? Вам же тупо нужно дозвониться в ConfBridge. Это чистый dialplan.


          1. MerZoTTa Автор
            21.10.2018 01:22

            А если этот дозвонившийся захочет добавить в конференцию своего друга?
            У меня почему то, в таком случае, отрабатывает *1 именно из DYNAMIC_FEATURES, хотя по логике должно из dialplan_exec, так как он уже сидит в конфе. Может баг, может фича. Когда делал не было времени выяснять, надеюсь, что выясню в будущем


  1. nomadmoon
    20.10.2018 23:05

    Мужик, немедленный плюс тебе в карму. Возможно я ниосилятор но кто бы знал сколько боли мне в свое время принесла именно настройка конференции в астериске, а про пхп я даже не подумал, хотя неплохо его знаю. В следующий раз буду копать в эту сторону.


  1. Softer
    20.10.2018 23:55

    Спасибо как минимум за идеи, но от скриптования дальше диалпланов я бы отказался… Как-то это… Костыльно… Для себя это в некоторой мере может быть и удобно, а вот для последователей может оказаться не совсем очевидно.


    1. willyd
      20.10.2018 23:59

      Ну на самом деле не все вещи можно сделать в диалплане. И если у вас много динамических параметров, то план набора становится очень большой и не менее костыльный и уж точно не очевидней.


      1. arheops
        21.10.2018 00:19

        Простите, а можно доказать утверждение «не все вещи можно сделать в диалплане»?
        Фигня то в том, что диалплан явлется полной граматикой, а если вы училися в любом из отечественных университетов, у вас была хоть гдето теорема о том, что любую задачку грамматикой можно решить, ну из тех, которые вы вообще решить можете используя любой язык программированния.
        Громоздкий и костыльный диаплан становится у тех, кто не умеет пользоваться преимуществами граматик. Учитывая наличия функций работы со строчками, мат функций и regexp — диалплан обычно можно написать примерно в то же количество строк, что и программу на любом ЯП. Ну c точностью до o(1).


        1. willyd
          21.10.2018 00:48

          Насколько я знаю, данная задача в диалплане не разрешима, если только вы agi не считаете таковым. Плюс. есть всякие приложения, которые требуют отложенного выполнения, и вы их будете делать петлей через внешнее приложение.
          Я не писал, что дилплан прям убог. Или, как Мэт Джоржан — пишите все в ARI. Но задачи, которые неразрешимы только его средствами присутствуют.


          1. arheops
            21.10.2018 00:50

            Какая задача неразрешима в диалплане, уточните. Ато вы както начинаете спорить с доказаными теоремами.
            Возможно да, возможно нет. Но их точно можно сделать петлей через Originate и Wait.


            1. willyd
              21.10.2018 00:59

              Я не спорю не с чем.
              Я имел ввиду закинуть оба плеча звонка в конференцию с помощью кастом приложения.


              1. arheops
                21.10.2018 01:10

                Закинуть оба плеча с помощью диалплана можно. Надо сделать detach и подождать 0.1 секунды перед забросом. Нет проблемы.


                1. willyd
                  21.10.2018 01:18

                  detach где?
                  Я что-то не помню не в приложениях не в их опциях.


                  1. arheops
                    21.10.2018 01:29

                    detach/fork — это стандартный примитив програмирования. В диалпане используется Orginate.
                    После Orginate у вас получается новый thread(call) и он не зависит от главного. Чем не detach?
                    Как сделать несколько Originate из диалплана — я уже написал ниже же.


                    1. willyd
                      21.10.2018 01:36

                      Я знаю, что это означает.
                      Суть в том, как вы это в application map впишите, и как избежать того, что бридж развалится как только вы оторвете одно из плеч.


                      1. arheops
                        21.10.2018 01:39

                        если вы про application/features — оно должно просто форкнуть звонок и закрыться. После чего форкнутый звонок становится управляющим, звонит на оригинатор с использованием auto-answer и на все остальные телефоны по необходимости(но надо подождать, пока hangup отработает).

                        С бридажами там не очевидно. Там не все разваливатся, надо смотреть. Там надо Transfer с двумя аргументами использовать.

                        Могу только сказать, что я делал 3-way call на чистом диалпане и оно работало. А не, у меня там был один внешний скрипт, он AMI Transfer выполнял( не использую System для таких вещей).


                        1. willyd
                          21.10.2018 01:52

                          Могу только сказать, что я делал 3-way call на чистом диалпане и оно работало.
                          Блин, вот в этом подвох.
                          Там нужно быть уверенным, что у тебя за предыдущей командой hangup не отработает быстрее чем ты схватишь этот канал. Ну и как бы из диалплана не особо вариантов управления другими каналами.
                          В моем случае, я бы все сделал через вэб-хук и AMI. Тем более что у меня уже есть функция кидающая оба звонка в конференцию.


                          1. arheops
                            21.10.2018 01:55

                            wait(0.2) поставьте после dial. делов то.
                            Проблемы, конечно, бывают. Но решаемые.

                            Зачем вам web сервер держать, если есть fastagi? Который еще и переменные сразу передаст и проще на порядок. Можно через xinetd запускать по мере необходимости.


                            1. willyd
                              21.10.2018 02:03

                              Потому-что у меня полноценный веб интерфейс на сервер. Со всякими свистоперделками, одна и который это drag'n'drop звонка в конференцию. Но в любом случае, я пока вижу выход только через скрипт, как он будет запущен, будь-то через AGI или через веб-хук, разницы нет. Хотя для интереса нужно будет как-то попробовать.


                      1. arheops
                        21.10.2018 01:42

                        Отрыв плеча не опасен сам по себе. После отрыва termination плеча origination плечо уходит дальше по диаплану(параметр g команды Dial).


                        1. willyd
                          21.10.2018 01:54

                          Но вы ведь не знаете, где этот звонок. То есть, у вас должны везде одинаково отрабатываться завершение. И еще нужно какую-то переменную закинуть на этот канал, а фича у нас отрабатывала на другом.


                          1. arheops
                            21.10.2018 01:56

                            О, вы начали понимать, что PBX должна проектироваться, а не писаться по кусочкам.
                            Да, у вас должен быть стандартный диалплан завершения звонка и стандартная команда собственно звонка.


                            1. willyd
                              21.10.2018 02:07

                              Ну хватит, пожалуйста.
                              У меня все стандартное по максимуму, но в новой системе, я немного отошел и сейчас сделал несколько типов Dial'a в зависимости от вызывающего приложения.


                            1. willyd
                              21.10.2018 02:23

                              Ну и asterisk, пока еще пишется людьми и распространяется свободно.
                              У меня ~120 серверов с системой. На одном из них на прошлой неделе вылез баг с LOCAL переменной. Я в ней храню счетчики попыток для IVR. На всех серверах этот контекст отрабатывается нормально, на этом — нет. Переменная затирается при переходе на t или i. Пришлось отказаться от использования локальной переменной.


    1. MerZoTTa Автор
      21.10.2018 01:28

      Ну на самом деле еще спорно, что будет для последователей сложнее, вникать в навороченный диалплан или все же в отдельные скриптики по одному на каждую задачу.


      1. arheops
        21.10.2018 01:32

        Просто скриптики имеют такое противное свойство теряться при бекапах и восстановлениях системы.
        А еще даже на перле скриптики перестают работать через 10 лет(сталкивался).
        На самом деле самый правильный вариант — диалплан + один fastagi сервер в стандартном месте. Внутри которого разложены по функциям ваши «скриптики».


  1. arheops
    21.10.2018 00:15

    ОЧЕНЬ не советую вообще использовать команду System внутри диалпланов астериска.
    ОСОБЕННО не советую это делать тем, кто не понимает причин необходимости экранированния переменных.

    Я, вобщемто, догадываюсь, почему топик стартеру не нравится ded. Тут вон даже не использованы стандартные php-agi библиотеки, вместо них лютый велосипедизм.

    Давайте рассмотрим вот этот фрагментик.

    System(/usr/bin/php /home/script/conference.php 2 ${CALLERID(num)} ${INVITEN})

    А теперь внимательно подумаем. А есть ли хоть что-то, что запрещает в современном астериске вписать сюда… да что угодно, включая спецсиволы баша.
    А ведь вообще ничего. Чтоб убедится, делаем вот такой простой диалплан
    exten => 1111,1,Answer
    same => n,Playback(beep)
    same => n,Set(CALLERID(num)=ops)
    same => n,NOOP(CALLERID NUM = ${CALLERID(num)})
    same => n,Hangup


    смотрим
    pro-sip*CLI> core set verbose 3
    — Executing [1111@test:1] Answer(«IAX2/iaxtest-7530», "") in new stack
    — Executing [1111@test:2] Playback(«IAX2/iaxtest-7530», «beep») in new stack
    — <IAX2/iaxtest-7530> Playing 'beep.gsm' (language 'en')
    — Executing [1111@test:3] Set(«IAX2/iaxtest-7530», «CALLERID(num)=ops») in new stack
    — Executing [1111@test:4] NoOp(«IAX2/iaxtest-7530», «CALLERID NUM = ops») in new stack
    — Executing [1111@test:5] Hangup(«IAX2/iaxtest-7530», "") in new stack

    Остальное даже анализировать както не хочется. Не делайте так, как написано. Используйте библиотеки и fastagi/ami


    1. willyd
      21.10.2018 00:31

      Я так понимаю, у них in-house решение. И учитывая, что этот вопрос ТС решался таким путем, я думаю, что у простых смертных там не особо есть возможности что-либо менять.
      А так-то да, agi тут как нельзя кстати.


      1. arheops
        21.10.2018 00:33

        Ну так прийдет им callerid(num)=`rm /etc/asterisk/ -f` от партнера, и удачи, че.
        Инхауз.
        Не надо тут AGI. Щас допишу решение одной из «проблем». Тут все диапланом делается.


        1. MerZoTTa Автор
          21.10.2018 00:38

          От оператора связи такой ерунды не придет, а вся внутренняя кухня только в моих руках.
          Ну и я не утверждаю, что это идеальный вариант. Каждый переделает так, как считает нужным и под свою задачу. Это, скорее, мысли на бумаге.


          1. arheops
            21.10.2018 00:42

            Ага, ага. Пишите дальше. И екстеншены вам не взломают, и оператора не взломают. И у оператора все ок будет(мало кто из операторов вообще говоря фильтрует это поле).


          1. willyd
            21.10.2018 00:50

            Хах… А вы давно работаете с VoIP и связью вообще?


            1. MerZoTTa Автор
              21.10.2018 00:59

              Ну вообще почти 4 года в федеральном операторе связи. И я не разу не видел, чтобы что то подобное было в части номера. Не знаю, что там у мелких виртуальных voip операторов, у которых ни лицензии, ни своих номеров, но у нас все поля, относящиеся к src и dst жестко контролировались на каждом узле.


              1. arheops
                21.10.2018 01:04

                А я 20 лет. И я такое ВИДЕЛ.
                У вас может и контролируются, но вы же это выложили в сеть и оно 100% будет использовано на узлах, где не контролируется. Да хоть пользователь в софтфоне может написать.
                4 года это джун вообще говоря.


                1. MerZoTTa Автор
                  21.10.2018 01:17

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

                  пользователь в софтфоне может написать

                  Не может, callerid прописывает сам астериск абоненту при каждом звонке.


                  1. arheops
                    21.10.2018 01:34

                    Может, вам два человека уже сказало, что так бывает.
                    Просто экранируйте переменные, ОСОБЕННО переменную callerid.


                    1. willyd
                      21.10.2018 01:40

                      Я использую отдельные переменные на пирах для хранения номеров.


              1. willyd
                21.10.2018 01:07

                А я сейчас с американцами работаю. И у меня отлично прилетают спутанные CLID и CNAM.


        1. willyd
          21.10.2018 00:57

          del. не туда


  1. arheops
    21.10.2018 00:41

    Ну давайте решим одну случайно взятую проблему, изза которой понадобился баш через диалпан.

    Итак, проблема

    Проблема в том, что выполняя кучу originate подряд через ami, система ждет, пока завершится предыдущий, чтобы дать следующий


    Что, говорите не решается?
    А как вы это в баше делаете? Ну как как… паралельно запускаете. В чем проблема проделать это в диалплане? Нет проблемы… Просто надо думать диалпланом. Смотрим

    ;func_odbc.conf
    ; тут можно было получить всю строчку целиком за счет sql, но мы же повторяем логику в php скрипте, потому просто получаем список куда звонить
    [GET_GROUPS]
    dsn=asterisk
    SELECT group_concat(extension) FROM asteriskconf_groups where groupid = '${SQL_ESC(${ARG1})}'
    ; group_concat возвращает одно значение состоящее из екстеншенов через запятую, принадлежит стандарту slq92 или гдето там.

    ;extensions.conf
    [call_all_group]
    exten => _X.,1,Set(list=${ODBC_GET_GROUPS(${EXTEN})}); берем список, ид=текущий екстеншен.
    same => n,Execif($[ "${list}" == "" ]?Hangup); ничего нет.
    same => n,Set(res=)
    same => n,Set(i=1); ставим счетчик в 0. Стандартный for цикл в испольнении астериска.
    same => n(loop),GotoIF($[ "${CUT(list,\,,${i}}" == "" ]?endfor); если нет больше ничего, выходим
    same => n,Set(res=${res}Local/${CUT(list,\,,${i}}@originate_conf/n&); добавляем строчку звонка. Тут немного магии, да.
    smae => n,Set(i=$[ ${i} + 1 ]); увеличение параметра цикла. стандартно.
    same => n,Goto(loop)
    same => n(endloop),Dial(${res}); запускаем цикл из N дозвонов, параллельно.

    [originate_conf]
    exten => _X.,1,Originate(SIP/${EXTEN},app,Confbridge,1000); звоним каждому, посылаем в конференцию.


    И что, говорите, длиннее получилося?
    ps написано только что, без заготовок, время написания 20 минут(смотрите время коментов), не проверялося, если случайно работает считать невозможным чудом. Все совпадения случайны и так далее.


    1. willyd
      21.10.2018 00:56

      Что-то я немного запутался.
      Инициирующий канал куда делся?
      После первого ответа Dial не сбросит остальные каналы?


      1. arheops
        21.10.2018 01:03

        ну просто у вас малый скил параллельного программирования.
        первый диал выполняет стандартную группу RINGALL. Например вот так при двух экстеншенах.

        Dial(Local/1000@originate_conf/n&Local/1001@originate_conf)

        Дальше все.
        Каждый звонок в контекст originate_conf будет неотвечен, но создаст еще один канал, который собственно и звонит.
        будут выполнены ОБА звонка одновременно. Соответсвенно оба одновременных originate.
        Что случится с начальным каналом — неважно. можете там hangup дописать.


        1. willyd
          21.10.2018 01:03

          Только что курил, понял, там все каналы по сбросу закончатся.
          А инициатора забросить в ту же конференцию.


          1. arheops
            21.10.2018 01:07

            Это ж написано на коленке и не тестилося. Дальше делайте чего хотите.

            Я просто написал типичное решение «нерешаемой» проблемы в диаплане.

            Тут просто две стандартный структуры. for-loop (в первом контексте) и fork(во втором).
            А, еще третья — доступ к БД, но это совсем просто.


            1. willyd
              21.10.2018 01:14

              Я выше уточнил проблему, которую я имел ввиду.
              Ну и, пожалуйста, можете слегка спуститься? Я не из Непала… И хоть работал с ним меньше вашего, но тоже немало.