AGI - Asterisk Gateway Interface

Mit einem Dialplan kann man zwar schon einiges anstellen, aber der Zugriff auf andere Programme ist damit nicht möglich. Für komplexe Anwendungen oder die Anbindung externer Programme kann man sich mittels "AGI" PlugIns schreiben. AGI bedeutet "Asterisk Gateway Interface" und ist dem "Common Gateway Interface" von Webservern nachempfunden.

Programmiersprache

Als Programmiersprache kann man verwenden was man will, es gibt da keine wirklichen Einschränkungen, das einzige was die Sprache können muss ist vom "standard input" (stdin) zu lesen und auf "standard output" (stdout) zu schreiben, und das kann so ziemlich jede Programmiersprache.
Am häufigsten verwendet werden Skriptsprachen wie bash, perl oder python, wer will kann aber auch Java, C oder C++ nehmen.
Ich werde Euch im folgenden Zeigen, wie man ein AGI in perl schreibt.

Protokoll

Die Kommunikation zwischen Asterisk und Eurer Erweiterung funktioniert folgendermaßen.
Wenn unser Programm startet, müssen wir so lange Zeilen von STDIN lesen, bis eine Leere Zeile kommt. Die gelesenen Zeilen haben den Aufbau "NAME: WERT", z.B. "agi_callerid: 1234".
Danach können wir unser Programm etwas nützliches tun lassen, z.B. den Türöffner betätigen, den Videorecorder starten, irgendwas fernsteuern ...
Wenn wir der Asterisk ein Kommando schicken wollen, müssen wir es einfach auf STDOUT schreiben, z.B. durch print, printf oder echo.

Commands

answer
channel status
control stream file
database del
database deltree
database get
database put
exec
get data
get option
get variable
hangup
noop
receive char
receive text
record file
say alpha
say date
say datetime
say digits
say number
say phonetic
say time
send image
send text
set autohangup
set callerid
set context
set extension
set music
set priority
set variable
stream file
tdd mode
verbose
wait for digit

Beispiel: /var/lib/asterisk/agi-bin/say_callerid.agi

#!/usr/bin/perl

$|=1; # Wichtig damit jedes "print" sofort geflusht wird
$=" "; # Jedes "print" muss mit einen Zeilenwechsel enden, und anstatt bei jedem print
# ein machen zu muessen - was man haeufig vergisst - kann man das in $ global definieren

#
# Schritt 1)
# Initialisierung, einlesen der Variablen
#

while(<STDIN>) { # Zeilenweise einlesen
chomp; # Zeilenendezeichen wegschneiden
last unless length($_); # Fertig wenn leere Zeile
($name, $wert) = /([^:]+): (.*)/; # Schluessel und Wert extrahieren
$AGI{$name} = $wert; # und im assoziativen Array (Hash) speichern
}

# debug Ausgabe mit dem Kommando VERBOSE auf der Asterisk Konsole (CLI)
# sollte erst jetzt verwendet werden, da wir alles ausgelesen haben
print "VERBOSE Der Anrufer hat die Nummer $AGI{'agi_callerid'}";

#
# Schritt 2)
# Kommando absetzen
#

# Dem Anrufer seine Rufnummer Vorsprechen
# SAY DIGITS nimmt zwei Parameter:
# die Ziffern zum Vorlesen und
# eine Liste von Ziffern mit denen der Anrufer die Ansage abbrechen kann (hier # oder 1 oder 2)
print "SAY DIGITS $AGI{'agi_callerid'} #12" unless $AGI{'agi_callerid'} eq "unknown";

#
# WICHTIG: Wir duerfen jetzt keine weiteren Ausgaben nach STDOUT machen, sonst denkt die Asterisk
# es kaeme ein neues Kommando. Auch darauf achten, dass weitere aufgerufene Programme
# nicht "versehentlich" irgendeine Ausgabe machen, das muesste alles nach /dev/null
# umgeleitet werden!
#

#
# Schritt 3)
# Jetzt MUESSEN wir die Rueckgabe abwarten bis die Ansage vorueber ist
#

while(<STDIN>) {
m/200 result=[^-]/ && last; # wenn alles in Ordnung ist, kommt bei SAY DIGITS "200 result=0" zurueck
# oder die entsprechende Taste, falls man eine gedrückt hat
}

#
# weiter mit Schritt 2) oder fertig
#

/etc/asterisk/extensions.conf (relevanter Teil)

exten = 1234,1,Answer
exten = 1234,2,AGI(say_callerid.agi)
exten = 1234,3,Hangup