diagnostico: adicionada função (TESTING)

Signed-off-by: Rodrigo Bertoldo de Sousa Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
rodrigo committed 2026-03-30 21:52:13 -03:00
1 parent 7f38d16821
commit 0dbab52aa1
3 files changed
+430 -1

No files matched your search

+360
View File
@@ -226,6 +226,11 @@ class BotHandler {
await this.handleListarServicos(chatId, target); await this.handleListarServicos(chatId, target);
break; break;
case 'diagnosticar':
console.log(`🩺 Executando handleDiagnosticarCliente com target: ${target}`);
await this.handleDiagnosticarCliente(chatId, target);
break;
case 'dados_cliente': case 'dados_cliente':
case 'dados_cadastrais': case 'dados_cadastrais':
console.log(`📋 Executando handleDadosClientePorBusca com target: ${target}`); console.log(`📋 Executando handleDadosClientePorBusca com target: ${target}`);
@@ -871,6 +876,355 @@ Digite /help para ver todos os comandos disponíveis!
} }
} }
/**
* Diagnóstico completo de um cliente: TopSapp + Juniper PPPoE + ONU
*/
async handleDiagnosticarCliente(chatId, query) {
if (!query) {
await this.bot.sendMessage(chatId,
`🩺 *Diagnóstico de Cliente*\n\n` +
`💡 *Como usar:*\n` +
`• "diagnosticar João da Silva"\n` +
`• "diagnosticar 123.456.789-10"\n\n` +
`O diagnóstico verifica: status no TopSapp, sessão PPPoE no concentrador e status da ONU.`,
{ parse_mode: 'Markdown' }
);
return;
}
try {
await this.bot.sendMessage(chatId, '🔍 Buscando cliente...');
let location = '';
let searchQuery = query;
if (/guarai|colmeia/i.test(query)) {
location = 'Guarai/Colmeia';
searchQuery = query.replace(/(guarai|colmeia)/gi, '').trim();
} else if (/pedro afonso/i.test(query)) {
location = 'Pedro Afonso';
searchQuery = query.replace(/pedro afonso/gi, '').trim();
}
const resultado = await this.topsapp.buscarCliente(searchQuery, location);
if (!resultado.success) {
await this.bot.sendMessage(chatId, `❌ Erro na busca: ${resultado.error}`);
return;
}
if (!resultado.clientes || resultado.clientes.length === 0) {
await this.bot.sendMessage(chatId, `🔍 Nenhum cliente encontrado para: "${searchQuery}"`);
return;
}
const instancia = resultado.instancia || location;
if (resultado.clientes.length === 1) {
await this._mostrarServicosParaDiagnostico(chatId, resultado.clientes[0].id, instancia);
return;
}
// Múltiplos clientes → exibir teclado de seleção
let message = `🔍 *${resultado.clientes.length} clientes encontrados*\n\n`;
const keyboard = [];
resultado.clientes.slice(0, 8).forEach((cliente, index) => {
message += `*${index + 1}.* ${cliente.nome}`;
if (cliente.cpf) message += ` — CPF: \`${cliente.cpf}\``;
message += `\n`;
keyboard.push([{
text: `🩺 Diagnosticar: ${cliente.nome.substring(0, 30)}`,
callback_data: `diag_cliente_${cliente.id}_${instancia}`
}]);
});
await this.bot.sendMessage(chatId, message, {
parse_mode: 'Markdown',
reply_markup: { inline_keyboard: keyboard }
});
} catch (error) {
console.error('❌ Erro em handleDiagnosticarCliente:', error);
await this.bot.sendMessage(chatId, `❌ Erro: ${error.message}`);
}
}
async _mostrarServicosParaDiagnostico(chatId, idCliente, location) {
const servicosResult = await this.topsapp.obterServicosCliente(idCliente, location);
if (!servicosResult.success) {
await this.bot.sendMessage(chatId, `❌ Erro ao buscar serviços: ${servicosResult.error}`);
return;
}
const servicos = (servicosResult.servicos || []).filter(s => {
const st = (s.id_status || s.statusNumerico);
return st === 1 || st === '1' || String(s.status).toLowerCase() === 'liberado' || String(s.status).toLowerCase() === 'ativo';
});
if (servicos.length === 0) {
await this.bot.sendMessage(chatId, `⚠️ Cliente sem serviços ativos para diagnosticar.`);
return;
}
if (servicos.length === 1) {
await this.runDiagnosticoServico(chatId, idCliente, servicos[0].id_servico || servicos[0].id, location, servicos[0]);
return;
}
let message = `🩺 *Selecione o serviço para diagnosticar:*\n\n`;
const keyboard = [];
servicos.forEach((s, i) => {
const nome = s.descricao || s.nome || `Serviço ${i + 1}`;
const login = s.usuario || s.pppoe_login || s.login || '';
message += `*${i + 1}.* ${nome}${login ? ` — \`${login}\`` : ''}\n`;
keyboard.push([{
text: `🔍 ${nome.substring(0, 35)}`,
callback_data: `diag_servico_${idCliente}_${s.id_servico || s.id}_${location}`
}]);
});
await this.bot.sendMessage(chatId, message, {
parse_mode: 'Markdown',
reply_markup: { inline_keyboard: keyboard }
});
}
async handleDiagClienteCallback(callbackQuery, data) {
const chatId = callbackQuery.message.chat.id;
const parts = data.replace('diag_cliente_', '').split('_');
const idCliente = parts[0];
const location = parts.slice(1).join('_');
await this.bot.answerCallbackQuery(callbackQuery.id);
await this._mostrarServicosParaDiagnostico(chatId, idCliente, location);
}
async handleDiagServicoCallback(callbackQuery, data) {
const chatId = callbackQuery.message.chat.id;
const parts = data.replace('diag_servico_', '').split('_');
const idCliente = parts[0];
const idServico = parts[1];
const location = parts.slice(2).join('_');
await this.bot.answerCallbackQuery(callbackQuery.id);
await this.runDiagnosticoServico(chatId, idCliente, idServico, location, null);
}
async runDiagnosticoServico(chatId, idCliente, idServico, location, servicoPreCarregado) {
try {
await this.bot.sendMessage(chatId, '⏳ Executando diagnóstico completo...');
// Buscar serviços caso não tenhamos o objeto do serviço
let servico = servicoPreCarregado;
if (!servico) {
const servicosResult = await this.topsapp.obterServicosCliente(idCliente, location);
if (!servicosResult.success) {
await this.bot.sendMessage(chatId, `❌ Erro ao buscar serviços: ${servicosResult.error}`);
return;
}
const idNum = parseInt(idServico, 10);
servico = (servicosResult.servicos || []).find(s =>
(s.id_servico || s.id) === idNum || String(s.id_servico || s.id) === String(idServico)
);
}
if (!servico) {
await this.bot.sendMessage(chatId, `❌ Serviço #${idServico} não encontrado.`);
return;
}
const pppoeLogin = servico.usuario || servico.pppoe_login || servico.login || null;
const serial = servico.serial || servico.comodato_serial || servico.mac || null;
const statusServico = this.topsapp.getStatusServicoDetalhado
? this.topsapp.getStatusServicoDetalhado(servico.id_status)
: (servico.status || 'N/A');
const nomePlano = servico.descricao || servico.nome || 'N/A';
const endereco = servico.instalacao_endereco || servico.end_rua || null;
const cidade = servico.instalacao_cidade || null;
// Executar consultas em paralelo: Juniper + ONU
const [juniperResult, onuResult] = await Promise.all([
pppoeLogin
? this.ssh.getJuniperSubscriberInfo('juniper', pppoeLogin).catch(e => ({ success: false, error: e.message }))
: Promise.resolve({ success: false, skipped: true }),
serial
? this._diagnosticarONU(serial).catch(e => ({ success: false, error: e.message }))
: Promise.resolve({ success: false, skipped: true })
]);
// Montar relatório
let msg = `🩺 *Diagnóstico do Cliente*\n`;
msg += `━━━━━━━━━━━━━━━━━━━━━\n\n`;
// Bloco 1: TopSapp
msg += `📋 *Serviço TopSapp*\n`;
msg += ` 📦 Plano: ${nomePlano}\n`;
msg += ` 🔌 Status: ${statusServico}\n`;
if (pppoeLogin) msg += ` 👤 Login PPPoE: \`${pppoeLogin}\`\n`;
if (endereco) msg += ` 📍 Endereço: ${endereco}${cidade ? `, ${cidade}` : ''}\n`;
msg += '\n';
// Bloco 2: Concentrador Juniper
msg += `🌐 *Concentrador Juniper*\n`;
if (juniperResult.skipped) {
msg += ` ⚫ Login PPPoE não informado no cadastro\n`;
} else if (!juniperResult.success) {
msg += ` ⚠️ Erro ao consultar: ${juniperResult.error || 'falha na consulta'}\n`;
} else if (juniperResult.connected) {
msg += ` 🟢 *Conectado*\n`;
msg += ` 🌐 IP: \`${juniperResult.ip || 'N/A'}\`\n`;
msg += ` 🔌 Interface: \`${juniperResult.interface || 'N/A'}\`\n`;
} else {
msg += ` 🔴 *Desconectado* (sem sessão PPPoE ativa)\n`;
}
msg += '\n';
// Bloco 3: ONU
msg += `📡 *ONU / ONT*\n`;
if (onuResult.skipped) {
msg += ` ⚫ Serial não informado no cadastro\n`;
} else if (!onuResult.success) {
msg += ` ❌ ${onuResult.error || 'ONU não encontrada'}\n`;
if (serial) msg += ` 📋 Serial buscado: \`${serial}\`\n`;
} else {
const rxLabel = onuResult.rxPower || 'N/A';
const rxNum = parseFloat(rxLabel);
const rxEmoji = isNaN(rxNum) ? '⚫' : rxNum >= -27 ? '🟢' : rxNum >= -30 ? '🟡' : '🔴';
msg += ` 📋 Serial: \`${serial}\`\n`;
msg += ` 🏢 OLT: ${onuResult.oltName || onuResult.oltType || 'N/A'}\n`;
if (onuResult.slot || onuResult.pon) {
msg += ` 📍 Slot/PON: ${onuResult.slot || '?'}/${onuResult.pon || '?'}\n`;
}
msg += ` 🔌 Estado: ${onuResult.online ? '🟢 Online' : '🔴 Offline'}\n`;
msg += ` ${rxEmoji} Rx Power: *${rxLabel}*`;
if (onuResult.type === 'fiberhome') msg += ` _(compensado)_`;
msg += '\n';
if (onuResult.portaEthernet) {
const ethOk = /forward|up|1gbit|100mbit/i.test(onuResult.portaEthernet);
msg += ` ${ethOk ? '🟢' : '🔴'} Porta Ethernet: ${onuResult.portaEthernet}\n`;
}
}
await this.bot.sendMessage(chatId, msg, { parse_mode: 'Markdown' });
} catch (error) {
console.error('❌ Erro em runDiagnosticoServico:', error);
await this.bot.sendMessage(chatId, `❌ Erro no diagnóstico: ${error.message}`);
}
}
/**
* Diagnóstico de ONU: detecta tipo (Datacom/FiberHome) e retorna dados estruturados
*/
async _diagnosticarONU(serial) {
const serialUpper = serial.toUpperCase();
if (serialUpper.startsWith('DACM')) {
return await this._diagnosticarONUDatacom(serial);
} else {
return await this._diagnosticarONUFiberhome(serial);
}
}
async _diagnosticarONUDatacom(serial) {
const olts = this.ssh.listOLTs();
if (olts.length === 0) {
return { success: false, error: 'Nenhuma OLT Datacom configurada' };
}
// Buscar ONU em todas as OLTs em paralelo
const searches = olts.map(olt =>
this.ssh.findOnuBySerial(olt.id, serial)
.then(r => ({ ...r, oltId: olt.id, oltName: olt.name }))
.catch(() => ({ success: false }))
);
const results = await Promise.allSettled(searches);
let found = null;
for (const r of results) {
if (r.status === 'fulfilled' && r.value.success) {
found = r.value;
break;
}
}
if (!found) {
return { success: false, error: `ONU ${serial} não encontrada em nenhuma OLT Datacom` };
}
const { oltId, oltName, onuInfo } = found;
const { slot, onuId } = onuInfo;
// Buscar sinal e porta ethernet em paralelo
const [signalResult, ethResult] = await Promise.all([
this.ssh.checkOnuSignal(oltId, slot, onuId).catch(e => ({ success: false, error: e.message })),
this.ssh.checkOnuEthernetDatacom(oltId, slot, onuId).catch(e => ({ success: false }))
]);
// Parsear sinal
const rawSignal = signalResult.output || '';
const stateMatch = rawSignal.match(/Operational\s+state\s*:\s*(\S+)/i);
const rxMatch = rawSignal.match(/Rx\s+Optical\s+Power\s*\[dBm\]\s*:\s*([-\d.]+)/i);
const operationalState = stateMatch ? stateMatch[1] : null;
const rxPower = rxMatch ? `${rxMatch[1]} dBm` : null;
const online = operationalState ? /up|active|online/i.test(operationalState) : false;
// Parsear ethernet
let portaEthernet = null;
if (ethResult.success) {
const ethRaw = ethResult.rawOutput || '';
const ethMatch = ethRaw.match(/\b(Forwarding|Down|Up|Blocking)\b[^\n]*([\d]+(?:Gbit|Mbit)[^\n]*|N\/A)?/i);
if (ethMatch) {
portaEthernet = ethMatch[0].trim().replace(/\s+/g, ' ');
}
}
return {
success: true,
type: 'datacom',
oltName,
slot,
pon: slot ? slot.split('/')[2] || slot : null,
onuId,
online,
rxPower,
operationalState,
portaEthernet
};
}
async _diagnosticarONUFiberhome(serial) {
const resultado = await this.fiberhome.obterStatusONU(serial);
if (!resultado.success) {
return { success: false, error: resultado.error || 'ONU não encontrada na FiberHome' };
}
const portaPrincipal = resultado.portasEthernet && resultado.portasEthernet.length > 0
? resultado.portasEthernet[0]
: null;
return {
success: true,
type: 'fiberhome',
oltName: 'FiberHome',
slot: resultado.slot,
pon: resultado.pon,
onuId: resultado.onuId,
online: resultado.online,
rxPower: resultado.rxPower,
txPower: resultado.txPower,
portaEthernet: portaPrincipal ? `${portaPrincipal.status}` : null
};
}
/** /**
* Verificar sinal de ONU * Verificar sinal de ONU
*/ */
@@ -1539,6 +1893,12 @@ Digite /help para ver todos os comandos disponíveis!
const serialNumber = data.replace('fiberhome_details_', ''); const serialNumber = data.replace('fiberhome_details_', '');
await this.bot.answerCallbackQuery(callbackQuery.id); await this.bot.answerCallbackQuery(callbackQuery.id);
await this.handleStatusONU(chatId, serialNumber); await this.handleStatusONU(chatId, serialNumber);
} else if (data.startsWith('diag_cliente_')) {
console.log(`🔍 Condição diag_cliente_ atendida`);
await this.handleDiagClienteCallback(callbackQuery, data);
} else if (data.startsWith('diag_servico_')) {
console.log(`🔍 Condição diag_servico_ atendida`);
await this.handleDiagServicoCallback(callbackQuery, data);
} else if (data.startsWith('dados_cliente_')) { } else if (data.startsWith('dados_cliente_')) {
console.log(`🔍 Condição dados_cliente_ atendida`); console.log(`🔍 Condição dados_cliente_ atendida`);
await this.handleDadosClienteCallback(callbackQuery, data); await this.handleDadosClienteCallback(callbackQuery, data);
+18 -1
View File
@@ -32,7 +32,8 @@ class NLPProcessor {
'dados_cliente': ['dados cliente', 'dados cadastrais', 'cadastro cliente', 'informações cliente', 'info cliente', 'perfil cliente', 'ficha cliente', 'registro cliente', 'ver dados'], 'dados_cliente': ['dados cliente', 'dados cadastrais', 'cadastro cliente', 'informações cliente', 'info cliente', 'perfil cliente', 'ficha cliente', 'registro cliente', 'ver dados'],
'listar_servicos': ['listar serviços', 'servicos cliente', 'planos cliente', 'contratos cliente', 'ver servicos', 'plano cliente', 'assinaturas', 'planos', 'serviços ativos', 'ver plano'], 'listar_servicos': ['listar serviços', 'servicos cliente', 'planos cliente', 'contratos cliente', 'ver servicos', 'plano cliente', 'assinaturas', 'planos', 'serviços ativos', 'ver plano'],
'abrir_suporte': ['abrir suporte', 'criar suporte', 'adicionar suporte', 'novo suporte', 'suporte', 'chamado', 'ticket', 'atendimento', 'suporte tecnico', 'atendimento tecnico', 'abrir chamado', 'registrar chamado', 'criar chamado', 'visita tecnica', 'agendar visita', 'reclamação', 'reclamacao'], 'abrir_suporte': ['abrir suporte', 'criar suporte', 'adicionar suporte', 'novo suporte', 'suporte', 'chamado', 'ticket', 'atendimento', 'suporte tecnico', 'atendimento tecnico', 'abrir chamado', 'registrar chamado', 'criar chamado', 'visita tecnica', 'agendar visita', 'reclamação', 'reclamacao'],
'consultar_usuario': ['consultar usuario', 'consultar user', 'ver usuario', 'status usuario', 'status user', 'info usuario', 'info user', 'checar usuario', 'verificar usuario', 'pesquisar usuario', 'localizar usuario', 'buscar usuario', 'usuario conectado', 'user conectado', 'usuario online', 'user online', 'usuario ativo', 'user ativo'] 'consultar_usuario': ['consultar usuario', 'consultar user', 'ver usuario', 'status usuario', 'status user', 'info usuario', 'info user', 'checar usuario', 'verificar usuario', 'pesquisar usuario', 'localizar usuario', 'buscar usuario', 'usuario conectado', 'user conectado', 'usuario online', 'user online', 'usuario ativo', 'user ativo'],
'diagnosticar': ['diagnosticar', 'diagnostico', 'diagnose', 'diagnosticar cliente', 'diagnostico cliente', 'checar cliente completo', 'verificar cliente completo', 'relatorio cliente', 'relatorio completo', 'status completo cliente', 'analisar cliente', 'inspecionar cliente', 'situacao do cliente', 'situação do cliente']
}; };
// Padrões de comandos // Padrões de comandos
@@ -81,6 +82,8 @@ class NLPProcessor {
// Padrões para consultar usuário específico // Padrões para consultar usuário específico
/^(consultar|ver|status|info|checar|verificar|pesquisar|localizar|buscar)\s+(usuario|user)\s+(.+)$/i, /^(consultar|ver|status|info|checar|verificar|pesquisar|localizar|buscar)\s+(usuario|user)\s+(.+)$/i,
/^(usuario|user)\s+(.+)$/i, /^(usuario|user)\s+(.+)$/i,
// Padrões para diagnosticar cliente
/^(diagnosticar|diagnostico|diagnose|analisar|inspecionar)\s+(cliente\s+)?(.+)$/i,
// Padrões para listar usuários // Padrões para listar usuários
/^(listar|mostrar)\s+(usuários|usuarios|clientes|users)(\s+conectados)?$/i, /^(listar|mostrar)\s+(usuários|usuarios|clientes|users)(\s+conectados)?$/i,
// Padrões gerais (devem vir por último) // Padrões gerais (devem vir por último)
@@ -255,6 +258,20 @@ class NLPProcessor {
}; };
} }
// Detectar diagnóstico de cliente
if (/^diagnosticar$|^diagnostico$|^diagnose$|^analisar$|^inspecionar$/i.test(action)) {
// match[2] pode ser "cliente " ou já o nome, match[3] é o nome quando match[2]="cliente "
let query = match[3] || match[2];
// remover prefixo "cliente" se vier junto
if (query) query = query.replace(/^cliente\s+/i, '').trim();
return {
action: 'diagnosticar',
target: query || null,
confidence: 0.95,
type: 'diagnosticar_pattern'
};
}
// Detectar comandos de suporte // Detectar comandos de suporte
if (/abrir|criar|adicionar|novo|suporte|chamado|ticket|atendimento/i.test(action) && if (/abrir|criar|adicionar|novo|suporte|chamado|ticket|atendimento/i.test(action) &&
/suporte|chamado|ticket|atendimento/i.test(fullMatch)) { /suporte|chamado|ticket|atendimento/i.test(fullMatch)) {
+52
View File
@@ -468,6 +468,58 @@ class SSHManager {
} }
} }
/**
* Verifica o status da porta Ethernet de uma ONU Datacom pelo slot e ID
* @param {string} equipmentId - ID do equipamento OLT
* @param {string} slot - Slot da ONU (ex: 1/1/1)
* @param {string} onuId - ID da ONU
* @returns {Promise} - { success, portStatus, speed, rawOutput }
*/
async checkOnuEthernetDatacom(equipmentId, slot, onuId) {
try {
const connection = await this.connect(equipmentId);
if (!connection.success) {
throw new Error(`Falha na conexão: ${connection.message}`);
}
const command = `show interface gpon ${slot} onu ${onuId} ethernet`;
const result = await this.executeCommand(connection.connectionId, command);
this.disconnect(connection.connectionId);
if (!result.success) {
return { success: false, error: result.error, rawOutput: result.output };
}
// Extrai status e velocidade da porta ethernet
// Linha típica: " 1 Forwarding 1Gbit Full-Duplex"
// ou: " 1 Down N/A"
const portStatus = null;
const speed = null;
const output = result.output || '';
// Procurar por linha de porta ethernet
const lineMatch = output.match(/\b(Forwarding|Down|Up|Blocking)\b.*?([\d]+(?:Gbit|Mbit)[^\n]*|N\/A)/i);
const statusVal = lineMatch ? lineMatch[1] : null;
const speedVal = lineMatch ? lineMatch[2]?.trim() : null;
return {
success: true,
portStatus: statusVal,
speed: speedVal,
rawOutput: output
};
} catch (error) {
return {
success: false,
error: error.message,
rawOutput: null
};
}
}
/** /**
* Busca informações de uma ONU pelo serial number na OLT * Busca informações de uma ONU pelo serial number na OLT
* @param {string} equipmentId - ID do equipamento OLT * @param {string} equipmentId - ID do equipamento OLT