banniere

Asterisk et l'interface AGI (Asterisk Gateway Interface)

Interface AGI

asterisk

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.
asterisk

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.
asterisk

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()
Permet de lire le canal son.

  • DeadAGI()
Permet de contrôler les canaux désactivés (hangup/raccroché).

  • FastAGI()
Permet au script d'être utilisé sur un serveur Asterisk distant.
Exemple : exten => 777, 1, AGI(agi://192.168.2.100/monscript.agi)
asterisk

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 :
telephonie sur ip
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
asterisk

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).