Skip to the content.

Глава 18. AGI

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

– Эдди Веддер

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

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

В этом разделе приведен краткий пример использования AGI.

Во-первых, давайте создадим скрипт, который мы собираемся запустить. Скрипты AGI как правило помещаются в /var/lib/asterisk/agi-bin.

$ cd /var/lib/asterisk/agi-bin
$ vim hello-world.sh
#!/bin/bash
# Consume all variables sent by Asterisk
while read VAR && [ -n ${VAR} ] ; do : ; done
# Answer the call.
echo "ANSWER"
read RESPONSE
# Say the letters of "Hello World"
echo 'SAY ALPHA "Hello World" ""'
read RESPONSE
exit 0
$ chown asterisk:asterisk hello-world.sh
$ chmod 700 hello-world.sh

Теперь добавьте следующую строку в /etc/asterisk/extensions.conf в контекст [sets]:

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

Сохраните и перезагрузите свой диалплан и теперь, когда вы звоните на номер 237, то должны услышать, как Эллисон произносит “Hello World.”

Варианты AGI

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

Process-Based AGI

Process-based AGI (AGI на основе процесса) является простейшим вариантом AGI. Пример быстрого запуска в начале этой главы является примером сценария Process-based 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 application AGI

Плюсы Process-Based AGI (на основе процессов)

Это самая простая форма реализации аги.

Минусы Process-Based AGI

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

EAGI

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

*CLI> core show application EAGI

Плюсы расширенного AGI

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

Минусы расширенного AGI

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

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

FastAGI—AGI через TCP

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

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

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

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

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

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

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

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

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

В этом примере DNS-сервера для домена shifteight.org потребуется хотя бы одна SRV-запись, настроенная для _agi._tcp.shifteight.org.

Плюсы FastAGI

Более эффективен чем process-based AGI. Вместо того, чтобы порождать новый процесс на вызов, можно построить сервер FastAGI для обработки нескольких вызовов.

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

Минусы FastAGI

Он является более сложным при реализации сервера FastAGI, чем реализация приложения AGI на основе процессов.

Async AGI - АМИ-контролируемый AGI

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

Дополнительную информацию об Asterisk Manager Interface можно найти в Главе 17.

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

exten => 239,AGI(agi:async)

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

Плюсы Async AGI

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

Минусы Async аги

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

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

Чтобы использовать Async AGI, учетная запись AMI должна иметь разрешение agi как на read, так и на write. Например, следующий пользователь определенный в manager.conf будет иметь возможность как а) выполнять действия менеджера AGI, так и б) получать события AGI:


; Определите пользователя с именем "hello" и паролем "world".
; Предоставьте этому пользователю разрешения на чтение/запись для AGI.
;
[hello]
secret = world
read = agi
write = agi
      

Обзор коммуникаций AGI

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

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

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

AGI на основе процессов/FastAGI

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

переменная_agi: значение

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

Переменная Значение/пример Описание
agi_request hello-world.sh Первый аргумент, который был передан в приложение AGI() или EAGI(). Для process-based 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 uniqueid для agi_channel.
agi_version 1.8.0-beta4 Используемая версия Asterisk.
agi_callerid 12565551212 Полная строка callerID, установленная на agi_channel.
agi_calleridname Russell Bryant Имя caller ID, установленное на agi_channel.
agi_callingpres 0 Представление вызывающего абонента, связанное с caller ID, установленное на agi_channel. Дополнительные сведения см. в выводе core show function CALLERPRES в CLI Asterisk.
agi_callingani2 0 ANI2 абонента, связанный с agi_channel.
agi_callington 0 ТН (тип номера) ID абонента, связанный с agi_channel.
agi_callingtns 0 Набранный номер TNS (выбор транзитной сети), связанный с 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 Код учетной записи, связанный с agi_channel.
agi_threadid 140071216785168 threadid потока в Asterisk, на котором выполняется приложение AGI() или EAGI(). Это может быть полезно для связывания журналов, созданных приложением AGI, с журналами, созданными Asterisk, поскольку журналы Asterisk содержат идентификаторы потоков.
agi_arg_<argument number> my argument Эти переменные предоставляют содержимое дополнительных аргументов, предоставленных приложению AGI() или EAGI().

