artem
13.03.2018
15833

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

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

AMI из PHP

Первым делом определимся с реализацией. Возьмем скриптовый серверный язык 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: loginrn»;
$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: Originatern»;
       $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: Originatern»;
       $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: Originatern»;
       $action .= «Channel: SIP/$ext_fromrn»;
       $action .= «Callerid: Phoenix-whispersrn»;
       $action .= «Application: ChanSpyrn»;
       $action .= «Data: $ext_to,wxrn»;
       $action .= «Timeout: 15000rn»;
       $action .= «Priority: 1rnrn»;
       $action .= «Async: yesrnrn»;
       fputs($socket,$action);
       usleep(500000);
}

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

function destr_call($channel){
       global $socket;
       $action  = «Action: Hanguprn»;
       $action .= «Channel: $channelrnrn»;
       fputs($socket,$action);
       usleep(500000);
}
 

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

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

function mute_conf($conf_num,$channel){
       global $socket;
       $action  = «Action: ConfbridgeMutern»;
       $action .= «Conference: $conf_numrn»;
       $action .= «Channel: $channelrnrn»;
       fputs($socket,$action);
       usleep(500000);
}
Отмена предыдущей команды.
function unmute_conf($conf_num,$channel){
       global $socket;
       $action  = «Action: ConfbridgeUnmutern»;
       $action .= «Conference: $conf_numrn»;
       $action .= «Channel: $channelrnrn»;
       fputs($socket,$action);
       usleep(500000);
}

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

function start_record_conf($conf_num){
       global $socket;
       $action  = «Action: ConfbridgeStartRecordrn»;
       $action .= «Conference: $conf_numrn»;
       fputs($socket,$action);
       usleep(500000);
}

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

function stop_record_conf($conf_num){
       global $socket;
       $action  = «Action: ConfbridgeStopRecordrn»;
       $action .= «Conference: $conf_numrn»;
       fputs($socket,$action);
       usleep(500000);
}

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

function locked_conf($conf_num){
       global $socket;
       $action  = «Action: ConfbridgeLockrn»;
       $action .= «Conference: $conf_numrn»;
       fputs($socket,$action);
       usleep(500000);
}
Отмена: открытие конференц-комнаты.
function unlocked_conf($conf_num){
       global $socket;
       $action  = «Action: ConfbridgeUnlockrn»;
       $action .= «Conference: $conf_numrn»;
       fputs($socket,$action);
       usleep(500000);
}

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

