Глава 18

Интерфейс шлюза Asterisk (AGI)

Кофеин. Шлюз наркотиков.

-Eddie Vedder

Asterisk превратилась в простой, но мощный программный интерфейс для обработки вызовов. Тем не менее, многие люди, особенно те, кто имеет малый опыт программирования, по-прежнему предпочитают реализовывать свою обработку пользовательских вызовов на другом языке программирования. Использование другого языка программирования также может позволить вам использовать существующий код для интеграции с другими системами. Интерфейс шлюза Asterisk (AGI) позволяет разрабатывать функции управления сторонними вызовами на языке программирования по вашему выбору. Если вы не заинтересованы в реализации управления вызовами вне диалплана Asterisk, вы можете спокойно пропустить эту главу.

Быстрый старт

В этом разделе приведен краткий пример использования AGI. Сначала добавьте следующую строку в /etc/asterisk/extensions.conf:

exten => 500,1,AGI(hello-world.sh)

Затем создайте скрипт hello-world.sh в /var/lib/asterisk/agi-bin, как показано в Примере 21-1

Пример 21-1. Пример сценария AGI, hello-world.sh

#!/bin/bash

# Использовать все переменные, отправленные Asterisk

while read VAR && [ -n ${VAR} ] ; do : ; done

# Ответить на вызов.

echo «ANSWER»

read RESPONSE

# Произнести «Hello World»

echo ‘SAY ALPHA «Hello World» «»‘

read RESPONSE

exit 0

Теперь вызовите внутренний номер 500 с включенной отладкой AGI и послушайте, как Эллисон произносит «Hello World”:

*CLI> agi set debug on

AGI Debugging Enabled

— Executing [500@phones:1] AGI(«SIP/0004F2060EB4-00000009»,

«hello-world.sh») in new stack

— Launched AGI Script /var/lib/asterisk/agi-bin/hello-world.sh

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_request: hello-world.sh

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_channel: SIP/0004F2060EB4-00000009

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_language: en

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_type: SIP

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_uniqueid: 1284382003.9

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_version: SVN-branch-11-r378376

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callerid: 2563619899

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_calleridname: Russell Bryant

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callingpres: 0

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callingani2: 0

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callington: 0

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_callingtns: 0

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_dnid: 7010

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_rdnis: unknown

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_context: phones

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_extension: 500

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_priority: 1

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_enhanced: 0.0

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_accountcode:

<SIP/0004F2060EB4-00000009>AGI Tx >> agi_threadid: 140071216785168

<SIP/0004F2060EB4-00000009>AGI Tx >>

<SIP/0004F2060EB4-00000009>AGI Rx << ANSWER

<SIP/0004F2060EB4-00000009>AGI Tx >> 200 result=0

<SIP/0004F2060EB4-00000009>AGI Rx << SAY ALPHA «Hello World» «»

— <SIP/0004F2060EB4-00000009> Playing ‘letters/h.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/e.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/l.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/l.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/o.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/space.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/w.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/o.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/r.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/l.gsm’ (language ‘en’)

— <SIP/0004F2060EB4-00000009> Playing ‘letters/d.gsm’ (language ‘en’)

<SIP/0004F2060EB4-00000009>AGI Tx >> 200 result=0

—<SIP/0004F2060EB4-00000009>AGI Script hello-world.sh completed, returning 0

Варианты AGI

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

Process-Based AGI

Process-Based AGI (на основе процессов) — это самый простой вариант AGI. Пример быстрого запуска в начале этой главы является примером сценария AGI на основе процесса. Сценарий вызывается с использованием приложения AGI() из диалплана Asterisk. Приложение для запуска указано как первый аргумент AGI(). Если не указан полный путь, ожидается, что приложение будет находиться в каталоге /var/lib/asterisk/agi-bin. Аргументы, которые необходимо передать вашему приложению AGI, могут быть указаны в качестве дополнительных аргументов для AGI() в диалплане Asterisk. Синтаксис:

AGI(command[,arg1[,arg2[,…]]])

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

