martes, 28 de agosto de 2012

Como hacer un Autodialer para Asterisk


Hola, antes de continuar, quiero decirles que he trabajado en una nueva forma de hacer el marcador automático, con intertefaz web, en fin, mucho mejor. Pueden encontrar más información aquí.

En esta entrada les explicaré como escribir un Auto Dialer sencillo en Java. 
Como requerimiento, es necesario estar familiarizado con Asterisk, la forma en que trabaja su dial plan y también conocimiento en el lenguaje de programación Java. 

Con algunas modificaciones, luego podrían hacer alguna de las siguientes cosas:
  • Agendar un cron job que ejecute el programa en algún momento del día, por ejemplo, como llamada de  despertador.
  • Hacer una difusión masiva de un mensaje de audio, hacia una lista de números.
  • Realizar una campaña de marketing, donde se realicen llamadas y se haga preguntas de opción múltiple que se puedan responder marcando un digito.
  • Leer una lista de números desde una base de datos, etc.
Éstas son sólo algunas ideas, pero hay muchas posibilidades.

Los call files de Asterisk
Son archivos que, básicamente, contienen las instrucciones para realizar una llamada. Colocados en el directorio correcto, son detectados por Asterisk y éste ejecuta las instrucciones escritas en el archivo.

Estrategia para abordar el problema
Nuestra estrategia será escribir una aplicación Java que produzca estos archivos en un directorio temporal y luego los copie al directorio /var/spool/asterisk/outgoing/, de forma que Asterisk los pueda procesar. 

Es en realidad algo muy sencillo que luego podemos ir armando con mayor grado de complejidad y opciones según nuestra necesidad.

Sintaxis de un call file para reproducir un mensaje (o ejecutar una aplicación)
Tomado de http://www.voip-info.org, tan sólo lo traduje al español:
Especificar cómo y dónde llamar:
  • Channel: <canal>: Canal a utilizar para la llamada.
  • CallerID: "nombre" <número> Identificador de quien llama, Por favor notar: Puede no funcionar si no se respeta el formato: CallerID: "Un nombre" <1234>
  • MaxRetries: <número> Número de reintentos antes que la llamada falle (sin incluir el intento inicial, por ejemplo 0 es igual a un intento en total de realizar la llamada). El valor por defecto es 0.
  • RetryTime: <número> Segundos entre los reintentos, No insistir tan seguido sobre un teléfono que no está disponible. El valor por defecto es 300 (5 min).
  • WaitTime: <número> Segundos para esperar que la llamada sea contestada. El valor por defecto es 45.
  • Account: Poner el valor de cuenta a utilizar.
Si la llamada es contestada, conectarla aquí:
  • Context: <nombre del contexto> Un contexto en el archivo extensions.conf
  • Extension: <extensión> Una extensión definida en el archivo extensions.conf
  • Priority: <prioridad> La prioridad de la extensión desde donde se iniciará la conexión.
  • Set: Poner una variable para utilizar el la lógica del contexto (eejmplo: file1=/tmp/to ); en Asterisk 1.0.x usamos 'SetVar' en lugar de 'Set'
  • Application: Aplicación de Asterisk a ser ejecutada (se utiliza en lugar de especificar context, extension y priority)
  • Data: Las opciones a ser pasadas a la aplicación.
Nuevo (?) en Asterisk 1.4
  • Set: Puede ahora también escribir a funciones del dialplan como CDR()
  • AlwaysDelete: Yes/No -Si la fecha y tiempo de modificación del archivo es el futuro, el archivo de llamada no será borrado.
  • Archive: Yes/No - Mover hacia el subdirectorio "outgoing_done" con "Status: value", donde value puede ser Completed, Expired o Failed.
 Entonces, tenemos dos opciones, referenciar un contexto/extensión/prioridad que exista en nuestro archivo extensions.conf, o podemos invocar una Aplicación de Asterisk indicando sus parámetros.

Prerequisitos para probar el Demo.
  • Una instalación Ubuntu o CentOS donde se esté ejecutando Asterisk.
  • Si deseamos realizar llamadas hacia la PSTN, debemos tener configurada una troncal que nos permita hacer esto.
  • Un archivo de audio en el directorio /var/lib/asterisk/sounds/ llamado mensaje.gsm con algún mensaje de prueba que se reproducirá para las personas que contesten nuestras llamadas.
  •  Tener JDK instalado (se puede si está instalado con el comando java -version, caso contrario instalar).
  • Creamos el directorio /var/spool/asterisk/tmp/
  • Disponer de credenciales con privilegios en la instalación Ubuntu.
Ahora, creamos un archivo, que llamaremos AutoDialerDemo.java.

Como mencioné anteriormente, debemos tener creado el directorio /var/spool/asterisk/tmp/y el usuario que ejecute la aplicación debe tener permiso de escritura sobre el mismo (debería ser super usuario para descomplicar la explicación).
    //Directorio donde se crearán inicialmente los archivos
    private static final String TMP_DIR_PATH = "/var/spool/asterisk/tmp";
Utilizaremos un archivo que llamaremos prueba.cfg, que debe encontrarse en el mismo directorio que nuestro archivo AutoDialerDemo.java.
      
    //Al ejecutar la aplicación, se debe pasar como parámetro el nombre del archivo de configuración (por ejemplo prueba.cfg)
    generarLlamadas(args[0]);



Un ejemplo del contenido de nuestro archivo prueba.cfg sería:

    app.proveedor=TuProveedor
     app.numero_0=1234UnNumeroComoLoEsperaTuProveedor
     app.numero_1=1234OtroNumeroComoLoEsperaTuProveedor    
     app.numero_2=1159393260807EnMiCaso
     ...
     app.numero_9=1234UltimoNumeroComoLoEsperaTuProveedor