function kick_from_conf($conf_num,$channel){
       global $socket;
       $action  = «Action: ConfbridgeKickrn»;
       $action .= «Conference: $conf_numrn»;
       $action .= «Channel: $channelrnrn»;
       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: loginrn»;
              $auth .= «Username: «.ami_user.»rn»;
              $auth .= «Secret: «.ami_pass.»rn»;
              $auth .= «Events: offrnrn»;
              fputs($socket,$auth);
              usleep(500000);
       }
             
       function mute_conf($conf_num,$channel){
              global $socket;
              $action  = «Action: ConfbridgeMutern»;
              $action .= «Conference: $conf_numrn»;
              $action .= «Channel: $channelrnrn»;
              fputs($socket,$action);
              usleep(500000);
       }
 
       function unmute_conf($conf_num,$channel){
              global $socket;
              $action  = «Action: ConfbridgeUnmutern»;
              $action .= «Conference: $conf_numrn»;
              $action .= «Channel: $channelrnrn»;
              fputs($socket,$action);
              usleep(500000);
       }
      
       function start_record_conf($conf_num){
              global $socket;
              $action  = «Action: ConfbridgeStartRecordrn»;
              $action .= «Conference: $conf_numrn»;
              fputs($socket,$action);
              usleep(500000);
       }
 
       function stop_record_conf($conf_num){
              global $socket;
              $action  = «Action: ConfbridgeStopRecordrn»;
              $action .= «Conference: $conf_numrn»;
              fputs($socket,$action);
              usleep(500000);
       }     
 
       function locked_conf($conf_num){
              global $socket
              $action  = «Action: ConfbridgeLockrn»;
              $action .= «Conference: $conf_numrn»;
              fputs($socket,$action);
              usleep(500000);
       }
 
       function unlocked_conf($conf_num){
              global $socket;
              $action  = «Action: ConfbridgeUnlockrn»;
              $action .= «Conference: $conf_numrn»;
              fputs($socket,$action);
              usleep(500000);
       }
 
       function kick_from_conf($conf_num,$channel){
              global $socket;
              $action  = «Action: ConfbridgeKickrn»;
              $action .= «Conference: $conf_numrn»;
              $action .= «Channel: $channelrnrn»;
              fputs($socket,$action);
              usleep(500000);
       }
      
       function init_call($ext_from,$ext_to){
              global $socket;
              $action  = «Action: Originatern»;
              $action .= «Channel: SIP/$ext_fromrn»;
              $action .= «Callerid: Phoenix-call <$ext_from>rn»;
              $action .= «Timeout: 15000rn»;
              $action .= «Context: from-internalrn»;
              $action .= «Exten: $ext_torn»;
              $action .= «Priority: 1rnrn»;
              $action .= «Async: yesrnrn»;
              fputs($socket,$action);
              usleep(500000);
       }
 
       function pickup_call($ext_from,$channel){
              global $socket;
              $action  = «Action: Originatern»;
              $action .= «Channel: SIP/$ext_fromrn»;
              $action .= «Callerid: Phoenix-Pickuprn»;
              $action .= «Application: PickupChanrn»;
              $action .= «Data: $channelrnrn»;
              $action .= «Timeout: 15000rn»;
              $action .= «Priority: 1rnrn»;
              $action .= «Async: yesrnrn»;
              fputs($socket,$action);
              usleep(500000);
       }
 
       function spy_call($ext_from,$ext_to){
              global $socket;
              $action  = «Action: Originatern»;
              $action .= «Channel: SIP/$ext_fromrn»;
              $action .= «Callerid: Phoenix-Spyrn»;
              $action .= «Application: ChanSpyrn»;
              $action .= «Data: $ext_to,qxrn»;
              $action .= «Timeout: 15000rn»;
              $action .= «Priority: 1rnrn»;
              $action .= «Async: yesrnrn»;
              fputs($socket,$action);
              usleep(500000);
       }
 
       function whispers_call($ext_from,$ext_to){
              global $socket;
              $action  = «Action: Originatern»;
              $action .= «Channel: SIP/$ext_fromrn»;
              $action .= «Callerid: Phoenix-whispersrn»;
              $action .= «Application: ChanSpyrn»;
              $action .= «Data: $ext_to,wxrn»;
              $action .= «Timeout: 15000rn»;
              $action .= «Priority: 1rnrn»;
              $action .= «Async: yesrnrn»;
              fputs($socket,$action);
              usleep(500000);
       }
 
       function destr_call($channel){
              global $socket;
              $action  = «Action: Hanguprn»;
              $action .= «Channel: $channelrnrn»;
              fputs($socket,$action);
              usleep(500000);
       }
      
       function ami_disconnect(){
              global $socket;
              $action = «Action: Logoff\r\n\r\n»;
              fputs($socket,$action);
              usleep(500000);
              fclose($socket);
       }
 
?>

Кейсы внедрения
Asterisk от VoxLink
Узнайте, какие крупные компании уже используют Asterisk в работе.
Скачать
Подписаться
Уведомить о
guest
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии

Остались вопросы?

Я - Кондрашин Игорь, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.

VoIP оборудование


ближайшие курсы

10 доводов в пользу Asterisk

Распространяется бесплатно.

Asterisk – программное обеспечение с открытым исходным кодом, распространяется по лицензии GPL. Следовательно, установив один раз Asterisk вам не придется дополнительно платить за новых абонентов, подключение новых транков, расширение функционала и прочие лицензии. Это приближает стоимость владения станцией к нулю.

Безопасен в использовании.

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

Надежен в эксплуатации.

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

Гибкий в настройке.

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

Имеет огромный функционал.

Во многом именно Asterisk показал какой должна быть современная телефонная станция. За многие годы развития функциональность Asterisk расширилась, а все основные возможности по-прежнему доступны бесплатно сразу после установки.

Интегрируется с любыми системами.

То, что Asterisk не умеет сам, он позволяет реализовать за счет интеграции. Это могут быть интеграции с коммерческими телефонными станциями, CRM, ERP системами, биллингом, сервисами колл-трекинга, колл-бэка и модулями статистики и аналитики.

Позволяет телефонизировать офис за считанные часы.

В нашей практике были проекты, реализованные за один рабочий день. Это значит, что утром к нам обращался клиент, а уже через несколько часов он пользовался новой IP-АТС. Безусловно, такая скорость редкость, ведь АТС – инструмент зарабатывания денег для многих компаний и спешка во внедрении не уместна. Но в случае острой необходимости Asterisk готов к быстрому старту.

Отличная масштабируемость.

Очень утомительно постоянно возвращаться к одному и тому же вопросу. Такое часто бывает в случае некачественного исполнения работ или выбора заведомо неподходящего бизнес-решения. С Asterisk точно не будет такой проблемы! Телефонная станция, построенная на Asterisk может быть масштабируема до немыслимых размеров. Главное – правильно подобрать оборудование.

Повышает управляемость бизнеса.

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

Снижает расходы на связь.

Связь между внутренними абонентами IP-АТС бесплатна всегда, независимо от их географического расположения. Также к Asterisk можно подключить любых операторов телефонии, в том числе GSM сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.