Пример переменных, которые могут быть отправлены в приложение AGI, см. в разделе выходные данные отладки связи AGI в разделе Быстрый старт. Конец списка переменных будет обозначен пустой строкой. Код обрабатывает эти переменные путем считывания строк ввода в цикле, пока не будет получена пустая строка. В этот момент приложение продолжается и начинает выполнять команды 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 было закодировано URL-адресом.

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

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

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

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

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

Команда AGI Описание
ANSWER Ответ на входящий вызов.
ASYNCAGI BREAK Завершение сеанса Async AGI и возврат канала в диалплан Asterisk.
CHANNEL STATUS Получение статуса канала. Используется для получения текущего состояния канала, такого как up (ответ), down (трубка положена) или вызов.
DATABASE DEL Удаляет пару ключ/значение из встроенной базы данных AstDB.
DATABASE DELTREE Удаляет дерево пар ключ/значение из встроенной базы данных AstDB.
DATABASE GET Извлекает значение для ключа в базе данных AstDB.
DATABASE PUT Устанавливает значение для ключа в базе данных AstDB.
EXEC Выполняет приложение диалплана Asterisk на канале. Эта команда очень полезна в том, что между EXEC и GET FULL VARIABLE, вы можете сделать все что угодно с вызовом, который можете сделать из диалплана Asterisk.
GET DATA Считывание цифр от вызывающего абонента.
GET FULL VARIABLE Оценвает выражение диалплана Asterisk. Вы можете отправить строку, содержащую переменные и/или функции диалплана и Asterisk вернет результат после выполнения соответствующих подстановок. Эта команда очень полезна в том, что между EXEC и FET 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 ALFA Озвучить строку символов. Вы можете найти пример этого в разделе “Быстрый старт”. Чтобы получить локализованную обработку этого и другой команды SAY, устанавливающие язык канала либо в файле конфигурации устройств (например, sip.conf ) или в диалплане установкой функцией диалплана CHANNEL(language).
SAY DIGIT Озвучить строку цифр. Например, 100 будет озвучено “один ноль ноль”, если язык канала установлен на русский.
SAY NUMBER Озвучить номер телефона. Например, 100 будет сказано как “сто”, если язык канала установлен на русский.
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 (Telecommunications Device for the Deaf - телекоммуникационное устройство для глухих) на канале.
VERBOSE Отправить на канал сообщение verbose logger. Подробные сообщения отображаются в консоли Asterisk, если значение параметра verbose достаточно высокое. Подробные сообщения также будут отправляться в любой файл журнала, настроенный для verbose канала регистратора в /etc/asterisk/logger.conf.
WAIT FOR DIGIT Дождитесь, пока вызывающий абонент нажмет цифру.
SPEECH CREATE Инициализировать распознавание речи. Это необходимо сделать перед использованием других речевых команд AGI. b
SPEECH SET Установить настройку речевого движка. Доступные настройки относятся только к используемому механизму распознавания речи.
SPEECH DESTROY Уничтожить ресурсы, выделенные для выполнения распознавания речи. Эта команда должна быть последней выполненной речевой командой.
SPEECH LOAD GRAMMAR Загрузить грамматику
SPEECH UNLOAD GRAMMAR Выгрузить грамматику
SPEECH ACTIVATE GRAMMAR Активировать грамматику, которая была загружена.
SPEECH DEACTIVATE 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 command AGI в CLI Asterisk. Демонстрация поможет выяснить, как выполняются команды AGI с помощью метода Async AGI. Во-первых, расширение создается в диалплане, который выполняет сеанс Async AGI в канале:

exten = > 240,AGI(agi:async)

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

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

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 предоставит уведомление о том, что это произошло, чтобы ваше приложение могло соответствующим образом настроить свою работу.

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

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

Если вы хотите отключить функцию отправки Asterisk сигнала SIGHUP для вашего приложения process-based AGI или строки HANGUP для вашего сервера FastAGI - вы можете сделать это, установив переменную канала AGISIGHUP как показано в этом коротком примере:


; нет SIGHUP (AGI) или HANGUP (FastAGI)
exten => 237,1,Set(AGISIGHUP=no)
    same => n,AGI(hello-world.sh)

