Курсы по использованию Asterisk

IP-телефония — технология будущего. Обучитесь работе с IP-АТС Asterisk для того чтобы внедрить и профессионально использовать при решении коммуникационных задач.

Работайте с Asterisk профессионально!

Многоуровневая защита IP-АТС Asterisk

Телефонные станции очень часто становятся объектами хакерских атак. Узнайте, каким образом необходимо строить многоуровневую защиту для Вашей IP-АТС.

Не оставьте хакерам шансов. Защитите свой Asterisk от атак.

Используйте Веб-Интерфейс для удобства настройки

Панель управление FreePBX позволяет легко и удобно управлять всей системой. Научитесь эффективно использовать FreePBX для решения своих задач.

Управление станцией и статистика в окне браузера.

Научитесь работать с Asterisk из консоли

Для понимания работы с Asterisk необходимо уметь настраивать его вручную с конфигурационными файлами и командной строкой CLI Asterisk.

Научитесь «тонкой» настройке Asterisk

Цель курсов - максимум практики.

Обучение нацелено на практическую работу с IP-оборудованием: платы потоков E1, VoIP-телефонные аппараты, голосовые шлюзы FXS и прочее.

Обучение на реальном оборудовании — залог успеха.

AMI функционал из PHP

База знаний Linux

Статья посвященная набору стандартных Action's (действий), которыми можно управлять из любого кода или приложения удаленно сервером телефонии.

Первым делом определимся с реализацией. Возьмем скриптовый серверный язык PHP (но им все не ограничено, подробнее читать в статье: ) и напишем несколько базовых функций-примитивов. Но прежде всего разберемся с тем, куда и как отправляются команды.

Оглавление:

·         Создание сокета и его закрытие

·         Авторизация и ее отмена

·         Примеры функций работы со звонками

Создание сокета и его закрытие

Первым делом нужно определиться с тем куда мы будем обращаться на сетевом уровне. Нам необходимо создать объект типа сокет. Выполняем это следующим образом (внимание! параметры указаны примерные, в Вашей реализации они могут отличаться):

$socket = fsockopen(ami_host, ami_port, $ac_err_num, $ac_err_msg, 3);
       if (!$socket){
              echo "AMI connection: failed!
              Error number: ".$ac_err_num."
              Error notice: ".$ac_err_msg."
";
       }
       else{
              //echo "AMI connection: success
";
       }

Константы: ami_host, ami_port должны быть определены до создания сокета. По умолчанию при запуске внутри сервера применяются параметры:

define('ami_host','localhost');
define('ami_port','5038');

Теперь у нас определен сетевой объект на который мы будем засылать обращения. Следующим шагом опишем, как нам освободить его. Для этого достаточно всего одной строки:

fclose($socket);

Обратите внимание, эта функция вызывается с параметром сокета, который необходимо закрыть.

Авторизация и ее отмена

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

Первым делом снова объявим константами логин и пароль:

define('ami_user','fromami');
define('ami_pass','testamiconnecter');
И попробуем пройти авторизацию, выполнив следующее обращение к сокету:
$auth  = "Action: login\r\n";
$auth .= "Username: ".ami_user."\r\n";
$auth .= "Secret: ".ami_pass."\r\n";
$auth .= "Events: off\r\n\r\n";
fputs($socket,$auth);
usleep(500000);

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

Если все правильно и нашему пользователю разрешен доступ, мы увидем следующий ответ сокета (не обязательно в таком оформлении, но слова те же):

Ответ на авторизацию

Теперь у нас есть возможность отправлять команды в сокет, и посредством AMI управлять astrisk. Но прежде стоит заметить, что отмена авторизации выполняется так же обращением к сокету:

$action = "Action: Logoff\r\n\r\n";
fputs($socket,$action);
usleep(500000);


Примеры функций работы со звонками

К этому пункту мы умеем назначать объект сокета и освобождать его. Проходить и отменять авторизацию. Осталось описать то, зачем нам это было нужно: привести несколько примеров работы с AMI.

Для примера были выбраны самые распространенные функции АТС Asteriks: работа со звонком и конференцией.

1. Функции работы со звонком из AMI

Инициализируем вызов, за исключением некоторых различий функция напоминает синтаксис создания call-файлов. Обратите внимание в строке определения "Callerid", после самого имени, в угловых скобках указывается номер назначения, иначе в базу пойдет неверный номер.

function init_call($ext_from,$ext_to){
       global $socket;
       $action  = "Action: Originate\r\n";
       $action .= "Channel: SIP/$ext_from\r\n";
       $action .= "Callerid: Phoenix-call <$ext_to>\r\n";
       $action .= "Timeout: 15000\r\n";
       $action .= "Context: from-internal\r\n";
       $action .= "Exten: $ext_to\r\n";
       $action .= "Priority: 1\r\n\r\n";
       $action .= "Async: yes\r\n\r\n";
       fputs($socket,$action);
       usleep(500000);
}

