forked from SimplesIP/pabx-app
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.
437 lines
14 KiB
437 lines
14 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 |
|
} |
|
|
|
// 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); |
|
*/ |
|
}
|
|
|