После того, как завершение канала произошло - единственные команды 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 (если он еще не был отключен).

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

Пример 18-1 - это пример сценария AGI. Чтобы запустить этот скрипт - сначала поместите его в каталог /var/lib/asterisk/agi-bin. Тогда вы бы выполнили его из диалплана Asterisk вот так:

exten = > 241,1, AGI(account-lookup.py)
    same => n,Hangup()

Этот пример написан на Python и очень скудно документирован для краткости. Он демонстрирует, как сценарий AGI взаимодействует с Asterisk с помощью stdin и stdout.

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

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

Пример 18-1. account-lookup.py

#!/usr/bin/env python
# Пример для AGI (Asterisk Gateway Interface).

import sys

def agi_command(cmd):
    '''Вписать команду и вернуть ответ'''
    print cmd
    sys.stdout.flush() #очистка буфера
    return sys.stdin.readline().strip() # строка пробелов

asterisk_env = {} # чтерие переменной среды AGI из Asterisk
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

# Поддельные "базы данных" учетных записей.
ACCOUNTS = {
    '12345678': {'balance': '50'},
    '11223344': {'balance': '10'},
    '87654321': {'balance': '100'},
}

response = agi_command('ANSWER')

# три аргумента: приглашение, тайм-аут, максимальная длина
response = agi_command('GET DATA enter_account 3000 8')

if 'timeout' in response:
    response = agi_command('STREAM FILE goodbye ""')
    sys.exit(0)

# Ответ будет выглядеть как: 200 result=<digits>
# С разделителем '=' мы получаем индекс 1
account = response.split('=', 1)[1]

if account == '-1': # ответ при ошибке
    response = agi_command('STREAM FILE astcc-account-number-invalid ""')
    response = agi_command('HANGUP')
    sys.exit(0)

if account not in ACCOUNTS: # неверный
    response = agi_command('STREAM FILE astcc-account-number-invalid ""')
    sys.exit(0)

balance = ACCOUNTS[account]['balance']

response = agi_command('STREAM FILE account-balance-is ""')
response = agi_command('SAY NUMBER %s ""' % (balance))
sys.exit(0)

Разрабатываемые фреймворки

Был предпринят ряд усилий по созданию фреймворков или библиотек, облегчающих программирование AGI. Вы заметите, что некоторые из них уже упоминались в Главе 17. Так же, как и в случае с AMI, при оценке фреймворка мы рекомендуем вам найти тот, который соответствует следующим критериям:

Зрелость

Этот проект существует уже несколько лет? Зрелый проект гораздо менее вероятно будет иметь серьезные ошибки в нем.

Поддержка

Проверьте дату последнего обновления. Если проект не обновлялся в течении нескольких лет - есть большая вероятность, что он был заброшен. Он все еще может быть полезен, но вы будете представлены сами себе. Аналогично, как выглядит трекер ошибок? Много ли важных ошибок, которые игнорируются? (Будьте проницательны здесь, так как часто реалии поддержки свободного проекта требуют тщательного отбора — не все функции будут добавлены.)

Качество кода

Это хорошо написанная структура? Если он не был спроектирован хорошо - вы должны знать об этом, решая стоит ли доверять ему свой проект.

Сообщество

Есть ли активное сообщество разработчиков, поддерживающих этот проект? В случае если вам понадобится помощь - будет ли она доступна?

Документация

Код должен быть хорошо прокомментирован, но в идеале, вики или другая официальная документация для поддержки библиотеки имеет важное значение.

На момент подготовки настоящего документа фреймворки, перечисленные в Таблице 18-3, удовлетворяли всем или большинству из приведенных выше критериев. Если вы не видите здесь библиотеку для вашего предпочтительного языка программирования, она может быть где-то там, но просто не вошедшей в наш список.

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

Фреймворк Язык
Adhearsion Ruby
Asterisk-Java Java
AsterNET .NET
ding-dong Node.js
PAGI PHP
Panoramisk Python
StarPy Python + Twisted

Вывод

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

Глава 17. AMI и файлы вызовов Содержание Глава 19. Asterisk REST Interface