Asterisk 1.8 y las bolsas de minutos - Func_odbc

A menudo me han preguntado como implementar una bolsa de minutos con Asterisk. La bolsa de minutos consiste en asignar un numero determinado de minutos para las llamadas a algunos destinos. En este articulo vamos a explicar como se puede implementar ese tipo de configuración en Asterisk 1.8 utilizando la función func_odbc.

Como sabemos, los registros de las llamadas se pueden guardar en una base de datos. En este caso se utilizará MySQL. Los pre requisitos para que el sistema funcione, es haber compilado Asterisk con el soporte para MySQL y ODBC.

en entra en el cliente:

mysql –u root –pcontraseña

se crea la base de datos:

mysql> create database asteriskcdr;

ahora se crea la tabla para guardar los registros de las llamadas:

mysql> CREATE TABLE cdr (
id bigint(20) NOT NULL auto_increment,
calldate datetime NOT NULL default '0000-00-00 00:00:00',
clid varchar(80) NOT NULL default '',
src varchar(80) NOT NULL default '',
dst varchar(80) NOT NULL default '',
dcontext varchar(80) NOT NULL default '',
channel varchar(80) NOT NULL default '',
dstchannel varchar(80) NOT NULL default '',
lastapp varchar(80) NOT NULL default '',
lastdata varchar(80) NOT NULL default '',
duration int(11) NOT NULL default '0',
billsec int(11) NOT NULL default '0',
disposition varchar(45) NOT NULL default '',
amaflags int(11) NOT NULL default '0',
accountcode varchar(20) NOT NULL default '',
peeraccount varchar(20) NOT NULL default '',
uniqueid varchar(32) NOT NULL default '',
linkedid varchar(80) NOT NULL default '',
userfield varchar(255) NOT NULL default '',
PRIMARY KEY (`id`),
KEY callerid (clid)
);

El campo de la tabla que nos interesa es “billsec” donde se guardará el tiempo de duración, en segundos, desde que la llamada ha sido contestada.

Se crea un usuario que tengas los privilegios para leer/modificar los datos de la base de datos asteriskcdr:

mysql> GRANT ALL PRIVILEGES ON asteriskcdr.* TO 'asterisk'@'localhost' IDENTIFIED BY 'sesamo';

Se actualizan los privilegios y se sale del cliente MySQL:

mysql> flush privileges;

mysql> quit

Ahora se crea una conexión ODBC a la base de datos. Antes se configura ODBC para que pueda conectarse a base de datos MySQL:

nano /etc/odbcinst.ini

Para CentOS 6.3 se pegan estas líneas:

[MySQL]
Description = ODBC para MySQL
Driver = /usr/lib/libmyodbc5.so
Setup = /usr/lib/libodbcmyS.so
FileUsage = 1

Para CentOS 5.8:

[MySQL]
Description = ODBC para MySQL
Driver = /usr/lib/libmyodbc3.so
Setup = /usr/lib/libodbcmyS.so
FileUsage = 1

Se guardan los cambios y se crea la conexión ODBC:

nano /etc/odbc.ini

se copian las siguientes líneas:

[asteriskcdr]
Description = MySQL AsteriskCDR
Driver = MySQL
Database = asteriskcdr
Server = localhost
User = asterisk
Password = sesamo
Port = 3306
Option = 3

Se guardan los cambios y se averigua que la conexión esté funcionando:

isql asteriskcdr asterisk sesamo

+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+

Perfecto. Ya se puede salir del programa:

SQL> quit

El próximo paso es crear un nuevo bloque en el archivo res_odbc.conf

nano /etc/asterisk/res_odbc.conf

al final de archivo, se añade el siguiente bloque:

[cdr]
enabled => yes
dsn => asteriskcdr
username => asterisk
password => sesamo
pre-connect => yes
sanitysql => select 1
idlecheck => 3600
connect_timeout => 10

Se guardan los cambios y se crea la query (consulta) en el archivo func_odbc.conf:

nano /etc/asterisk/func_odbc.conf