После того, как Asterisk выполнит ваше приложение AGI, связь между Asterisk и вашим приложением будет проходить через stdin и stdout. Более подробно об этом сообщении будет освещено в “Обзор связи AGI”. Дополнительные сведения о вызове AGI() из диалплана см. в документации, встроенной в Asterisk:

* CLI> core show applicationAGI

Плюсы AGI на основе процессов

Это простейшая форма AGI для реализации.

Недостатки AGI на основе процессов

Это наименее эффективная форма AGI в отношении потребления ресурсов. Системы с высокой нагрузкой должны учитывать FastAGI, обсуждаемые в «FastAGI-AGI через TCP» вместо этого.

EAGI

EAGI (Enhanced AGI) является небольшим вариантом для AGI(). Он вызывается в диалплане Asterisk как EAGI(). Разница заключается в том, что в дополнение к сообщениям stdin и stdout Asterisk также обеспечивает однонаправленный поток аудио, поступающий из канала в файловом дескрипторе 3. Для получения дополнительной информации о том, как вызвать EAGI() из диалплана Asterisk, проверьте встроенную документацию в Asterisk:

* CLI> core show application EAGI

Плюсы Enhanced AGI

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

Недостатки Enhanced AGI

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

Для альтернативного способа получения доступа к аудио вне Asterisk рассмотрите возможность использования JACK. Asterisk имеет модуль интеграции JACK, называемый app_jack. Он предоставляет приложение JACK() и функцию JACK_HOOK().

DeadAGI Is Dead

В версиях Asterisk до 1.8, было приложение называемое DeadAGI(). Его цель была аналогична цели AGI(), за исключением того, что вы использовали ее на канале, который уже повесил трубку. Обычно это делается в специальном расширении h, когда хотите использовать приложение AGI, чтобы помочь в некоторых типах обработки после вызова. Вызов DeadAGI() из диалплана будет по-прежнему работать, но вы получите сообщение WARNING в логе Asterisk. Он устарел в пользу использования AGI() во всех случаях. Код для AGI() был обновлен, поэтому он знает как правильно настроить работу после завершения вызова канала.

Плюсы DeadAGI

Нет. Он мертв.

Недостатки DeadAGI

Он мертв. На самом деле, не используйте его. Если вы это сделаете, ваша конфигурация может сломаться, если DeadAGI() полностью удалят из Asterisk в будущей версии.

FastAGI-AGI через TCP

FastAGI — это термин, используемый для управления вызовами AGI через TCP-соединение. С AGI на основе процессов экземпляр приложения AGI выполняется в системе для каждого вызова, а связь с этим приложением выполняется посредством stdin и stdout. С FastAGI TCP-соединение выполняется на сервере FastAGI. Управление вызовами выполняется с использованием одного и того же протокола AGI, но связь осуществляется по TCP-соединению и не требует запуска нового процесса для каждого вызова. Протокол AGI обсуждается более подробно в «Обзор связи AGI«.Использование FastAGI гораздо более масштабируемо, чем AGI на основе процессов, хотя его также сложнее реализовать.

FastAGI используется при вызове приложения AGI(), но вместо указания имени исполняемого приложения вы указываете agi:// URL-адрес. Например:

exten => 1234,1,AGI(agi://127.0.0.1)

Номер порта по умолчанию для подключения FastAGI — 4573. Другой номер порта может быть добавлен к URL после двоеточия. Например:

exten => 1234,1,AGI(agi://127.0.0.1:4574)

Так же как и с AGI на основе процессов, аргументы могут быть переданы в приложение FastAGI. Для этого добавьте их в качестве дополнительных аргументов в приложении AGI(), разделенных запятыми:

exten => 1234,1,AGI(agi://192.168.1.199,arg1,arg2,arg3)

FastAGI также поддерживает использование Service records записи (SRV-записи), если вы указываете URL-адрес в виде hagi://. Используя записи SRV, вы можете перечислить несколько хостов, к которым Asterisk может попытаться подключиться для высокой доступности и балансировки нагрузки. В следующем примере, чтобы найти сервер FastAGI для подключения, Asterisk выполнит поиск DNS для _agi._tcp.shifteight.org:

exten => 1234,1,AGI(hagi://shifteight.org)

Плюсы FastAGI

Он более эффективен, чем AGI на основе процессов. Вместо того, чтобы создавать процесс на вызов, сервер FastAGI может обрабатывать множество вызовов.

Может использоваться DNS для обеспечения высокой доступности и балансировки нагрузки между серверами FastAGI для дальнейшего повышения масштабируемости.

Недостатки FastAGI.

Сложнее реализовать сервер FastAGI, чем приложение AGI на основе процессов. Тем не менее, внедрение TCP-сервера было сделано намного раньше, поэтому есть множество примеров, доступных практически для любого языка программирования.

Async AGI-AMI-Контролируемый AGI

Async AGI — это новый метод использования AGI, который был впервые представлен в Asterisk 1.6.0. Цель async AGI — разрешить приложению, используещему AMI для асинхронной очереди команд, выполнение AGI на канале. Это может быть особенно полезно, если вы уже широко используете AMI и хотели бы воспользоваться одним и тем же приложением для обработки управления вызовами, в отличие от написания подробного диалплана Asterisk или разработки отдельного сервера FastAGI.

Более подробную информацию об AMI можно найти в Главе 20.

Async AGI вызывается приложением AGI(). Аргументом для AGI() должен быть agi:async, как показано в следующем примере:

exten => 1234,1,AGI(agi:async)

Дополнительную информацию о том, как использовать async AGI через AMI, можно найти в следующем разделе.

Плюсы async AGI

Существующее приложение AMI может использоваться для управления вызовами с использованием команд AGI.

Недостатки async AGI

Это самый сложный способ реализации AGI.

Настройка /etc/asterisk/manager.conf для Async AGI

«Конфигурация» в Главе 20 обсуждает параметры конфигурации manager.conf в подробностях. Чтобы использовать async AGI учетная запись AMI должна иметь agi разрешение read и write. Например, следующий определяемый пользователь в manager.conf будет определять возможность одновременно выполнять действия и получать события AGI диспетчера:

;

; Определите пользователя с именем ‘hello’ и паролем ‘world’.

; Дайте этому пользователю разрешения read/write для AGI.

;

[hello]

secret = world

read = agi

write = agi

Обзор связи AGI

В предыдущем разделе обсуждались варианты AGI, которые можно использовать. В этом разделе более подробно описано как ваше пользовательское приложение AGI взаимодействует с Asterisk после вызова AGI().

Настройка сеанса AGI

После вызова AGI() или EAGI() из диалплана Asterisk некоторая информация передается в приложение AGI для настройки сеанса. В этом разделе обсуждается, какие шаги предпринимаются в начале сеанса для разных вариантов AGI.

Process-Based AGI/FastAGI

Для приложения Process-Based AGI или подключения к серверу FastAGI переменные, перечисленные в Таблице 21-1, будут первыми данными, отправленными из Asterisk в ваше приложение. Каждая переменная будет в отдельной строке, в виде:

agi_variable: value

Таблица 21-1. Переменные среды AGI

Переменная Значение/Пример Описание
agi_request hello-world.sh Первый аргумент, переданный приложению AGI() или EAGI(). Для AGI на основе процесса это имя приложения AGI, которое было выполнено. Для FastAGI это был бы URL, который использовался для достижения сервера FastAGI.
agi_channel SIP/0004F2060EB4-00000009 Имя канала, который выполнил приложение AGI() или EAGI().
agi_language en Язык, установленный на agi_channel.
agi_type SIP Тип канала для agi_channel.
agi_uniqueid 1284382003.9 Уникальный идентификатор в agi_channel.
agi_version 1.8.0-beta4 Используемая версия Asterisk.
agi_callerid 12565551212 Полная строка CallerID, которая установлена на agi_channel.
agi_calleridname Russell Bryant CallerID name, заданное в agi_channel.
agi_callingpres 0 Представление абонента, связанное с CallerID, заданное на agi_channel. Дополнительные сведения см. в выходных данных core show function CALLERPRES в CLI Asterisk.
agi_callingani2 0 ANI2 абонента, связанное с agi_channel.
agi_callington 0 CallerID TON (тип номера), связанный с agi_channel.
agi_callingtns 0 Набранный номер TNS (Transit Network Select), связанный с agi_channel.
agi_dnid 7010 Набранный номер, связанный с agi_channel.
agi_rdnis unknown Номер перенаправления, связанный с agi_channel.
agi_context phones Контекст диалплана, в котором находился agi_channel при выполнении приложения AGI() или EAGI().
agi_extension 500 Внутр.номер в диалплане, который выполнял agi_channel при запуске приложения AGI() или EAGI().
agi_priority 1 Приоритет agi_extension в agi_context, который выполнил AGI() или EAGI().
agi_enhanced 0.0 Указание на то, использовался ли AGI() или EAGI() из диалплана. 0.0 указывает на использование метода AGI(), 1.0 — на EAGI().
agi_accountcode myaccount accountcode, связанный с agi_channel.
agi_threadid 140071216785168 threadid потока в Asterisk, в котором выполняется приложение AGI() или EAGI(). Это может быть полезно для связывания журналов, созданных приложением AGI, с журналами, созданными Asterisk, так как журналы Asterisk содержат идентификаторы потоков.
agi_arg_<argument number> my argument Эти переменные предоставляют содержимое дополнительных аргументов, предоставленных приложению AGI() или EAGI().

Пример переменных, которые могут быть отправлены в приложение AGI, см. в отчете об отладке связи AGI в «Быстрый старт«. Конец списка переменных указывается пустой строкой. Пример 21-1обрабатывает эти переменные, считывая строки ввода в цикле до тех пор, пока не будет получена пустая строка. В этот момент приложение продолжает работу и начинает выполнение команд AGI.

Async AGI

Когда вы используете async AGI, Asterisk отправляет событие диспетчера под названием AsyncAGI, чтобы инициировать сеанс async AGI. Это событие позволит приложениям, слушающим события диспетчера, взять на себя управление вызовом через действие диспетчера AGI. Вот пример события диспетчера, отправленного Asterisk:

Event: AsyncAGI

Privilege: agi,all

SubEvent: Start

Channel: SIP/0000FFFF0001-00000000

Env: agi_request%3A%20async%0Aagi_channel%3A%20SIP%2F0000FFFF0001-00000000%0A \

agi_language%3A%20en%0Aagi_type%3A%20SIP%0A \

agi_uniqueid%3A%201285219743.0%0A \

agi_version%3A%201.8.0-beta5%0Aagi_callerid%3A%2012565551111%0A \

agi_calleridname%3A%20Julie%20Bryant%0Aagi_callingpres%3A%200%0A \

agi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0A \

agi_dnid%3A%20111%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20LocalSets%0A \

agi_extension%3A%20111%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0A \

agi_accountcode%3A%20%0Aagi_threadid%3A%20-1339524208%0A%0A

Значение заголовка Env в этом событии диспетчера AsyncAGI находится на одной строке. Длинное значение заголовка Env кодируется в URI.

Команды и ответы

После настройки сеанса AGI Asterisk начинает обработку вызовов в ответ на команды, отправленные из приложения AGI. Как только команда AGI была выдана Asterisk, никакие другие команды не будут обработаны на том канале, пока текущая команда не будет завершена. По завершении обработки команды Asterisk выдаст результат.

AGI обрабатывает команды последовательно. После выполнения команды дальнейшие команды не могут быть выполнены до тех пор, пока Asterisk не вернет ответ. Выполнение некоторых команд может занять очень много времени. Например, команда AGI EXEC выполняет приложение Asterisk. Если команда является EXEC Dial, связь AGI заблокирована, пока не сделан вызов. Если на данном этапе приложению AGI требуется дальнейшее взаимодействие с Asterisk, оно может сделать это с помощью AMI, описанного в Главе 20.

Полный список доступных команд AGI можно получить из консоли Asterisk, выполнив команду agi showcommands. Эти команды описаны в Таблице 21-2. Чтобы получить более подробную информацию о конкретной команде AGI, включая синтаксическую информацию для любых аргументов, ожидаемых командой, используйте команду agi show commands topic <COMMAND>. Например, чтобы увидеть встроенную документацию для команды AGI ANSWER, вы должны использовать команду agi show commands topic ANSWER.

Таблица 21-2. Команды AGI

Команда AGI Описание
ANSWER Ответить на входящий вызов.
ASYNCAGY BREAK Завершить асинхронный сеанс AGI и вернуть канал в диалплан Asterisk.
CHANNEL STATUS Получение статуса канала. Используется для получения текущего состояния канала, такого как up (отвечено), down (отклонено) или ringing.
DATABASE DEL Удалить пару ключ/значение из встроенной базы данных AstDB.
DATABASE DELTREE Удалить дерево пар ключ/значение из встроенной базы данных AstDB.
DATABASE GET Получить значение для ключа в AstDB.
DATABASE PUT Установите значение для ключа в AstDB.
EXEC Выполнить приложение диалплана Asterisk в канале. Эта команда очень мощная в том, что между EXEC и GET FULL VARIABLE вы можете делать все, что угодно с помощью вызова, который вы можете сделать из диалплана Asterisk.
GET DATA Считывание цифр вызывающего абонента.
GET FULLVARIABLE Вычислить выражение диалплана Asterisk. Можно отправить строку, содержащую переменные и/или функции диалплана, а Asterisk вернет результат после выполнения соответствующих подстановок. Эта команда очень мощная в том, что между EXEC и GET FULL VARIABLE вы можете делать все что угодно с помощью вызова, который вы можете сделать из диалплана Asterisk.
GET OPTION Воспроизводить звуковой файл во время ожидания цифры от вызывающего абонента. Это похоже на приложение Background() диалплана.
GET VARIABLE Получение значения переменной канала.
HANGUP Завершить канал.a
NOOP Ничего не делать. Вы получите в ответе результат выполнения этой команды, как и от любой другой. Её можно использовать как простой тест связи с Asterisk.
RECEIVE CHAR Получить один символ. Работает только для типов каналов, которые поддерживают его, таких как IAX2 с помощью фреймов TEXT или SIP с помощью метода MESSAGE.
RECEIVE TEXT Получить текстовое сообщение. Работает в тех же случаях, что и RECEIVE CHAR.
RECORD FILE Записать звук от вызывающего абонента в файл. Эта блокирующая операция, аналогичная приложению диалплана Record(). Для записи вызова в фоновом режиме, во время выполнения других операций, используйте EXEC Monitor или EXEC MixMonitor.
SAY ALPHA Произнести строку символов. Вы можете найти пример этого в разделе “Быстрый старт”. Чтобы получить локализованную обработку этой и других команд SAY, установите язык канала либо в файле конфигурации устройства (например sip.conf), либо в диалплане посредством функции CHANNEL(language).
SAY DIGITS Произнести строку цифр. Например, 100 будет звучать как “one zero zero”, если язык канала установлен на английский.
SAY NUMBER Произнести число. Например, 100 будет звучать как “one hundred”, если язык канала установлен на английский.
SAY PHONETIC Произнести строку символов, но использовать общее слово для каждой буквы (Альфа, Браво, Чарли…).
SAY DATE Произнести данную дату.
SAY TIME Произнести данное время.
SAY DATETIME Произнести данную дату и время, используя указанный формат.
SEND IMAGE Отправить изображение в канал. IAX2 это поддерживает, но нет активно развивающихся клиентов IAX2, о которых мы знаем, чтобы это поддерживали.
SEND TEXT Отправить текст в канал, который его поддерживает. Может использоваться с каналами SIP и IAX2, по крайней мере.
SET AUTOHANGUP Запланировать завершение канала в определенный момент времени в будущем.
SET CALLERID Установить имя и номер вызывающего абонента для канала.
SET CONTEXT Установить текущий контекст диалплана для канала.
SET EXTENSION Установить текущий внутренний номер диалплана для канала.
SET MUSIC Запуск или остановка музыки в режиме ожидания для канала.
SET PRIORITY Установить текущий приоритет диалплана для канала.
SET VARIABLE Задать для переменной канала данное значение.
STREAM FILE Направить поток содержимого файла в канал.
CONTROL STREAM FILE Направить поток содержимого файла в канал, но также позволяет каналу управлять потоком. Например, канал может приостановить, перемотать назад или перемотать поток вперед.
TDD MODE Переключить режим TDD (телекоммуникационное устройство для глухих) для канала.
VERBOSE Отправить подробное сообщение в логгер канала. Подробные сообщения отображаются в консоли Asterisk, если параметр verbose достаточно высок. Подробные сообщения также отправляются в любой файл журнала, настроенный на verbose для логгера канала в файле /etc/asterisk/logger.conf.
WAIT FOR DIGIT Ожидать пока абонент нажмет цифру.
SPEECH CREATE Инициализация распознавания речи. Это необходимо сделать перед использованием других речевых команд AGI.b
SPEECH SET Настройка речевого движка. Доступные параметры относятся к используемому механизму распознавания речи.
SPEECH DESTROY Уничтожить ресурсы, выделенные для распознавания речи. Эта команда должна быть последней выполненной речевой командой.
SPEECH LOAD GRAMMAR Загрузить grammar.
SPEECH UNLOAD GRAMMAR Выгрузить grammar.
SPEECH ACTIVATE GRAMMAR Активировать загруженный grammar.
SPEECH DEACTIVATE GRAMMAR Деактивировать grammar.
SPEECH RECOGNIZE Воспроизводить подсказку и выполнять распознавание речи, а также ожидать цифры, которые будут нажаты.
GOSUB Выполнить подпрограмму диалплана. Она будет выполняться так же, как и приложение диалплана GoSub().

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

b Хотя Asterisk включает основной API для обработки распознавания речи, он не поставляется с модулем, обеспечивающим механизм распознавания речи. В настоящее время Digium предоставляет два коммерческих варианта распознавания речи: Lumenvox и Vestec.

Process-Based AGI/FastAGI

Команды AGI отправляются в Asterisk по одной строке. Строка должна заканчиваться одним символом новой строки. Как только команда была отправлена в Asterisk, дальнейшие команды не будут приниматься до тех пор, пока последняя команда не завершится, и ответ будет отправлен обратно в приложение AGI. Ниже приведен пример ответа на команду AGI:

200 result = 0

Консоль Asterisk позволяет отлаживать сообщения с помощью приложения AGI. Чтобы включить отладку связи AGI, запустите команду agi set debug on. Чтобы отключить отладку, используйте agi set debug off. Пока режим отладки включен, вся связь с и из приложения AGI будет выводиться в консоль Asterisk. Пример этого вывода можно найти в «Быстрый старт«.

Async AGI

Когда вы используете async AGI, команды выдаются с помощью диспетчера действий AGI. Чтобы увидеть встроенную документацию для действия менеджера AGI, запустите manager show AGI в Asterisk CLI. Демонстрация поможет выяснить, как команды AGI выполняются с использованием метода async AGI. Сначала в диалплане создается расширение, которое запускает сеанс async AGI на канале:

exten => 7011,1,AGI(agi:async)

Когда приложение диалплана AGI выполняется, событие диспетчера, называемое AsyncAGI, будет отправлено во все переменные среды AGI. Подробная информация об этом событии находится в разделе «Async AGI«. После этого действия диспетчера AGI могут начинаться через AMI.

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

Action: AGI

Channel: SIP/0004F2060EB4-00000013

ActionID: my-action-id

CommandID: my-command-id

Command: VERBOSE «Puppies like cotton candy.» 1

Response: Success

ActionID: my-action-id

Message: Added AGI command to queue

Event: AsyncAGI

Privilege: agi,all

SubEvent: Exec

Channel: SIP/0004F2060EB4-00000013

CommandID: my-command-id

Result: 200%20result%3D1%0A

Следующий результат — это то, что было видно на консоли Asterisk во время этого сеанса async AGI:

— Executing [7011@phones:1] AGI(«SIP/0004F2060EB4-00000013»,

«agi:async») in new stack

agi:async: Puppies like cotton candy.

== Spawn extension (phones, 7011, 1)

exited non-zero on ‘SIP/0004F2060EB4-00000013’

Завершение сеанса AGI

Сессия AGI завершается, когда ваше приложение AGI готово к завершению. Информация о том, как это происходит, зависит от того, использует ли ваше приложение Process-Based AGI, FastAGI или async AGI.

Process-Based AGI/FastAGI

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

Если происходит завершение канала, пока ваш сеанс AGI по-прежнему активен, Asterisk будет уведомлять об этом, чтобы ваше приложение могло соответствующим образом настроить свою работу.

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

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

Следующее что происходит после завершения канала заключается в том, что явное уведомление о завершении отправляется в ваше приложение. Для Process-Based AGI сигнал SIGHUP будет отправлен процессу для уведомления об отключении. Для подключения FastAGI Asterisk отправит строку, содержащую слово HANGUP. Если вы хотите отключить отправку Asterisk сигнала SIGHUP к приложению Process-Based AGI или строку HANGUP в ваш FastAGI сервер, вы можете сделать это, установив переменную канала AGISIGHUP как показано в следующем коротком примере:

;

; Не отправлять SIGHUP в процесс AGI

; или строку «HANGUP» на сервер FastAGI.

;

exten => 500,1,Set(AGISIGHUP=no)

same => n,AGI(my-agi-application)

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

Async AGI

Когда вы используете async AGI, интерфейс диспетчера предоставляет механизмы для уведомления о завершении каналов. Если вы хотите завершить сеанс async AGI для канала, то должны выполнить команду ASYNCAGI BREAK. Когда сеанс async AGI завершается, Asterisk отправит событие диспетчера AsyncAGI с SubEvent об End. Ниже приведен пример завершения сеанса async AGI:

Action: AGI

Channel: SIP/0004F2060EB4-0000001b

ActionID: my-action-id

CommandID: my-command-id

Command: ASYNCAGI BREAK

Response: Success

ActionID: my-action-id

Message: Added AGI command to queue

Event: AsyncAGI

Privilege: agi,all

SubEvent: End

Channel: SIP/0004F2060EB4-0000001b

В этот момент канал возвращается к диалплану Asterisk, если он еще не был завершен.

Пример: База данных учетных записей доступа

Пример 21-2 является примером сценария AGI. Чтобы запустить этот скрипт, вы должны сначала разместить его в каталоге /var/lib/asterisk/agi-bin. Затем выполните его из диалплана Asterisk следующим образом:

exten => agiexample1,1,AGI(agiexample1.py)

Этот пример написан на Python и задокументирован повсеместно. Он показывает, как скрипт может предложить вызывающему абоненту ввести номер учетной записи, а затем воспроизвести некоторую информацию из этой учетной записи. Что еще более важно, он показывает, как скрипт AGI взаимодействует с Asterisk для запуска команд AGI и получения ответа от Asterisk.

Пример 21-2. agiexample1.py

#!/usr/bin/env python

#

# Пример для AGI (Asterisk Gateway Interface).

#

# Ответить на звонок. Получить номер аккаунта от вызывающего абонента и

# воспроизвести некоторую информацию об учетной записи вызывающего абонента.

#

# Разработано для «Asterisk: The Definitive Guide»

#

import sys

#

# Команды AGI записываются в stdout. После обработки команды

# ответ от Asterisk поступает в stdin.

#

def agi_command(cmd):

»’Write out the command and return the response»’

# печатает отправку команды в stdout.

print cmd

# Убедитесь, что он не буферизуется.

sys.stdout.flush()

# Прочитать строку ответа из stdin. Используйте strip(), чтобы удалить

# любые пробелы, которые могут присутствовать (в первую очередь конец

# строки).

return sys.stdin.readline().strip()

#

# Прочитать переменные среды AGI, отправленные из Asterisk в скрипт при запуске.

# Ввод выглядит:

# agi_request: example1.py

# agi_channel: SIP/000FF2266EE4-00000009

# …

#

# После этого куска кода Вы сможете получить эту информацию как:

# asterisk_env[‘agi_request’]

# asterisk_env[‘agi_channel’]

#

asterisk_env = {}

while True:

line = sys.stdin.readline().strip()

if not len(line):

break

var_name, var_value = line.split(‘:’, 1)

asterisk_env[var_name] = var_value

#

# Настроить поддельную «базу данных» учетных записей. В более реалистичном

# скрипте AGI, который выполняет поиск данных учетной записи, вы подключитесь к

# внешней базе данных или другому API для получения этой информации. Для целей

# данного примера мы пропустим это и сделаем поиск данных из этого локального

# набора примеров.

#

ACCOUNTS = {

‘12345678’: {‘balance’: ’50’},

‘11223344’: {‘balance’: ’10’},

‘87654321’: {‘balance’: ‘100’},

}

#

# После вызова используем команду AGI — ANSWER.

#

# Смотри: *CLI> agi show commands topic ANSWER

#

response = agi_command(‘ANSWER’)

#

# Попросить абонента ввести номер учетной записи с помощью команды GET DATA.

# — Проиграть приглашение абоненту please-enter-account-number

# — Установить время ожидания 30 секунд

# — Прочитайть максимум 8 цифр (наши номера аккаунтов — 8 цифр)

#

# Смотри: *CLI> agi show commands topic GET DATA

#

response = agi_command(‘GET DATA please-enter-account-number 30000 8’)

#

# Если таймаут истек, просто выйти обратно в диалплан.

#

# Ответ будет выглядеть так:

# 200 result=<digits> (timeout)

#

if ‘timeout’ in response:

sys.exit(0)

#

# Ответ будет выглядеть так:

# 200 result=<digits>

# число будет -1 если произошла ошибка.

#

# Разделить ответ на ‘=’, максимум 1 раз. В результате получается массив из двух

# элементов. Второй элемент в массиве (индекс 1) является частью, которая пришла

# после ‘=’ и является номером аккаунта, который мы хотим.

#

account = response.split(‘=’, 1)[1]

#

# Если произошла ошибка, просто повесить трубку.

#

# Смотри: *CLI> agi show commands topic HANGUP

#

if account == ‘-1’:

response = agi_command(‘HANGUP’)

sys.exit(0)

#

# Если аккаунт недействителен, сообщить об этом абоненту и выйти из диалплана.

#

# Смотри: *CLI> agi show commands topic STREAM FILE

#

if account not in ACCOUNTS:

response = agi_command(‘STREAM FILE invalid-account «»‘)

sys.exit(0)

balance = ACCOUNTS[account][‘balance’]

#

# Сообщить абоненту баланс, а затем выйти обратно в диалплан.

#

# Смотри: *CLI> agi show commands topic SAY NUMBER

#

response = agi_command(‘STREAM FILE your-balance-is «»‘)

response = agi_command(‘SAY NUMBER %s «»‘ % (balance))

sys.exit(0)

Разработка Фреймворков

Был предпринят ряд усилий по созданию фреймворков или библиотек, облегчающих Программирование AGI. Таблица 21-3 перечисляет некоторые из них. Если вы не видите здесь библиотеки для предпочитаемого языка программирования, выполните быстрый поиск и вы, скорее всего, найдете ее как и другие.

Таблица 21-3. Разрабокта фреймворков AGI

Фреймворк Язык URL
Adhearsion Ruby http://adhearsion.com/
Asterisk-Java Java http://asterisk-java.org/
Asterisk-perl Perl http://asterisk.gnuinter.net/
PHPAGI PHP http://phpagi.sourceforge.net/
StarPy Python http://starpy.sourceforge.net/

Вывод

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

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

Я - Виталий Шелест, менеджер компании 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 сим-карты и настроить маршрутизацию вызовов по наиболее выгодному тарифу. Всё это позволяет экономить с первых минут пользования станцией.