Home    | Software    | Articoli    | Tips'n Tricks    | Contattaci    | Supportaci  
Home arrow Articoli arrow Un semplice motore di "scripting" con Asterisk-Java

Un semplice motore di "scripting" con Asterisk-Java


Introduzione
Asterisk, il noto PBX , espone grandi potenzialità di integrazione. Nello specifico, a noi interessa AGI (Asterisk Gateway Inteface), una sorta di API per  interagire con il motore.
Oggi introdurremo l'interfaccia java di AGI: asterisk-java. Utilizzeremo questa semplice libreria per costruire appunto un motore di scripting. Il linguaggio scelto è BeanShell,  ma è possibile sostituirlo con un qualsiasi linguaggio supportato dal jdk Sun (python,ruby,groovy, javascript etc.). 
 

Asterisk-Java e AGI - Un pò di codice

La libreria asterisk-java è di utilizzo immediato; il più semplice uso è mediante il protocollo fastagi, e sebbene esponga anche una Manager API, fastagi sarà sufficiente per i nostri scopi attuali. Per costruire il nostro motore di scripting dobbiamo solo "subclassare" la classe "BaseAgiScript" e reimplementare il metodo "service". Dall'oggetto "AgiRequest" otterremo il parametro ai quali siamo interessati, "script", che non è nient'altro che il nome dello script beanshell che vogliamo eseguire. Passeremo poi una istanza della nostra classe, gli oggetti AgiRequest e AgiChannel all'interprete beanshell, lanceremo uno script globale che definisca alcune funzioni di utilità (vedere sotto) e alla fine lanceremo lo script richiesto. L'approccio è piuttosto flessibile, il nostro motore di scripting non necessita di risiedere sulla stessa macchina del pbx, e possiamo aggiungere/modificare script al volo senza ricompilazioni o ripartenze del motore.


package org.beanizer.bagiserver;


import bsh.EvalError;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.asteriskjava.fastagi.AgiChannel;
import org.asteriskjava.fastagi.AgiException;
import org.asteriskjava.fastagi.AgiRequest;
import org.asteriskjava.fastagi.BaseAgiScript;

import bsh.Interpreter;

public class BAgiServer extends BaseAgiScript{

public BAgiServer() {
}

public void service(AgiRequest request, AgiChannel channel)
throws AgiException {

Interpreter interp=new Interpreter();
try {
String script=((request.getParameter("script")!=null) && !( request.getParameter("script").equals("") ) )
								? request.getParameter("script") : "../scriptlib/default";
interp.source("scriptlib/bagi_import.bsh");
interp.set("bagi",this);
interp.set("request",request);
interp.set("channel",channel);

interp.source("scripts/"+ script +".bsh");

} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
} catch (EvalError ex) {
ex.printStackTrace();
}
}

}

 

Un file  "fastagi-mapping.properties" deve esistere nel "classpath" del motore di scripting e deve contenere qualcosa del tipo:

server.agi = BAgiServer

dove "server.agi"  è il nome dello script chiamato da asterisk, da mapparsi a "BagiServer", il nome della nostra classe.

Abbiamo anche bisogno di aggiungere una chiamata al nostro motore dal "dial plan" di asterisk, per cui in "extensions.conf" aggiungeremo: 