al final del archivo se añaden las siguientes líneas:

[CDR]
dsn=cdr
readsql=select sum(billsec) from cdr where calldate like '${SQL_ESC(${ARG1})}%' and src='${SQL_ESC(${ARG2})}' and dst like '00573%'

La función “sum” de MySQL permite sumar todos los valores encontrados en el campo billsec por el mes seleccionado.

La consulta sería: suma todos los segundos de conversación efectuados por la extensión X en el mes Y (normalmente el mes corriente) para las llamadas hacia números que empiecen por 00573 (en este caso los celulares de Colombia).

Una vez creada la consulta hacia la base de datos se puede crear un nuevo bloque en el dialplan para efectuar la consulta y utilizar el valor devuelto para definir el tipo de comportamiento.

Se abre el archivo del plan de marcado:

nano /etc/asterisk/extensions.conf

y en el contesto utilizado para las llamadas salientes a celulares de Colombia, se añade el siguiente bloque:

exten => _00573.,1,Noop
same => n,Set(fecha=${STRFTIME(epoch,,%Y-%m)}%)
same => n,Set(consumo=${ODBC_CDR(${fecha},${CALLERID(num)})})
same => n,Set(minutos=${MATH(${consumo} / 60,int)})
same => n,Noop(${consumo} ${minutos})
same => n,Gotoif($[${minutos} > 300]?acaba)
same => n,Dial(SIP/voztovoice/${EXTEN})
same => n,Hangup
same => n(acaba),Festival(Estimado. No tienes más minutos de llamadas a celulares disponibles. Contacte el administrador)
same => n,Hangup

Este bloque se utilizará solamente para las llamadas a celulares (de Colombia). Asterisk antes de efectuar la llamada consulta la tabla CDR y suma todos los segundos utilizados por la extensión para llamadas a celulares. El valor se divide por 60 y del resultado se saca el numero entero (con la función MATH). Si los minutos utilizados son más de 300 se comunica a la extensión, de lo contrario se efectúa la llamada.

Vota el Articulo: 

Sin votos (todavía)
Evalúa la calidad del articulo

4 comentarios

Segunda Opción

Es muy interesante la opción que nos da Andrea.

En base a la idea expuesta anteriormente me permito publicar un temporizador diario, es decir podemos crear una bolsa de minutos diaria.

Les recuerdo amigos lectores y seguidores de VoztoVoice que es importante dar nuestro aporte a la comunidad asterisk y de software libre.

exten => _005.,1,MYSQL(Connect connid localhost root PASSWORD_MYSQL asteriskcdrdb)

; Primero se asigna a la variable fecha el día actual del servidor en el formato Año-Mes-Día
same => n,Set(fecha=${STRFTIME(epoch,,%Y-%m-%d)})
same => n,NoOp(connid = ${connid})

; Luego se incluye la variable fecha en la consulta mysql, además se convierten los segundos en minutos en este caso las llamadas internacionales es decir las que comiencen por 005 serán las que se limitarán

same => n,MYSQL(Query resultid ${connid} select sum(round((duration)/60)) AS TOTALMIN from cdr where dst like '005%' AND calldate>'${fecha} 00:00:00' AND calldate<'${fecha} 23:59:59')
same => n,NoOp(resultid = ${resultid})
same => n,MYSQL(Fetch fetchid ${resultid} var1)
same => n,NoOp(Variabili = ${fetchid},${var1},)
same => n,MYSQL(Clear ${resultid})
same => n,MYSQL(Disconnect ${connid})
same => n,NoOp(${var})

; Con While evaluamos el total de minutos diarios en este caso que no sean mayores a 10 minutos.

same => n,While($[${var1} < 10])

; Se crea una segunda variable llamada parcial para que reste el total de minutos por el restante o lo ya hablado.

same => n,Set(parcial=$[10 - ${var1}])

; Se multiplica por 60 para tener el valor en segundos

same => n,Set(final=$[${parcial}*60])

