Asterisk et l'interface AGI (Asterisk Gateway Interface)
Navigation rapide :
Introduction
Principe
Fonctionnalités
Appel du script AGI
Exemple de script avancé
Introduction
Principe
Fonctionnalités
Appel du script AGI
Exemple de script avancé
Interface AGI
Introduction
AGI (Asterisk Gateway Interface) est une interface permettant de faire communiquer le plan de numérotation (extensions.conf) avec des programmes extérieurs à Asterisk, écrits avec des langages de programmation aussi divers que PHP, Perl, Python, C, shell Linux.
Principe
AGI est matérialisé par l'écriture de scripts qui sont exécutés dans le plan de numérotation.
A chaque lancement d'un script AGI, Asterisk envoie au script un ensemble de variables avec leurs valeurs. Lorsque toutes les variables sont émises, Asterisk envoie une ligne vide pour préciser au script qu'il peut commencer ce pourquoi il a été écrit (suite d'instructions).
Le script envoie les commandes et Asterisk renvoie au script, pour chaque commande émise, une réponse.
Grâce à celà, Asterisk possède l'avantage de pouvoir être personnalisé, et là encore, la communauté autour d'Asterisk joue un rôle essentiel puisque de nombreux scripts sont disponibles sur le web, et il est possible d'obtenir de l'aide grâce aux forums spécialisés.
Fonctionnalités
Les fonctionnalités des scripts AGI sont aussi diverses que le langage de programmation choisi ne permet de possibilités.
Il est entre autres possible de :
- Faire des requêtes dans une base de données (et ainsi lier un serveur Asterisk à une application)
- Modifier les informations à l'affichage
- Améliorer les manipulations dans le plan de numérotation
- Améliorer le traitement des appels
- Créer un historique personnalisé
- Et bien d'autres encore...
A cela est ajouté la possibilité d'utiliser les autres applications de type AGI. A savoir :
- EAGI()
- DeadAGI()
- FastAGI()
Exemple : exten => 777, 1, AGI(agi://192.168.2.100/monscript.agi)
Appel du script AGI
Par défaut, les scripts sont créés et stockés dans /var/lib/asterisk/agi-bin (cela permet de ne pas mentionner le chemin complet d'appel des scripts). Il faudra toujours que les scripts disposent du droit d'exécution.
Ensuite, le script se lance de la manière suivante :
exten => 777, 1, Answer()
exten => 777, 2, AGI(monscript.agi|argument1|argument2...)
C'est ici que se fait l'interaction entre Asterisk et le programme.
La rédaction du script AGI se fait ensuite dans le langage de programmation choisi. Le programme devra récupérer les variables émises par Asterisk, et l'écriture du code se fait à l'aide de canaux de communication.
Voici un exemple de l'interaction entre Asterisk et AGI :
Source : 'VoIP et ToIP Asterisk' de Sébastien DEON, éditions ENI
Trois canaux de communications sont utilisés pour permettre le dialogue :
STDIN : AGI lit le canal STDIN sur lequel Asterisk émet ses informations.
STDOUT : AGI écrit ses infomations (commandes, variables) sur le canal STDOUT, qui seront transmises à Asterisk.
STDERR : AGI écrit des données sur le canal STDERR, à des fins essentiellement de debuggage. Le résultat s'affiche dans la CLI (Command Line Interface : Permet de passer des commandes à Asterisk depui l'interface web).
Exemple de script AGI
Ce script, écrit en langage C, permet à Asterisk de dire "un deux trois"
Fichier monscript.agi :
#include <stdio.h> main(){ char line[80]; /* use line buffering */ setinebuf(stdout); setinebuf(stderr); /* read and ignore AGI environment */ while(1){ fgets(line,80,stdin); if(strlen(line) <= 1) break; } /* Send asterisk a command */ printf("SAY DIGITS 123 \"\"\n"); /* Read response from Asterisk and show on console */ fgets(line,80,stdin); fputs(line,stderr); }Source : http://www.bitflipper.ca/Documentation/agi.html
Exemple de script avancé
Ce script a pour but de montrer les fonctionnalités avancées d'un script AGI.
Celui ci est rédigé en PHP. Il permet, lors d'un appel, de décrocher puis de se connecter à une base de données MYSQL et regarde si le numéro de téléphone d'origine est déjà dans la base de données. Si oui, le script authentifie l'utilisateur.
Base de données MYSQL : database name - cdrdb ( user - admin, login - admincdr ) table name - accounts ( two fields, pin_no and clid)
Script ani.agi : #!/usr/local/bin/php -q <?php ob_implicit_flush(true); set_time_limit(6); $in = fopen("php://stdin","r"); $stdlog = fopen("/var/log/asterisk/my_agi.log", "w"); // Do function definitions before we start the main loop function read() { global $in, $debug; $input = str_replace("\n", "", fgets($in, 4096)); return $input; } function errlog($line) { global $err; echo "VERBOSE \"$line\"\n"; } function write($line) { global $debug; echo $line."\n"; } // parse agi headers into array while ($env=read()) { $env = str_replace("\"","",$env); $s = split(": ",$env); $agi[str_replace("agi_","",$s[0])] = trim($s[1]); if $env == "") { break; } } function connect_db() { $db_connection = mysql_connect ('localhost', 'admin', 'admincdr') or die (mysql_error()); $db_select = mysql_select_db('cdrdb') or die (mysql_error()); } // main program $cli = $agi[callerid]; $exten= $agi[extension]; //errlog("Call from ".$agicallerid." - Calling phone"); connect_db(); $query1 = "SELECT pin_no FROM accounts WHERE clid = '$cli' "; $query_result1 = @mysql_query($query1); $row_count = mysql_num_rows($query_result1); $row1 = @mysql_fetch_array ($query_result1); If ($row_count !=0 ) { // caller is authenticated based on ANI $pin = $row1[pin_no]; write ("SET CONTEXT did"); write ("EXEC SETACCOUNT $pin"); write ("EXEC GoTO s|2"); // ask for the number to call, no authentication } Else { // clid does not exist so ask for PIN write ("SET CONTEXT did"); write ("EXEC GoTO 866XXXXXXX|7"); } // clean up file handlers etc. fclose($in); fclose($stdlog); exit; ?>
Fichier Extensions.conf : [did] ;for did callers exten => 866XXXXXXX,1,Ringing exten => 866XXXXXXX,2,Wait,4 exten => 866XXXXXXX,3,Answer exten => 866XXXXXXX,4,SetCIDname() exten => 866XXXXXXX,5,agi,ani.agi exten => 866XXXXXXX,6,Authenticate(/etc/asterisk/authenticate.txt|a) exten => 866XXXXXXX,7,GoTO(s|2) exten => s,2,BackGround(pls-entr-num-uwish2-call) exten => s,3,DigitTimeout,5 exten => s,4,ResponseTimeout,10 exten => _011XXXXXXXX.,1,Playback(pls-wait-connect-call) exten => _011XXXXXXXX.,2,AbsoluteTimeout(3600) exten => _011XXXXXXXX.,3,ResetCDR(w) exten => _011XXXXXXXX.,4,Dial(H323/${EXTEN}@a.b.c.d,90) exten => _011XXXXXXXX.,5,NoCDR() ; if no answer exten => _011XXXXXXXX.,6,Busy exten => _011XXXXXXXX.,105,NoCDR() ; if line is busy exten => _011XXXXXXXX.,106,Busy exten => t,1,Playback(vm-goodbye) ; if timeout exten => t,2,NoCDR() exten => t,3,Hangup exten => i,1,Playback,invalid ; if any number other than 011..... exten => i,2,Goto,s|3 ;exten => h,1,Hangup
Pour plus d'exemples, nous vous recommendons la lecture du chapitre AGI de ce document (license Creative Commons CC-by-nc-nd).