Un semplice motore di "scripting" con Asterisk-Java

www.beanizer.org
Un semplice motore di "scripting" con Asterisk-Java
IntroduzioneAsterisk, 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
http://www.beanizer.org/site
Realizzata con Joomla!
Generata: 16 March, 2017, 01:24
www.beanizer.org
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){
http://www.beanizer.org/site
Realizzata con Joomla!
Generata: 16 March, 2017, 01:24
www.beanizer.org
(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 chi
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
http://www.beanizer.org/site
Realizzata con Joomla!
Generata: 16 March, 2017, 01:24
www.beanizer.org
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.
http://www.beanizer.org/site
Realizzata con Joomla!
Generata: 16 March, 2017, 01:24