: Con TIMEOUT se asigna el valor absoluto de la llamada.

same => n,Set(TIMEOUT(absolute)=${final})
same => n,Dial(DAHDI/g0/${EXTEN},30)
same => n,Hangup
same => n,EndWhile()

; Si no tiene minutos disponibles entonces lanza un mensaje informando que ya se acabaron los minutos

same => n,Playback(custom/temporizador2)

; También dice cuantos tenía asignados

same => n,SayNumber(${var1})

; Y por último se despide

same => n,Playback(custom/temporizador2_1)
same => n,Hangup

Espero les sirva,

Saludos.

Andres Ramirez
Administrador VoIP
Bogotá - Colombia

Excelente articulo

una pregunta y como podria ser semanal y mensual el control de minutos existe algun posibilidad?
o por ejemplo una tabla donde tenga

Minutos asignados = a 5600
Minutos consmidos = a 300
Minutos disponibles = a 5300

entonces en el query seria
minutos asignados > 5600 no permitir llamada

Aportes a la Segunda Opcion

Realmente agradezco al equipo de Voztovoice, he encontrado cosa intesante que me han ayudado a resolver problemas a la hora de realizar implementaciones de Servicios de VoIP basados en Asterisk.

Tanto lo que ha expuesto Andrea y Andrés Ramírez son muy interesantes, a mi ha resultado las dos opciones a la hora de configurar el temporizador de llamadas, mi simple aporte se basa en lo que Andrés público, en el cual he obviado algunas opciones que he notado que no es necesario y bueno esto es más que nada a necesidad que uno tiene.

A diferencia de lo que ha expuesto Andrés este articulo está hecho para dar un temporizador a los anexos o extensiones donde se les asigna una bolsa de 130 minutos por quincena al pasar este saldo se le evitara las salidas al destino que llama en mi caso a todos los celulares de Perú 519XXXXXXXX reproduciéndole un audio de limite de saldo.

extension.conf