exten => 1200,1,Agi(agi://localhost/server.agi?script=scriptname)

dove "1200" è il numero di interno scelto, "localhost" è il server sul quale risiede il motore di scripting, "server.agi" il nome che abbiamo mappato per il nostro motore e "scriptname" il nome dello script beanshell da lanciare(senza estensione .bsh).

Quando qualcuno tenterà di chiamare l'estensione "1200", lo script specificato verrà  lanciato.

Nel nostro codice sorgente sono indicate 2 directory relative al "classpath" di base di BAgiServer:

  • "scriptlib", che contiene gli script "default"(chiamato se nessuno script viene indicato come parametro), e "bagi_import" ( dove mettiamo funzioni generiche utilizzabili da tutti gli script).
  • "scripts" , contenente tutti gli script definiti dall'utente.

Ovviamente questi percorsi sono arbitrari e possiamo sceglierli differentemente. Ora diamo un'occhiata ai nostri script beanshell.


bagi_import.bsh
 
Questo è uno script d'utilità automaticamente incluso ogni volta che il motore viene chiamato, quindi è qui che andranno tutte le funzioni d'utilizzo generale. Ecco come si presenta:


import
org.asteriskjava.fastagi.AgiChannel;
import org.asteriskjava.fastagi.AgiException;
import org.asteriskjava.fastagi.AgiRequest;
import org.asteriskjava.fastagi.BaseAgiScript;

import org.beanizer.bagiserver.BAgiServer;

BAgiServer bagi;
AgiRequest request;
AgiChannel channel;

say(String string){
java.rmi.server.UID uid=new java.rmi.server.UID();
String command="echo '" + string + "' | text2wave -scale 8 -o "+ "/tmp/"+uid.toString() +".ulaw -otype ulaw -";
channel.exec("System",command );
channel.streamFile("/tmp/" +uid.toString());
channel.exec("System","rm /tmp/"+ uid.toString() +".ulaw" );
}
answer(){
channel.answer();
}
hangup(){
channel.hangup();
}
getDigits(int number){
StringBuffer sb=new StringBuffer();
for(int t=0;t<number;t++){
sb.append(channel.waitForDigit(10000));
}
return sb.toString();
}

wait(int millis){
(new Thread()).sleep(millis);
}

 

Importiamo alcune classi necessarie, istanziamo BAgiServer, AgiRequest e AgiChannel, e definiamo alcune funzioni. Queste fanno esattamente ciò che il loro nome indica; da notare come il più delle volte siano solo involucri(wrapper) di metodi di AgiChannel("answer" e "hangup" ad es.).

"getDigits", fa uso  del metodo "waitForDigit" di AgiChannel per aspettare l'inserimento  da parte dell'utente di una quantità di cifre numerica indicata dal parametro della funzione e restituisce l'intero numero inserito come stringa.

"say", è usato per il  text-to -speech(conversione da testo ad audio) . Invece di usare Festival o Flite, quì ci serviamo di un approccio più leggero. Mediante il metodo "exec" di AgiChannel lanciamo "text2wave" sul pbx(quindi text2wave deve essere installato sul pbx,) e ne salviamo il risultato in un file audio in una  directory tmp con un nome casuale. Poi, mediante il metodo "streamFile" inviamo l'audio all'utente e infine cancelliamo il file audio temporaneo dalla directory tmp.


default.bsh

Questo viene chiamato quando non venga indicato un script da eseguire.


answer
();
wait(1000);
say("No script defined");
wait(1000);
hangup();

                                                             
Come  potete osservare, utilizza le funzioni  definite in bagi_import.bsh. Risponde alla chiamata(telefonica), attende 1 secondo, scandisce una frase, attende un altro secondo e riattacca.


test.bsh

Ora un esempio di script definito dall'utente. Lo script deve risiedere nella directory "scripts", e la chiamata agi da asterisk deve essere qualcosa del tipo:

exten => 1200,1,Agi(agi://localhost/server.agi?script=test)

Ecco il codice:



import org.asteriskjava.fastagi.command.*;
answer();
wait(1000);
say("Please, enter extension code");
String code = getDigits(3);
say("Trying to call extension:");
channel.sayDigits(code);
channel.exec("ChannelRedirect", channel.getName() + "|from-internal|" + code + "|1");
hangup();

 

Sono  certo che possiate apprezzare quanto sia a questo punto semplice scrivere uno script. In questo caso lo script attende un secondo, chiede vocalmente l'inserimento di un numero di interno, legge ad alta voce il numero inserito e tenta di redirigervi la chiamata. Poi riattacca.


Un paio di linee guida per chi voglia sperimentare:
  • La struttura delle directory deve essere::
                        basedir
                                        scriptlib
                                                          bagi_import.bsh
                                                          default.bsh
                                        scripts
                                                            your_scripts_go_here.bsh
                                        lib
                                                            asteriskjava.jar  (il nome dipende dalla versione)
                                                            beanshell.jar        (il nome dipende dalla versione)   
                                                            bagiserver.jar     (è la sola classe org.beanizer.bagiserver.BAgiServer.class in un file jar)
                                                            fastagi-mapping.properties    
  •         da "basedir", includere nel classpath la directory "lib"  e tutti i jar che contiene, e lanciare la classe 'org.asteriskjava.fastagi.DefaultAgiServer'.


Conclusioni

Quello proposto è solo lo scheletro di ciò che potrebbe diventare un completo motore di scripting agi per sviluppatori java (o ruby,python,javascript,groovy) alle prese con progetti di integrazione di Asterisk con piattaforme differenti. E' piuttosto semplice estendere il motore aggiungendo metodi alla classe java o, meglio, aggiungendo funzioni allo script bagi_import.bsh


Hasta la proxima.

 
< Prec.   Pros. >

  Articles RSS feed

Articoli pił recenti
Software pił recenti
   
designed by allmambo.com