PABX da Simples IP
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

448 lines
15 KiB

#!/usr/bin/php -Cq
<?php
/* * ***************************************************************************************
*
* RETORNO AUTOMÁTICO PARA CHAMADAS ABANDONADAS
* Autor
* Amarildo Pereira
*
* ***************************************************************************************
* Definições do Projeto
* Nome: Retorno de Abandonada
* Data início: 02/08/2018
* Equipe: Amarildo Pereira
* Descrição:
* O objetivo do script é processar e retornar chamadas que foram abandonadas na fila
* de atendimento de maneira automática, as mesmas são gravadas pela central telefônica
* quando ocorre o abandono nas tabelas: pbx_abandonadas_semretorno e
* pbx_abandonadas_status, a primeira traz informações sobre a chamada e a operação
* no momento do abandono e a segunda informações detalhadas sobre cada agente logado. O
* processamento das chamadas será realizada sob as seguintes regras:
* 1 - A opção retornar abandonadas deve estar marcada como sim na fila de atendimento.
* 2 - O campo "abdsr_data_hora_retorno" da tabela "pbx_abandonadas_semretorno" deve
* ser null
* 3 - O campo "abdsr_callback" da tabela "pbx_abandonadas_semretorno" deve estar com
* valor 0(zero) quando uma tentativa de retorno é feito esta campo é atualizado para 1
* mesmo que esta chamada não tenha sido completada com sucesso.
* 4 - O número de chamadas na espera deve ser 0.
* 5 - A opção disponível para discador deve estar desmarcada no agente.
* 6 - A opção indísponivel para fila deve estar desmarcada no agente.
* 7 - O status dos agentes devem estar como "LIVRE".
* 8 - Serão processadas chamadas com referência na configuração do sistema
* "Abandonadas Sem Retorno" onde é indicado o número de dias que o processamento
* será feito, se indicado 0(zero) por exemplo serão processadas chamadas apenas do
* dia atual, se 1(um) for indicado serão processadas chamadas d-1 e assim sucessiva-
* mente.
* **************************************************************************************
* Copyright (c) 2018, Simples IP
* ************************************************************************************* */
/*
* Habilita debug para linha de comanado.
*/
$debugLocal = 0;
$serverDebug = "127.0.0.1";
error_reporting($debugLocal ? E_ALL : E_ERROR);
ini_set('display_errors', $debugLocal);
require("util/constantes.php");
include 'util/util.php';
include 'funcoes/shared.php';
include("util/funcoesAmi.php");
/*
* Define o numero de tentativas que o serviço ira tentar retornar a ligação em
* caso de falha na discagem.
*/
define("RTABD_TENTATIVAS_RETORNO", 3);
/*
* Para o loop do serviço para um teste local.
*/
define("MAX_CLICLO_DEBUG", 3);
/*
* Tempo entre uma tentativa de retorno e outra
*/
define("RTABD_TEMPO_RETORNO", 180);
/*
* Ativa Debug.
*/
$debugAmi = 0;
/*
* Habilita o lancamento de erros nas funcoes ami.
*/
$errorMode = true;
/*
* Para rodar como um processo normal é preciso passar "N" como primeiro parametro.
*/
$notDaemon = isset($argv[1]) && ( strtoupper($argv[1]) === 'N');
/*
* Mostra o help quando passodo --help no primeiro argumento.
*/
GetRadHelp();
/*
* Inicializa o daemom quando N não for informado.
*/
if (!$debugLocal) {
GetDaemon($notDaemon);
}
/*
* Caminho para o arquivo de log do script.
*/
$pathLog = sprintf('/var/log/asterisk/rad.log', date("Y-m-d"));
/*
* Configuração do banco de dados. Deixe a variável $str em branco para pegar o valor padrao
*/
if ($debugLocal) {
$str = sprintf("host='%s' port='%s' dbname='%s' user='%s' password='%s'", $serverDebug, "5432", "pbx", "contacte", "ctepgSQL");
} else {
$str = EchoLog([GetDefStrDb()]);
}
/*
* Realiza a conexao ao banco de daodos.
*/
$conn = pg_connect($str);
/*
* Realiza a conexão com o manager do asterisk.
*/
list($host, $porta, $usuario, $senha) = EchoLog([(!$debugLocal ? GetSckConnect() : array($serverDebug, "5038", "manager", "manager007"))]);
$socket = ConectaAmi($host, $porta, $usuario, $senha);
/*
* Tempo em segundos que a aplicação espera para iniciar outro ciclo, buscando
* chamadas abandonadas sem retorno.
*/
$wait = !$debugLocal ? 10 : 1;
/*
* Ddd padrao da localidade;
*/
$dddPadrao = '';
/*
* Marca o inicio da execucao do script.
*/
GravaLog(sprintf("Inicia Execucao: %s\n", date('Y-m-d H:i:s')), $pathLog);
declare(ticks = 1);
$statusSignal = 0;
if (!$debugLocal) {
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGHUP, "sig_handler");
pcntl_signal(SIGUSR1, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
}
while (true) {
try {
/*
* Checa se a conexão com o banco de dados esta ok.
*/
if (!$conn) {
if (!$conn = pg_connect($str)) {
RaiseExcept("Não foi possível conectar ao banco de dados!");
}
}
/*
* Checa a conexão com o manager esta ok.
*/
if (!$socket) {
if (!$socket = ConectaAmi($host, $porta, $usuario, $senha)) {
RaiseExcept("Não foi possível conectar ao manager!");
}
}
if (!$dddPadrao) {
$dddPadrao = GetDddPadrao();
}
/*
* Verifica o numero de agentes livres.
*/
$agentesLivres = __GetAgentesLivresRad($conn);
if ($agentesLivres) {
/*
* Quantidade de dias que a em que será tratada as abandonadas.
*/
$diasAbandon = GetDiasAbandonada($conn);
/*
* Lista as chamadas abandonadas.
*/
$result = pg_query($conn, __GetQueryRad($agentesLivres, $diasAbandon));
while ($dadosRetorno = pg_fetch_array($result, null, PGSQL_ASSOC)) {
/*
* Pega valores necessarios para discagem.
*/
list($channelAgente, $matricula, $numDisc, $ramal, $uid, $fila, $horaAbd, $tentativa) = array_values($dadosRetorno);
/*
* Log do que esta sendo discado.
*/
GravaLog(sprintf("Dados da Discagem: [Uid: %s Dac: %s Num: %s Mat: %s Hora: %s] %s\n", $uid, $fila, $numDisc, $matricula, $horaAbd, date('Y-m-d H:i:s')), $pathLog);
/*
* Realiza a discagem.
*/
if (DiscarCallAmi($socket, $channelAgente, $matricula, RemoveDddPadrao($numDisc, $dddPadrao), $ramal) == "ok") {
/*
* Marca a chamada com retornada pelo callback.
*/
$query = sprintf("update pbx_abandonadas_semretorno set abdsr_callback = (coalesce(abdsr_callback,0) + 1), abdsr_callback_data = now() where abdsr_uniqueid = '%s'", $uid);
if (!pg_query($conn, $query)) {
/*
* Se a chamada nao for realizada registra o erro e segue com os demais numeros.
*/
GravaLog(sprintf("Erro: %s File: %s Line: %s \ncmd: %s\n", GetLasterror(), 'rad.php', '91', $query), $pathLog);
}
/*
* Sincroniza informacoes com o agente.
*/
SincronizaAgente($conn, $matricula, $numDisc, $horaAbd, ++$tentativa);
} else {
RaiseExcept("Não foi possível realizar a discagem!");
}
}
}
} catch (Exception $ex) {
/*
* Grava erros gerados durante a execucao.
*/
GravaLog(sprintf("Erro:%s File: %s Line: %s \ncmd: %s\n %s\n", $ex->getMessage(), $ex->getFile(), $ex->getLine(), $query, date('Y-m-d H:i:s')), $pathLog);
@pg_close();
$conn = false;
socket_close($socket);
$socket = null;
}
/*
* Verifica se o processo deve ser encerrado
*/
if (sig_status()) {
break;
}
if ($debugLocal) {
if (++$debugLocal == MAX_CLICLO_DEBUG) {
break;
}
}
sleep($wait);
}
/*
* Marca o fim da execucao do script.
*/
GravaLog(sprintf("Finaliza Execucao: %s\n\n", date('Y-m-d H:i:s')), $pathLog);
function __GetAgentesLivresRad($conn) {
$query = "select count(*)
from pbx_supervisor_agentes b, pbx_supervisor_dacs c, pbx_queues_grupos d
where c.dac = b.dac
and d.nome = c.dac
and b.status = 'LIVRE'
and b.status_discador = 0
and b.disponivel_atendimento = 1
and strtoint(c.espera) = 0
and d.retornar_abandonadas = 1";
if (!$result = pg_query($conn, $query)) {
RaiseExcept("Não foi possível consultar agentes livres!");
}
if (!pg_num_rows($result)) {
return 0;
}
$rows = pg_fetch_row($result);
return $rows[0];
}
function __GetQueryRad($agentesLivres, $diasAbandon) {
$tentativasRetorno = RTABD_TENTATIVAS_RETORNO;
$tempoRetorno = RTABD_TEMPO_RETORNO;
return " select case when(upper(b.modo_atendimento) = 'MANUAL')then
case when( upper(e.tipo_ramal) = 'KHOMP')then
upper(e.tipo_ramal) || '/r' || e.nome
else
e.dispositivo end
else
'Local/' || b.ramal || '@app-callcenter/n' end as canal_agente,
b.matricula,
abdsr_numero,
b.ramal,
abdsr_uniqueid,
abdsr_fila,
abdsr_data_hora_inicio,
a.abdsr_callback
from pbx_abandonadas_semretorno a, pbx_supervisor_agentes b, pbx_supervisor_dacs c, pbx_queues_grupos d, pbx_ramais e
where b.dac = a.abdsr_fila
and c.dac = b.dac
and d.nome = c.dac
and e.nome = b.ramal
and a.abdsr_data_hora_retorno is null
and length(a.abdsr_numero) >= 8
and ((a.abdsr_callback < $tentativasRetorno) and (extract(epoch from (now() - coalesce(abdsr_callback_data, now())))::int > $tempoRetorno) or (a.abdsr_callback = 0))
and b.status = 'LIVRE'
and b.status_discador = 0
and b.disponivel_atendimento = 1
and strtoint(c.espera) = 0
and d.retornar_abandonadas = 1
and a.abdsr_data >= (now()::date - $diasAbandon)
order by a.abdsr_data_hora_inicio desc limit $agentesLivres";
GravaLog("cmd:", $pathLog);
// and ((a.abdsr_callback <= $tentativasRetorno) and (extract(epoch from (now() - coalesce(abdsr_callback_data, now())))::int > $tempoRetorno) or (a.abdsr_callback = 0))
}
function SincronizaAgente($conn, $matricula, $numero, $dataAbd, $tentativa) {
global $pathLog;
$dataAbd = str_replace(':', '.', FormataDBDataHora($dataAbd));
$contId = sprintf("%s|%s|%s%s", 0, $numero, 0, "|Retorno ABD:$numero|Data:$dataAbd|Tentativa:$tentativa");
$query = sprintf("update pbx_supervisor_agentes set cont_identificador = %s where matricula = '%s'", QuotedStr($contId), $matricula);
if (!pg_query($conn, $query)) {
/*
* Registra chamada.
*/
GravaLog(sprintf("Erro: %s File: %s Line: %s \ncmd: %s\n", GetLasterror(), 'rad.php', '193', $query), $pathLog);
}
}
function GetDaemon($notDaemon) {
/*
* Se o script não for chamado com daemon sai sem executar nada.
*/
if ($notDaemon) {
return 0;
}
$pid = pcntl_fork();
if ($pid) {
exit(0); //success
}
/* PIDFile SYSTEMD
* SYSTEMD irá monitorar esse pid para verificar se ainda ativo
*/
$PIDFile = fopen( "/var/lock/subsys/rtabd.pid", "w+" );
$pid = getmypid( )
if( $pid == false ){
exit( 1 );
}
fwrite( $PIDFile, $pid);
fclose( $PIDFile );
// posix_setsid();
}
function GetRadHelp() {
global $argc, $argv;
$arg = isset($argv[1]) ? $argv[1] : '';
if ($argc && ((stripos($arg, '--h') !== false) || (stripos($arg, '-h') !== false) || (stripos($arg, '/h') !== false))) {
return displayUsage();
}
}
function displayUsage() {
echo "--------------------------------------------------------------------------------------------------------\n";
echo "- Servico de sincronizacao de dados para retorno de abandonadas -\n";
echo "--------------------------------------------------------------------------------------------------------\n";
echo "- Para rodar como um processo normal passe \"N\" como primeiro argumento:\"./retornoAbandonada.php N\"!-\n";
echo "- Para rodar como Daemon nao passe nenhum argumemento argumento:\"./retornoAbandonada.php \"! -\n";
echo "--------------------------------------------------------------------------------------------------------\n";
echo "\n";
exit(0);
}
function sig_handler($signo) {
global $statusSignal;
//echo "\n This signal is called. [$signo] \n";
$statusSignal = 1;
}
function sig_status() {
global $statusSignal;
pcntl_signal_dispatch();
return $statusSignal;
}
function __GetAgenteDiscagem($conn, $dados) {
$dadosDiscagem = array();
/*
* pega sempre o agente que recebeu uma chamda a mais tempo.
*/
$query = "select distinct case when(upper(b.modo_atendimento) = 'MANUAL')then
case when( upper(e.tipo_ramal) = 'KHOMP')then
upper(e.tipo_ramal) || '/r' || e.nome
else
e.dispositivo end
else
'Agent/' || b.matricula end as canal_agente,
b.matricula, b.ramal, b.duracao
from pbx_abandonadas_semretorno a, pbx_supervisor_agentes b, pbx_supervisor_dacs c, pbx_queues_grupos d, pbx_ramais e
where b.dac = a.abdsr_fila
and c.dac = b.dac
and d.nome = c.dac
and e.nome = b.ramal
and a.abdsr_data_hora_retorno is null
and a.abdsr_callback = 0
and b.status = 'LIVRE'
and b.status_discador = 0
and b.disponivel_atendimento = 1
and strtoint(c.espera) = 0
and d.retornar_abandonadas = 1
and a.abdsr_data >= (now()::date - 1)
order by b.duracao desc limit 1";
$result = pg_query($conn,$query);
if (!$result) {
GeraExcept("Erro ao consultar \"Agente Livre\"!");
}
if (pg_num_rows($result)) {
$row = pg_fetch_array($result, null, PGSQL_ASSOC);
$dadosDiscagem[] = $row['canal_agente'];
$dadosDiscagem[] = $row['matricula'];
$dadosDiscagem[] = $dados['abdsr_numero'];
$dadosDiscagem[] = $row['ramal'];
$dadosDiscagem[] = $dados['abdsr_uniqueid'];
$dadosDiscagem[] = $dados['abdsr_fila'];
$dadosDiscagem[] = $dados['abdsr_data_hora_inicio'];
return $dadosDiscagem;
}
return $dadosDiscagem;
/*
* __GetQueryRad ---> abdsr_numero, abdsr_uniqueid, abdsr_fila, abdsr_data_hora_inicio
$result = pg_query(__GetQueryRad($agentesLivres, $diasAbandon));
while ($rows = pg_fetch_array($result, null, PGSQL_ASSOC)) {
$rows = __GetAgenteDiscagem($conn, $rows);
list($channelAgente, $matricula, $numDisc, $ramal, $uid, $fila, $horaAbd) = array_values($rows);
*/
}