[macro-saldo-celulares]
exten => s,1,MYSQL(Connect connid localhost root cesc0ntcdr asteriskcdr)
same => n,Set(anexo=${CALLERID(num)})
same => n,NoOp(connid = ${connid})
;=============================
same => n,MYSQL(Query resultid ${connid} select truncate(sum(billsec)/60,1) from cdr where src=${anexo} and (calldate between '2014-03-16 06:00:00' and '2014-03-31 23:00:00') and substring(dst,1,3)='519' and disposition ='ANSWERED')
same => n,NoOp(resultid = ${resultid})
same => n,MYSQL(Fetch fetchid ${resultid} var1)
same => n,NoOp(Variabili = ${fetchid},${var1})
same => n,MYSQL(Clear ${resultid})
same => n,MYSQL(Disconnect ${connid})
same => n,While($[${var1} same => n,Set(Saldo=$[130 - ${var1}])
same => n,Dial(${ARG1},25,L(180000:30000:30000)) ; La opción L programo el tiempo en minutos para cada llamada que se realizara no más de 3 minutos y que se le avise faltando 30seg.
;=============================
same => n,Hangup()
same => n,EndWhile()
; Si no tiene minutos disponibles entonces lanza un mensaje informando que ya se acabaron los minutos
same => n,Wait(1)
same => n,Playback(limitedesaldo)
same => n,Hangup()

[activa-telefonia]
exten => _519XXXXXXXX,1,Set(FILE_NAME=${CALLERID(num)}-${EXTEN}-${STRFTIME(${EPOCH},,%d-%m-%Y-%H-%M-%S)})
same => n,set(calltime=${STRFTIME(${EPOCH},,%d%m%C%y)})
same => n,system(mkdir /home/monitor/carteras/ACTIVA_TELEFONIA/${mes}/${calltime})
same => n,MixMonitor(/home/monitor/carteras/ACTIVA_TELEFONIA/${mes}/${calltime}/${FILE_NAME}.gsm,b)
same => n,Macro(saldo-celulares,SIP/${EXTEN}@convergia)
same => n,Hangup()

A esto quiero agregar una variable muy interesante que nosotros como administradores de Servidores Asterisk necesitamos para mantener ordenado nuestras grabaciones, generalmente las grabaciones se graban en una carpeta por día y no por una carpeta por mes y dentro de ellas los días, esto se logra con una variable GLOBAL que es ${mes} que yo tengo agregado en dos líneas de mi plan de marcación en el contexto activa-telefonia.

Aquí el condigo fuente para obtener el valor del mes para la variable ${mes}:

extension.conf
[varfecha]
exten => 55555,1,MYSQL(Connect connid localhost root cesc0ntcdr asteriskcdr)
same => n,Set(fecha=${STRFTIME(${EPOCH},,%m)})
same => n,Set(ano=${STRFTIME(${EPOCH},,%Y)})
same => n,NoOp(connid = ${connid})
same => n,MYSQL(Query resultid ${connid} SELECT CASE month(calldate) when 01 then "ENERO" when 02 then "FEBRERO" when 03 then "MARZO" when 04 then "ABRIL" when 05 then "MAYO" when 06 then "JUNIO" when 07 then "JULIO" when 08 then "AGOSTO" when 09 then "SETIEMBRE" when 10 then "OCTUBRE" when 11 then "NOVIEMBRE" when 12 then "DICIEMBRE" END FROM `cdr` WHERE month(calldate)=${fecha} and CALLDATE LIKE "%${ano}%")
same => n,NoOp(resultid = ${resultid})
same => n,MYSQL(Fetch fetchid ${resultid} varmes)
same => n,NoOp(Variabili = ${fetchid},${varmes})
same => n,MYSQL(Clear ${resultid})
same => n,MYSQL(Disconnect ${connid})
same => n,Set(GLOBAL(mes)=${varmes})
same => n,Set(CALLERID(num)=5555555)
same => n,Dial(SIP/${EXTEN}@convergia,10,tTR)
same => n,Hangup()

Está claro que para generar este dato hay que llamar desde cualquier extensión a la extensión 55555, solo para obtener un valor del mes en la variable Set(GLOBAL(mes)=" " , que será global para todos los contextos, los deje a criterio de poner cualquier extensión o hasta que llame directo a un móvil, ya que repito es solo para generar el valor del mes.

La deficiencia de esto es que si se reinicia el Asterisk, se reinicia o apaga el Servidor se pierde el valor de la variable mes, para ello habría que crear un cron o crear un script que se inicie cada vez inicia el Servidor y que realice la llamada automáticamente, en mi cado lo tengo implementado de esta manera pero a veces no se inicia el script que he puesto en /etc/rc.local, hasta ahora ando buscando porque me sucede esto, espero que me den alguna idea.

Aquí mi script:
Lo he guardado con el nombre de iniciarllamada con permisos de ejecución.
> vim iniciarllamada
/usr/sbin/asterisk -r -x "console dial 55555@varfecha" > /dev/null 2>&1

> vim /etc/rc.local
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local
/root/ iniciarllamada

Espero que le sirva este humilde aporte, esta a imaginación vuestra.

Saludos.

-----------------------------------------------------
Victor Enciso
Administrador de Redes & Soluciones VoIP.
Lima - Perú
------------------------------------------------------

Buen Dia,

Buen Dia,

Aunque es un post que lleva mucho tiempo, quisiera solicitar una guia ya que no tengo muchos conocimientos en programacion.

En este momento me funciona perfecto como lo han explicado pero tengo un tema con un proveedor.

Este proveedor factura desde el 16 al 15 del otro mes, es decir, del 16 de Marzo al 15 de Abril, no se como realizar la programacion o adecuarla para que me tarifique de cada 16 a 15 del otro mes.

Agradeceria si me pueden colaborar.

Muchas Gracias

--- John Fredy Perez "FredySnake"

Suscribirse a Comentarios de "Asterisk 1.8 y las bolsas de minutos - Func_odbc" Suscribirse a VozToVoice - Todos los comentarios