Hola a todos.
Le comparto a toda la comunidad un script en php/AGI que junto con el dialplan permite distribuir el consumo de llamadas entre tus lineas de forma uniforme.
Problema:
Supongamos que tenemos 5 lineas en México y cada linea tiene un número de 100 llamadas libres. A partir de la llamada 101 se genera un costo por parte del carrier. Dependiendo de la configuración, por lo general asterisk utiliza los primeros canales disponibles del grupo, por lo que el mayor porcentaje de llamadas generadas saldrian por estas lineas, dejando a las ultimas casi sin consumo. Si bien asterisk tiene la opción de usar r1 o R1 (suponiendo que tenemos el grupo 1), su distribución es aleatoria y no uniforme entre las lineas que forman parte del grupo.
El script trabaja de la siguiente forma:
Declaro las lineas en la base de datos de asterisk
lineas 1 0
lineas 2 0
lineas 3 0
lineas 4 0
lineas 5 0
Donde lineas es el nombre de la familia 1,2,3,4 y5 son las llaves (o como id y representa el numero de linea) y 0 es su respectivo contador.
El algoritmo lee la base y mete la info en un array, lo ordena, posteriormente se crea otro array para tener las lineas ordenadas de acuerdo al contador. Una vez teniendo en orden las lineas el script checa si la linea con el contador mas bajo esta disponible, en caso de que lo este genera la llamada, en caso de que no este disponible regresa a consultar al array y obtiene la linea con el siguiente contador mas bajo y de nuevo checa si la linea esta disponible, si lo esta genera la llamada, si no regresa al arreglo, y asi sucesivamente.
Una vez que se genero la llamada se obtiene el estatus de la misma, si el estatus es Answered se incrementa el contador correspondiente, si es cualquier otro estatus no se incrementa.
Nota: en el caso de lineas analogicas el carrier debe de generar inversión de polaridad para que el estatus sea confiable, de lo contrario en cuanto empiece a generar todo asterisk lo toma como Answered, en ese caso yo utilizo una medida de tiempo para tratar de discriminar que llamadas fueron realmente exitosas.
En caso de llamadas internas no se incrementa el contador, al tener la linea vacia, se captura ese valor de la variable.
Primero en el dialplan se hace el llamado al AGI:
exten => _9XXXXXXXX,1,NoOp(${EXTEN})
exten => _9XXXXXXXX,n,AGI(rotacion.php,${EXTEN:1}) //En este caso usamos un prefijo 9 seguido de cualquier numero conformado por 8 digitos,
mandamos a ejecutar el AGI rotacion.php pasando como argumento el numero de telefono marcado sin el prefijo de marcación
exten => _9XXXXXXXX,n,HangUp() //Simplemente colgamos una vez realizada la llamada
El codigo del AGI:
#!/usr/bin/php
<?php
require('phpagi/phpagi.php');
array_shift($argv);
$numberGet=$argv[0]; //Se guarda el argumento (numero telefonico) en la variable $numberGet
$agi= new AGI();
$arrayLineas= array();//Se crea un arreglo que guardara la linea con su respectivo contador de llamadas
for($i=0;$i<4;$i++)
{
$temporal=$agi->database_get("lineas",$i+1);
$arrayLineas[$i]=$temporal["data"];
}
foreach ($arrayLineas as $key => $val) { //Para proposito de depuración y poder ver lo que se guarda en el arreglo
//$agi->NoOp($key."---".$val);
$agi->exec("Noop",$key."--->".$val);
}
asort($arrayLineas); //Ordenamos el arreglo de forma descendente, asi queda el valor mas bajo en la primera posición
$agi->exec("Noop","despues de ordenar---------");
foreach ($arrayLineas as $key => $val) { //Para fines de depuración
// $agi->NoOp($key."---".$val);
$agi->exec("Noop",$key."--->".$val);
}
$agi->exec("Noop","Lista de los indices---------");
$arrayLlamadas=array();
foreach ($arrayLineas as $key => $val) { //Creamos otro arreglo que guardara solo los indices del anterior, los cuales representan el numero
$arrayLlamadas[]=$key; // de linea a usar
}
foreach ($arrayLlamadas as $key => $val) {
$agi->exec("Noop",$key."--->".$val); //Para fines de depuración
$agi->NoOp($key."---".$val);
}
$contador=0;
$troncal=0;
$aux=0;
do{
$troncal=$arrayLlamadas[$contador]+1;
$agi->exec("ChanIsAvail DAHDI/".$troncal); //Esta parte es la interesante, una vez que tenemos el arreglo con las lineas y queremos
$result = $agi->get_variable('AVAILCHAN'); //hacer bien las cosas, debemos checar si el canal esta disponible para poder sacar la
$agi->exec("NoOp","availchan:".$result["data"]); //llamada, en un sitio de alto trafico de llamadas, muchas veces se caera en la situación
$contador++; //en que el canal con menor numero de llamadas este ocupado y tendremos que usar el inmediato
}while($result["data"]==""); //menor, es decir, siguiendo la logica de usar siempre los canales con menor numero
//$agi->NoOp("Numero de troncal:".$troncal); // de llamadas
//$agi->NoOp("Resultado final:".$result["data"]);
$aux=$agi->database_get("lineas",$troncal);
//$agi->NoOp("valor de aux:".$aux["result"]);
$agi->exec("Set","troncal=".(string)$troncal); //Una vez que se ha encontrado el canal viable para sacar la llamada se establecen variables
$agi->exec("Set","cantidad=".$aux["data"]); //que se utilizaran en el dialplan, las variables son:
$salida=$agi->exec_dial("DAHDI",(string)$troncal."/".(string)$numberGet); //troncal(canal usado), cantidad(contador de llamadas de ese canal)
//Se manda a ejecutar la llamada usando como argumento $troncal y $numberGet (la //variable que guarda el numero de telefono inicial, hasta aqui llega el agi
?>
Y por ultimo en la extención h (hangup) ponemos las acciones que se deben de ejecutar a finalizar la llamada:
exten => h,1,NoOp("troncal "${troncal}) //Para fines de depuración
exten => h,n,NoOp(${DIALSTATUS}) //Para fines de depuración
exten => h,n,NoOp(${DIALEDTIME}) //Para fines de depuración
exten => h,n,NoOp(cantidad:${cantidad}) //Para fines de depuración
exten => h,n,Gotoif($["${troncal}" = ""]?internal,h,colgar) //llamadas entre extensiones, troncal estara vacia
exten => h,n,Gotoif($["${DIALSTATUS}" != "ANSWER"]?internal,h,colgar)
exten => h,n,Gotoif($[${DIALEDTIME} < 17]?internal,h,colgar) //Se revisa el tiempo que lleva la llamada (esto para lineas analogicas)
exten => h,n(suma),Set(DB(lineas/${troncal})=${MATH(${cantidad}+1)})
exten => h,n(colgar),HangUp()
Este script no es perfecto pero me ha servido bastante bien, sientanse libres de mejorarlo, lo que pediria es citar la fuente original osea su servidor y compartir la mejora con la comunidad.
Les dejo una captura de los contadores trabajando despues de unas semanas.
Creditos:
David Castañón Lara
Licencia tipo: Invitame una chela o si lo necesitas dame chamba (también hago freelance), o en su defecto dame tu opinión.
preack@hotmail.com
@the_preack
Le comparto a toda la comunidad un script en php/AGI que junto con el dialplan permite distribuir el consumo de llamadas entre tus lineas de forma uniforme.
Problema:
Supongamos que tenemos 5 lineas en México y cada linea tiene un número de 100 llamadas libres. A partir de la llamada 101 se genera un costo por parte del carrier. Dependiendo de la configuración, por lo general asterisk utiliza los primeros canales disponibles del grupo, por lo que el mayor porcentaje de llamadas generadas saldrian por estas lineas, dejando a las ultimas casi sin consumo. Si bien asterisk tiene la opción de usar r1 o R1 (suponiendo que tenemos el grupo 1), su distribución es aleatoria y no uniforme entre las lineas que forman parte del grupo.
El script trabaja de la siguiente forma:
Declaro las lineas en la base de datos de asterisk
lineas 1 0
lineas 2 0
lineas 3 0
lineas 4 0
lineas 5 0
Donde lineas es el nombre de la familia 1,2,3,4 y5 son las llaves (o como id y representa el numero de linea) y 0 es su respectivo contador.
El algoritmo lee la base y mete la info en un array, lo ordena, posteriormente se crea otro array para tener las lineas ordenadas de acuerdo al contador. Una vez teniendo en orden las lineas el script checa si la linea con el contador mas bajo esta disponible, en caso de que lo este genera la llamada, en caso de que no este disponible regresa a consultar al array y obtiene la linea con el siguiente contador mas bajo y de nuevo checa si la linea esta disponible, si lo esta genera la llamada, si no regresa al arreglo, y asi sucesivamente.
Una vez que se genero la llamada se obtiene el estatus de la misma, si el estatus es Answered se incrementa el contador correspondiente, si es cualquier otro estatus no se incrementa.
Nota: en el caso de lineas analogicas el carrier debe de generar inversión de polaridad para que el estatus sea confiable, de lo contrario en cuanto empiece a generar todo asterisk lo toma como Answered, en ese caso yo utilizo una medida de tiempo para tratar de discriminar que llamadas fueron realmente exitosas.
En caso de llamadas internas no se incrementa el contador, al tener la linea vacia, se captura ese valor de la variable.
Primero en el dialplan se hace el llamado al AGI:
exten => _9XXXXXXXX,1,NoOp(${EXTEN})
exten => _9XXXXXXXX,n,AGI(rotacion.php,${EXTEN:1}) //En este caso usamos un prefijo 9 seguido de cualquier numero conformado por 8 digitos,
mandamos a ejecutar el AGI rotacion.php pasando como argumento el numero de telefono marcado sin el prefijo de marcación
exten => _9XXXXXXXX,n,HangUp() //Simplemente colgamos una vez realizada la llamada
El codigo del AGI:
#!/usr/bin/php
<?php
require('phpagi/phpagi.php');
array_shift($argv);
$numberGet=$argv[0]; //Se guarda el argumento (numero telefonico) en la variable $numberGet
$agi= new AGI();
$arrayLineas= array();//Se crea un arreglo que guardara la linea con su respectivo contador de llamadas
for($i=0;$i<4;$i++)
{
$temporal=$agi->database_get("lineas",$i+1);
$arrayLineas[$i]=$temporal["data"];
}
foreach ($arrayLineas as $key => $val) { //Para proposito de depuración y poder ver lo que se guarda en el arreglo
//$agi->NoOp($key."---".$val);
$agi->exec("Noop",$key."--->".$val);
}
asort($arrayLineas); //Ordenamos el arreglo de forma descendente, asi queda el valor mas bajo en la primera posición
$agi->exec("Noop","despues de ordenar---------");
foreach ($arrayLineas as $key => $val) { //Para fines de depuración
// $agi->NoOp($key."---".$val);
$agi->exec("Noop",$key."--->".$val);
}
$agi->exec("Noop","Lista de los indices---------");
$arrayLlamadas=array();
foreach ($arrayLineas as $key => $val) { //Creamos otro arreglo que guardara solo los indices del anterior, los cuales representan el numero
$arrayLlamadas[]=$key; // de linea a usar
}
foreach ($arrayLlamadas as $key => $val) {
$agi->exec("Noop",$key."--->".$val); //Para fines de depuración
$agi->NoOp($key."---".$val);
}
$contador=0;
$troncal=0;
$aux=0;
do{
$troncal=$arrayLlamadas[$contador]+1;
$agi->exec("ChanIsAvail DAHDI/".$troncal); //Esta parte es la interesante, una vez que tenemos el arreglo con las lineas y queremos
$result = $agi->get_variable('AVAILCHAN'); //hacer bien las cosas, debemos checar si el canal esta disponible para poder sacar la
$agi->exec("NoOp","availchan:".$result["data"]); //llamada, en un sitio de alto trafico de llamadas, muchas veces se caera en la situación
$contador++; //en que el canal con menor numero de llamadas este ocupado y tendremos que usar el inmediato
}while($result["data"]==""); //menor, es decir, siguiendo la logica de usar siempre los canales con menor numero
//$agi->NoOp("Numero de troncal:".$troncal); // de llamadas
//$agi->NoOp("Resultado final:".$result["data"]);
$aux=$agi->database_get("lineas",$troncal);
//$agi->NoOp("valor de aux:".$aux["result"]);
$agi->exec("Set","troncal=".(string)$troncal); //Una vez que se ha encontrado el canal viable para sacar la llamada se establecen variables
$agi->exec("Set","cantidad=".$aux["data"]); //que se utilizaran en el dialplan, las variables son:
$salida=$agi->exec_dial("DAHDI",(string)$troncal."/".(string)$numberGet); //troncal(canal usado), cantidad(contador de llamadas de ese canal)
//Se manda a ejecutar la llamada usando como argumento $troncal y $numberGet (la //variable que guarda el numero de telefono inicial, hasta aqui llega el agi
?>
Y por ultimo en la extención h (hangup) ponemos las acciones que se deben de ejecutar a finalizar la llamada:
exten => h,1,NoOp("troncal "${troncal}) //Para fines de depuración
exten => h,n,NoOp(${DIALSTATUS}) //Para fines de depuración
exten => h,n,NoOp(${DIALEDTIME}) //Para fines de depuración
exten => h,n,NoOp(cantidad:${cantidad}) //Para fines de depuración
exten => h,n,Gotoif($["${troncal}" = ""]?internal,h,colgar) //llamadas entre extensiones, troncal estara vacia
exten => h,n,Gotoif($["${DIALSTATUS}" != "ANSWER"]?internal,h,colgar)
exten => h,n,Gotoif($[${DIALEDTIME} < 17]?internal,h,colgar) //Se revisa el tiempo que lleva la llamada (esto para lineas analogicas)
exten => h,n(suma),Set(DB(lineas/${troncal})=${MATH(${cantidad}+1)})
exten => h,n(colgar),HangUp()
Este script no es perfecto pero me ha servido bastante bien, sientanse libres de mejorarlo, lo que pediria es citar la fuente original osea su servidor y compartir la mejora con la comunidad.
Les dejo una captura de los contadores trabajando despues de unas semanas.
Creditos:
David Castañón Lara
Licencia tipo: Invitame una chela o si lo necesitas dame chamba (también hago freelance), o en su defecto dame tu opinión.
preack@hotmail.com
@the_preack
Comentario