AMI функционал из PHP
Статья затрагивает набор стандартных 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: 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);
       }
 
?>
	Остались вопросы?
Я - Першин Артём, менеджер компании Voxlink. Хотите уточнить детали или готовы оставить заявку? Укажите номер телефона, я перезвоню в течение 3-х секунд.
категории
- DECT
 - Linux
 - Вспомогательный софт при работе с Asterisk
 - Интеграция с CRM и другими системами
 - Интеграция с другими АТС
 - Использование Elastix
 - Использование FreePBX
 - Книга
 - Мониторинг и траблшутинг
 - Настройка Asterisk
 - Настройка IP-телефонов
 - Настройка VoIP-оборудования
 - Новости и Статьи
 - Подключение операторов связи
 - Разработка под Asterisk
 - Установка Asterisk
 
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 сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.
							
								
								
                            
                                
                                                                        База знаний                                                                    
                                                                        IP-АТС                                                                    
                                                                        Оборудование                                                                    
                                                                        О нас                                                                    
		
							
						
							
								
							
							
							
					
					
					
					
					
					
				
							