Перезват вызова. Обратите внимание! Здесь нам необходимо передавать не только номер, того кто осуществляет перехват, но и номер канала.

function pickup_call($ext_from,$channel){
       global $socket;
       $action  = "Action: Originate\r\n";
       $action .= "Channel: SIP/$ext_from\r\n";
       $action .= "Callerid: Phoenix-Pickup\r\n";
       $action .= "Application: PickupChan\r\n";
       $action .= "Data: $channel\r\n\r\n";
       $action .= "Timeout: 15000\r\n";
       $action .= "Priority: 1\r\n\r\n";
       $action .= "Async: yes\r\n\r\n";
       fputs($socket,$action);
       usleep(500000);
}

Прослушка разговора. Использует два параметра: номера того кто прослушивает и того ктого будут слушать. Обратите внимание на параметры, которые передаются после номера вызываемого абонента.

function spy_call($ext_from,$ext_to){
       global $socket;
       $action  = "Action: Originate\r\n";
       $action .= "Channel: SIP/$ext_from\r\n";
       $action .= "Callerid: Phoenix-Spy\r\n";
       $action .= "Application: ChanSpy\r\n";
       $action .= "Data: $ext_to,qx\r\n";
       $action .= "Timeout: 15000\r\n";
       $action .= "Priority: 1\r\n\r\n";
       $action .= "Async: yes\r\n\r\n";
       fputs($socket,$action);
       usleep(500000);
}

Суфлирование вызова. Параметры аналогичные прослушке.

function whispers_call($ext_from,$ext_to){
       global $socket;
       $action  = "Action: Originate\r\n";
       $action .= "Channel: SIP/$ext_from\r\n";
       $action .= "Callerid: Phoenix-whispers\r\n";
       $action .= "Application: ChanSpy\r\n";
       $action .= "Data: $ext_to,wx\r\n";
       $action .= "Timeout: 15000\r\n";
       $action .= "Priority: 1\r\n\r\n";
       $action .= "Async: yes\r\n\r\n";
       fputs($socket,$action);
       usleep(500000);
}


И функция-антогонист инициализации вызова: завершение. Принимает единственный параметр - номер канала который необходимо прервать.

function destr_call($channel){
       global $socket;
       $action  = "Action: Hangup\r\n";
       $action .= "Channel: $channel\r\n\r\n";
       fputs($socket,$action);
       usleep(500000);
}
 

Просто? Возможно. А как будет выглядеть управление конференцией? Следующими функциями. Обратите внимание, здесь все функции имеют комплементарные пары.

Блокирование звука у участница конференции. Фактически перекрывание RTP потока от определенного канала. Поэтому передаются параметры: номер конференции и канала.

function mute_conf($conf_num,$channel){
       global $socket;
       $action  = "Action: ConfbridgeMute\r\n";
       $action .= "Conference: $conf_num\r\n";
       $action .= "Channel: $channel\r\n\r\n";
       fputs($socket,$action);
       usleep(500000);
}
Отмена предыдущей команды.
function unmute_conf($conf_num,$channel){
       global $socket;
       $action  = "Action: ConfbridgeUnmute\r\n";
       $action .= "Conference: $conf_num\r\n";
       $action .= "Channel: $channel\r\n\r\n";
       fputs($socket,$action);
       usleep(500000);
}

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

function start_record_conf($conf_num){
       global $socket;
       $action  = "Action: ConfbridgeStartRecord\r\n";
       $action .= "Conference: $conf_num\r\n";
       fputs($socket,$action);
       usleep(500000);
}

Отмена: выключение записи конференции.

function stop_record_conf($conf_num){
       global $socket;
       $action  = "Action: ConfbridgeStopRecord\r\n";
       $action .= "Conference: $conf_num\r\n";
       fputs($socket,$action);
       usleep(500000);
}

Блокирование конференции. Запрещает подключение новых участников конференции.

function locked_conf($conf_num){
       global $socket;
       $action  = "Action: ConfbridgeLock\r\n";
       $action .= "Conference: $conf_num\r\n";
       fputs($socket,$action);
       usleep(500000);
}
Отмена: открытие конференц-комнаты.
function unlocked_conf($conf_num){
       global $socket;
       $action  = "Action: ConfbridgeUnlock\r\n";
       $action .= "Conference: $conf_num\r\n";
       fputs($socket,$action);
       usleep(500000);
}

Функция удаления из конференции. Фактически, после оповещения оператора прерывает его канал, поэтому два параметра.

