cgnat: add função
Signed-off-by: Rodrigo Martins <rodrigo@martinsevieira.adv.br>
This commit is contained in:
1 parent
f20c586f94
commit
6a5742ad7c
6 files changed
+10330
-2
No files matched your search
+208
-1
@@ -7,6 +7,7 @@ const { GenieACSManager } = require('../genieacs');
|
||||
const config = require('../config/config');
|
||||
const logger = require('../utils/logger');
|
||||
const DateProcessor = require('../utils/dateProcessor');
|
||||
const cgnatParser = require('../cgn_list/parser');
|
||||
|
||||
class BotHandler {
|
||||
constructor(bot) {
|
||||
@@ -287,6 +288,11 @@ class BotHandler {
|
||||
console.log(`🔄 Executando handleReiniciarOnu com target: ${target}`);
|
||||
await this.handleReiniciarOnu(chatId, target);
|
||||
break;
|
||||
|
||||
case 'ver_cgnat':
|
||||
console.log(`🌐 Executando handleVerCgnat com target: ${target}`);
|
||||
await this.handleVerCgnat(chatId, userId, target);
|
||||
break;
|
||||
|
||||
case 'conectar':
|
||||
await this.handleConnect(chatId, target);
|
||||
@@ -1800,11 +1806,15 @@ Digite /help para ver todos os comandos disponíveis!
|
||||
await this.executeCustomCommand(chatId, session.connectionId, text);
|
||||
this.userSessions.delete(userId);
|
||||
return;
|
||||
|
||||
|
||||
case 'waiting_username_disconnect':
|
||||
await this.handleDisconnectUser(chatId, text.trim());
|
||||
this.userSessions.delete(userId);
|
||||
return;
|
||||
|
||||
case 'cgnat_aguardando_datetime':
|
||||
await this.handleCgnatDatetimeInput(chatId, userId, text.trim(), session);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2142,6 +2152,11 @@ Digite /help para ver todos os comandos disponíveis!
|
||||
await this._configurarRoteadorPorServico(chatId, userId, servico, location);
|
||||
}
|
||||
}
|
||||
} else if (data.startsWith('buscar_cgnat_user_')) {
|
||||
console.log(`🔍 Condição buscar_cgnat_user_ atendida`);
|
||||
await this.bot.answerCallbackQuery(callbackQuery.id);
|
||||
const login = data.replace('buscar_cgnat_user_', '');
|
||||
await this.handleBuscarCliente(chatId, login);
|
||||
} else if (data.startsWith('diag_cliente_')) {
|
||||
console.log(`🔍 Condição diag_cliente_ atendida`);
|
||||
await this.handleDiagClienteCallback(callbackQuery, data);
|
||||
@@ -8858,6 +8873,198 @@ Digite /help para ver todos os comandos disponíveis!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consulta CGNAT: dado IP público + porta opcional, informa o IP privado
|
||||
* e opcionalmente busca no TopSapp quem estava usando aquele IP.
|
||||
*/
|
||||
async handleVerCgnat(chatId, userId, target) {
|
||||
if (!target) {
|
||||
await this.bot.sendMessage(chatId,
|
||||
'🌐 *Consulta CGNAT*\n\n' +
|
||||
'Informe o IP público e, opcionalmente, a porta:\n\n' +
|
||||
'• `cgnat 131.0.193.0 1500` — busca por IP + porta\n' +
|
||||
'• `cgnat 131.0.193.0` — lista todos os IPs privados do IP público',
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parsear target: "IP" ou "IP PORTA"
|
||||
const parts = target.trim().split(/\s+/);
|
||||
const publicIp = parts[0];
|
||||
const porta = parts[1] ? parseInt(parts[1], 10) : null;
|
||||
|
||||
// Validar IP
|
||||
if (!/^\d+\.\d+\.\d+\.\d+$/.test(publicIp)) {
|
||||
await this.bot.sendMessage(chatId, `❌ IP inválido: \`${publicIp}\``, { parse_mode: 'Markdown' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cgnatParser.hasPublicIp(publicIp)) {
|
||||
await this.bot.sendMessage(chatId, `❌ IP \`${publicIp}\` não encontrado na tabela CGNAT.`, { parse_mode: 'Markdown' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (porta !== null) {
|
||||
// Busca por IP + porta
|
||||
const entry = cgnatParser.lookupByPort(publicIp, porta);
|
||||
if (!entry) {
|
||||
await this.bot.sendMessage(chatId,
|
||||
`❌ Porta *${porta}* não encontrada nos ranges do IP \`${publicIp}\`.`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.bot.sendMessage(chatId,
|
||||
`🌐 *Resultado CGNAT*\n\n` +
|
||||
`📡 IP Público: \`${publicIp}\`\n` +
|
||||
`🔌 Porta: \`${porta}\`\n` +
|
||||
`🖥️ IP Privado: \`${entry.privateIP}\`\n` +
|
||||
`📊 Range de portas: \`${entry.portStart} – ${entry.portEnd}\`\n\n` +
|
||||
`🔍 Deseja consultar quem usava este IP no TopSapp?\n` +
|
||||
`Digite a data e hora do acesso no formato:\n` +
|
||||
`\`DD/MM/AAAA HH:MM\`\n\n` +
|
||||
`Ou envie *cancelar* para encerrar.`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
|
||||
this.userSessions.set(userId, {
|
||||
type: 'cgnat_aguardando_datetime',
|
||||
privateIP: entry.privateIP,
|
||||
publicIP: publicIp,
|
||||
porta
|
||||
});
|
||||
|
||||
} else {
|
||||
// Listar todos os IPs privados
|
||||
const entries = cgnatParser.lookupAll(publicIp);
|
||||
if (!entries || entries.length === 0) {
|
||||
await this.bot.sendMessage(chatId, `❌ Nenhum IP privado encontrado para \`${publicIp}\`.`, { parse_mode: 'Markdown' });
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = `🌐 *IPs privados do IP público \`${publicIp}\`*\n\n`;
|
||||
for (const e of entries) {
|
||||
msg += `• \`${e.privateIP}\` — portas \`${e.portStart}\` a \`${e.portEnd}\`\n`;
|
||||
}
|
||||
msg += `\n💡 Para identificar o cliente, informe também a porta:\n\`cgnat ${publicIp} PORTA\``;
|
||||
|
||||
await this.bot.sendMessage(chatId, msg, { parse_mode: 'Markdown' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recebe a data/hora e consulta o TopSapp por acessos do IP privado
|
||||
*/
|
||||
async handleCgnatDatetimeInput(chatId, userId, text, session) {
|
||||
if (/^cancelar$/i.test(text)) {
|
||||
this.userSessions.delete(userId);
|
||||
await this.bot.sendMessage(chatId, '❌ Consulta cancelada.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Aceitar formatos: "DD/MM/AAAA HH:MM" ou "DD/MM/AAAA"
|
||||
const dtMatch = text.match(/^(\d{2}\/\d{2}\/\d{4})(?:\s+(\d{2}:\d{2}))?$/);
|
||||
if (!dtMatch) {
|
||||
await this.bot.sendMessage(chatId,
|
||||
'❌ Formato inválido. Use `DD/MM/AAAA HH:MM` ou `DD/MM/AAAA`.\n' +
|
||||
'Ou envie *cancelar* para encerrar.',
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.userSessions.delete(userId);
|
||||
|
||||
const dataStr = dtMatch[1]; // DD/MM/AAAA
|
||||
const horaStr = dtMatch[2]; // HH:MM ou undefined
|
||||
|
||||
await this.bot.sendMessage(chatId,
|
||||
`🔍 Consultando acessos de \`${session.privateIP}\` em ${dataStr}${horaStr ? ' ' + horaStr : ''}...`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
|
||||
const result = await this.topsapp.obterAcessosPorEstacao(
|
||||
session.privateIP,
|
||||
dataStr,
|
||||
dataStr
|
||||
);
|
||||
|
||||
if (!result.success) {
|
||||
await this.bot.sendMessage(chatId,
|
||||
`❌ Erro ao consultar TopSapp: ${result.error}`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const acessos = result.data?.dados || [];
|
||||
|
||||
if (acessos.length === 0) {
|
||||
await this.bot.sendMessage(chatId,
|
||||
`ℹ️ Nenhum acesso encontrado para o IP \`${session.privateIP}\` em ${dataStr}.`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Filtrar por hora se informada
|
||||
let filtrados = acessos;
|
||||
if (horaStr) {
|
||||
const [hh, mm] = horaStr.split(':').map(Number);
|
||||
const refMs = hh * 3600000 + mm * 60000;
|
||||
const janela = 3600000; // ±1 hora
|
||||
filtrados = acessos.filter(a => {
|
||||
const inicioMatch = a.inicio ? a.inicio.match(/(\d{2}):(\d{2})/) : null;
|
||||
if (!inicioMatch) return true;
|
||||
const aMs = parseInt(inicioMatch[1]) * 3600000 + parseInt(inicioMatch[2]) * 60000;
|
||||
return Math.abs(aMs - refMs) <= janela;
|
||||
});
|
||||
}
|
||||
|
||||
if (filtrados.length === 0) {
|
||||
await this.bot.sendMessage(chatId,
|
||||
`ℹ️ Nenhum acesso encontrado para \`${session.privateIP}\` próximo às ${horaStr} em ${dataStr}.`,
|
||||
{ parse_mode: 'Markdown' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = `🌐 *Acessos CGNAT — \`${session.privateIP}\`*\n`;
|
||||
msg += `📅 Data: ${dataStr}${horaStr ? ' ± 1h de ' + horaStr : ''}\n\n`;
|
||||
|
||||
for (const a of filtrados.slice(0, 10)) {
|
||||
const inicio = a.inicio ? a.inicio.replace(/\.\d+.*$/, '').replace('T', ' ') : '—';
|
||||
const termino = a.termino ? a.termino.replace(/\.\d+.*$/, '').replace('T', ' ') : 'ativo';
|
||||
msg += `👤 *${a.usuario || '—'}*\n`;
|
||||
msg += ` 🕐 ${inicio} → ${termino}\n`;
|
||||
if (a.ip) msg += ` 🖥️ IP: \`${a.ip}\`\n`;
|
||||
if (a.estacao) msg += ` 📡 Estação: ${a.estacao}\n`;
|
||||
msg += '\n';
|
||||
}
|
||||
|
||||
if (filtrados.length > 10) {
|
||||
msg += `_... e mais ${filtrados.length - 10} registro(s)._\n`;
|
||||
}
|
||||
|
||||
await this.bot.sendMessage(chatId, msg, { parse_mode: 'Markdown' });
|
||||
|
||||
// Se encontrou usuário(s), oferecer busca de nome do cliente
|
||||
const logins = [...new Set(filtrados.slice(0, 10).map(a => a.usuario).filter(Boolean))];
|
||||
if (logins.length === 1) {
|
||||
const keyboard = {
|
||||
inline_keyboard: [[
|
||||
{ text: `🔍 Ver dados de ${logins[0]}`, callback_data: `buscar_cgnat_user_${logins[0]}` },
|
||||
{ text: '❌ Fechar', callback_data: 'cancel_action' }
|
||||
]]
|
||||
};
|
||||
await this.bot.sendMessage(chatId, '💡 Deseja ver os dados cadastrais do cliente?', {
|
||||
reply_markup: keyboard
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reiniciar ONU Datacom — busca em todas as OLTs
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user