Finalmente, creamos un archivo .call por cada número en la lista, y lo estructuramos con los datos especificados en el archivo prueba.cfg:

           //Asignamos el nombre del archivo
            file = new File(tmpDir, filename + ".call");

            writer = new BufferedWriter(new FileWriter(file));

            String text = "";
          
            //Especificamos el canal, en este caso, la llamada que realizaremos
            text = "Channel: SIP/" + proveedor + "/" + numero;
            writer.write(text);
            writer.newLine();
          
            System.out.println(text);
          
            //Además del intento inicial, realizaremos un reintento si la llamada no es conectada
            text = "MaxRetries: 1";
            writer.write(text);
            writer.newLine();

            //El reintento lo realizaremos 60 segundos después del intento inicial, de ser el caso
            text = "RetryTime: 60";
            writer.write(text);
            writer.newLine();

            //Esperaremos 30 segundos a que nuestra llamada sea contestada
            text = "WaitTime: 30";
            writer.write(text);
            writer.newLine();

            //Una vez contestada, reproduciremos un mensaje
            text = "Application: Playback";
            writer.write(text);
            writer.newLine();
          
            //El mensaje a reproducir será el audio en el archivo mensaje.gsm
            //Recuerda que este archivo debe estar en el directorio /var/lib/asterisk/sounds
          
            text = "Data: mensaje";
            writer.write(text);
            writer.newLine();

Copiamos el archivo generado desde el directorio /var/spool/asterisk/tmp hacia el directorio /var/spool/asterisk/outogoing. Lo hacemos de esta forma para que asterisk no intente procesar un archivo incompleto:

             //Damos permisos al usuario asterisk sobre nuestro archivo
             Runtime.getRuntime().exec("chown asterisk:asterisk /var/spool/asterisk/tmp/" + filename + ".call");
             //Movemos el archivo desde el directorio temporal hacia el directorio donde asterisk lo procesará
             Runtime.getRuntime().exec("mv /var/spool/asterisk/tmp/" + filename + ".call /var/spool/asterisk/outgoing/" + filename + ".call");

        

 Compilamos el archivo java ejecutando el siguiente comando en la terminal:
     $ javac AutoDialerDemo.java

Y obtenemos el archivo AutoDialerDemo.class. Luego, asumiendo que el archivo prueba.cfg se encuentra en el mismo directorio que AutoDialerDemo.class, y que la información en él es correcta, ejecutamos:
     $ java AutoDialerDemo prueba.cfg

Aconsejo tener otra ventana de terminal abierta conectada al CLI de Asterisk, de forma que podamos ver qué sucede tanto en la salida de nuestra aplicación como en los mensajes de Asterisk.

Incluyo el archivo AutoDialerDemo.java.
¡Suerte con estas llamadas autogeneradas! Sólo intenta no hacer tus pruebas en el horario en que otros descansan zzz.

10 comentarios:

  1. Hola amigo gracias por el aporte, existe alguna interfaz Grafica?

    ResponderEliminar
    Respuestas
    1. Hola Bryan, cuando desarrollé esto no lo hice con interfaz gráfica. Te comento que volví a desarrollar una solución para generar llamadas automáticas pero esta vez basado en Asterisk Manage Interface (AMI). Espero publicarla pronto.

      Eliminar
    2. Hola Alex!! espero que bien hermano, esperando con ansias tu publicación sobre AMI

      Eliminar
    3. Hola Alex!! espero que bien hermano, esperando con ansias tu publicación sobre AMI

      Eliminar
  2. Hola Alex,

    ¿Como puedo colgar una llamada saliente creada mediante un call file? Se que poniendo en consola "sip show channels" me salen las llamadas activas pero no se cual es el canal para poder colgarla.

    Un saludo

    ResponderEliminar
    Respuestas
    1. No he revisado el tema de colgar llamadas generadas por archivo. Depende de la aplicación de tus llamadas, es decir, por qué razón deberían de quedar abiertas luego de su propósito? (por ejemplo, reproducir un archivo de audio). Lo más seguro es siempre agregar un "Application: Hangup" al final.
      Te comento que volví a desarrollar una solución para generar llamadas automáticas pero esta vez basado en Asterisk Manage Interface (AMI). Espero publicarla pronto.

      Eliminar
    2. Muchas gracias Alex por tu pronta respuesta. La razón de querer forzar a colgar una llamadas es simplemente por comodidad a la hora de probar aplicaciones, por ejemplo, de encuestas telefónicas automáticas. Ya que si salta un buzon de voz, la llamada se queda pillada hasta que termina el buzon.
      He buscado por google, pero no encuentro la manera de forzar a colgar estas llamadas.

      Muchas gracias!

      Eliminar
    3. Hola como estás, por un desarrollo en el que he estado trabajando me topé con esto de cerrar llamadas si te contesta un buzón de voz. Hay una aplicación en asterisk que se llamada AMD (por answering machine detection). Creo que esto te podría servir para lo que necesitabas hacer. Espero que no sea tarde!

      Eliminar
  3. Tengo una versión mejorada para la generación atuomática de llamadas telefónicas, pueden encontrar mayor información en http://www.iveloper.com/marcador-automatico-de-llamadas-vocero/
    Saludos!

    ResponderEliminar
    Respuestas
    1. Alex, me gustaria revisar su proyecto en mención, cualquier cosa me puede escribir a mi correo yogui801@msn.com

      Eliminar