function kick_from_conf($conf_num,$channel){
       global $socket;
       $action  = "Action: ConfbridgeKick\r\n";
       $action .= "Conference: $conf_num\r\n";
       $action .= "Channel: $channel\r\n\r\n";
       fputs($socket,$action);
       usleep(500000);

Внимательный читатель мог заметить недостачу. Было оговорено, что все функции конференции парные. Но при этом у функции удаления из конфы не было пары. Как же попасть в конференцию? Все просто: нужно инициализировать вызов на внутренний номер оператора и связать с номером конференции. Т.е. допустимо использовать функцию init_call из предыдущего пункта.

Осталось доработать интерфейс на свой вкус и настроить взаимодействие с перечисленными функциями. По аналогии можно выполнить множество других команд, о которых можно прочитать по ссылке:

А в конце приведу полный код файла-скрипта. Его можно включать в проект и использовать как библиотеку.

<?php
 
       function ami_connect(){
              global $socket;
              $socket = fsockopen(ami_host, ami_port, $ac_err_num, $ac_err_msg, 3);
              if (!$socket){
                    echo "AMI connection: failed!
                           Error number:".$ac_err_num."
                           Error notice:".$ac_err_msg."
";
              }
              else{
                    //echo "AMI connection:success
";
              }
             
              $auth  = "Action: login\r\n";
              $auth .= "Username: ".ami_user."\r\n";
              $auth .= "Secret: ".ami_pass."\r\n";
              $auth .= "Events: off\r\n\r\n";
              fputs($socket,$auth);
              usleep(500000);
       }
             
       function mute_conf($conf_num,$channel){
              global $socket;
              $action  = "Action: ConfbridgeMute\r\n";
              $action .= "Conference: $conf_num\r\n";
              $action .= "Channel: $channel\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
 
       function unmute_conf($conf_num,$channel){
              global $socket;
              $action  = "Action: ConfbridgeUnmute\r\n";
              $action .= "Conference: $conf_num\r\n";
              $action .= "Channel: $channel\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
      
       function start_record_conf($conf_num){
              global $socket;
              $action  = "Action: ConfbridgeStartRecord\r\n";
              $action .= "Conference: $conf_num\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
 
       function stop_record_conf($conf_num){
              global $socket;
              $action  = "Action: ConfbridgeStopRecord\r\n";
              $action .= "Conference: $conf_num\r\n";
              fputs($socket,$action);
              usleep(500000);
       }     
 
       function locked_conf($conf_num){
              global $socket
              $action  = "Action: ConfbridgeLock\r\n";
              $action .= "Conference: $conf_num\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
 
       function unlocked_conf($conf_num){
              global $socket;
              $action  = "Action: ConfbridgeUnlock\r\n";
              $action .= "Conference: $conf_num\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
 
       function kick_from_conf($conf_num,$channel){
              global $socket;
              $action  = "Action: ConfbridgeKick\r\n";
              $action .= "Conference: $conf_num\r\n";
              $action .= "Channel: $channel\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
      
       function init_call($ext_from,$ext_to){
              global $socket;
              $action  = "Action: Originate\r\n";
              $action .= "Channel: SIP/$ext_from\r\n";
              $action .= "Callerid: Phoenix-call <$ext_from>\r\n";
              $action .= "Timeout: 15000\r\n";
              $action .= "Context: from-internal\r\n";
              $action .= "Exten: $ext_to\r\n";
              $action .= "Priority: 1\r\n\r\n";
              $action .= "Async: yes\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
 
       function pickup_call($ext_from,$channel){
              global $socket;
              $action  = "Action: Originate\r\n";
              $action .= "Channel: SIP/$ext_from\r\n";
              $action .= "Callerid: Phoenix-Pickup\r\n";
              $action .= "Application: PickupChan\r\n";
              $action .= "Data: $channel\r\n\r\n";
              $action .= "Timeout: 15000\r\n";
              $action .= "Priority: 1\r\n\r\n";
              $action .= "Async: yes\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
 
       function spy_call($ext_from,$ext_to){
              global $socket;
              $action  = "Action: Originate\r\n";
              $action .= "Channel: SIP/$ext_from\r\n";
              $action .= "Callerid: Phoenix-Spy\r\n";
              $action .= "Application: ChanSpy\r\n";
              $action .= "Data: $ext_to,qx\r\n";
              $action .= "Timeout: 15000\r\n";
              $action .= "Priority: 1\r\n\r\n";
              $action .= "Async: yes\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
 
       function whispers_call($ext_from,$ext_to){
              global $socket;
              $action  = "Action: Originate\r\n";
              $action .= "Channel: SIP/$ext_from\r\n";
              $action .= "Callerid: Phoenix-whispers\r\n";
              $action .= "Application: ChanSpy\r\n";
              $action .= "Data: $ext_to,wx\r\n";
              $action .= "Timeout: 15000\r\n";
              $action .= "Priority: 1\r\n\r\n";
              $action .= "Async: yes\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
 
       function destr_call($channel){
              global $socket;
              $action  = "Action: Hangup\r\n";
              $action .= "Channel: $channel\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
       }
      
       function ami_disconnect(){
              global $socket;
              $action = "Action: Logoff\r\n\r\n";
              fputs($socket,$action);
              usleep(500000);
              fclose($socket);
       }
 
?>

sip, Подключение, Channel, call, callerid