diff --git a/src/bot/handler.js b/src/bot/handler.js index 642abea..08e3ad9 100644 --- a/src/bot/handler.js +++ b/src/bot/handler.js @@ -9006,10 +9006,11 @@ Digite /help para ver todos os comandos disponíveis! { parse_mode: 'Markdown' } ); - const result = await this.topsapp.obterAcessosPorIp( + const result = await this.topsapp.buscarSessaoPorHorario( session.privateIP, dataStr, - dataStr + dataStr, + horaStr || null ); if (!result.success) { @@ -9022,39 +9023,20 @@ Digite /help para ver todos os comandos disponíveis! if (result.total === 0) { const extra = result.totalGeral > 0 - ? `\n\n_(${result.totalGeral} registros no período, nenhum com IP \`${session.privateIP}\`)_` - : ''; + ? `\n\n_${result.totalGeral} registros retornados para ${dataStr} — nenhuma sessão com IP \`${session.privateIP}\` ou ativa às ${horaStr || 'hora informada'}._` + : `\n\n_Nenhum registro retornado para ${dataStr}._`; await this.bot.sendMessage(chatId, - `ℹ️ Nenhum acesso encontrado para o IP \`${session.privateIP}\` em ${dataStr}.${extra}`, + `ℹ️ Não foi possível identificar o cliente para \`${session.privateIP}\` em ${dataStr}${horaStr ? ' ' + horaStr : ''}.${extra}`, { parse_mode: 'Markdown' } ); return; } - // Filtrar por hora se informada - let filtrados = result.dados; - if (horaStr) { - const [hh, mm] = horaStr.split(':').map(Number); - const refMs = hh * 3600000 + mm * 60000; - const janela = 3600000; // ±1 hora - filtrados = result.dados.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 de \`${session.privateIP}\` próximo às ${horaStr} em ${dataStr}.\n_(${result.total} registro(s) no dia fora desse horário)_`, - { parse_mode: 'Markdown' } - ); - return; - } - - const titulo = `Acessos em ${dataStr}${horaStr ? ' ± 1h de ' + horaStr : ''}`; - await this._exibirAcessosCgnat(chatId, session.privateIP, filtrados, titulo); + const aviso = result.modoFallback + ? '\n⚠️ _IP não encontrado nos registros (sessão encerrada). Mostrando sessões ativas nesse horário._\n' + : ''; + const titulo = `Sessão em ${dataStr}${horaStr ? ' ' + horaStr : ''}`; + await this._exibirAcessosCgnat(chatId, session.privateIP, result.dados, titulo + aviso); } /** diff --git a/src/topsapp/manager.js b/src/topsapp/manager.js index 8e1a6b4..b9e9f15 100644 --- a/src/topsapp/manager.js +++ b/src/topsapp/manager.js @@ -1330,46 +1330,8 @@ class TopSappManager { } /** - * Busca acessos por data no TopSapp e filtra pelo IP privado CGNAT client-side - * (estacao = nome NAS, não o IP — por isso filtramos pelo campo `ip` da resposta) - */ - async obterAcessosPorIp(privateIP, dataInicial, dataFinal, location = '') { - if (!this.isReady()) { - return { success: false, error: 'TopSapp não está configurado' }; - } - try { - const credentials = this.config.getCredentialsByLocation(location); - const instance = this.api.getInstanceByLocation(location); - const params = {}; - if (dataInicial) params.dataInicial = dataInicial; - if (dataFinal) params.dataFinal = dataFinal; - const result = await this.api.obterAcessos(params, instance, credentials); - - if (!result.success) return result; - - const todos = result.data?.dados || []; - console.log(`🔍 CGNAT: ${todos.length} acessos retornados para o período`); - if (todos.length > 0) { - console.log(`🔍 CGNAT: campos do primeiro registro:`, Object.keys(todos[0])); - console.log(`🔍 CGNAT: amostra ip/estacao:`, todos.slice(0, 3).map(a => ({ ip: a.ip, estacao: a.estacao, usuario: a.usuario }))); - } - - const encontrados = todos.filter(a => a.ip === privateIP); - return { - success: true, - dados: encontrados, - total: encontrados.length, - totalGeral: todos.length, - instance: result.instance - }; - } catch (error) { - return { success: false, error: error.message }; - } - } - - /** - * Busca qual serviço está usando um IP privado CGNAT agora (conexões ativas) - * Usa ObterClientesServicos com conexao: "online" e filtra client-side pelo campo ip + * Busca sessão ativa com determinado IP privado CGNAT. + * Usa status:'on' — somente sessões ativas têm o campo `ip` preenchido pelo RADIUS. */ async buscarConexaoAtivaPorIp(privateIP, location = '') { if (!this.isReady()) { @@ -1378,16 +1340,12 @@ class TopSappManager { try { const credentials = this.config.getCredentialsByLocation(location); const instance = this.api.getInstanceByLocation(location); - const result = await this.api.obterConexoesAtivas(instance, credentials); + const result = await this.api.obterAcessos({ status: 'on' }, instance, credentials); if (!result.success) return result; const todos = result.data?.dados || []; - console.log(`🔍 CGNAT ativas: ${todos.length} conexões online retornadas`); - if (todos.length > 0) { - console.log(`🔍 CGNAT ativas: campos do primeiro registro:`, Object.keys(todos[0])); - console.log(`🔍 CGNAT ativas: amostra ip/usuario:`, todos.slice(0, 3).map(s => ({ ip: s.ip, usuario: s.usuario }))); - } + console.log(`🔍 CGNAT ativas: ${todos.length} sessões ativas retornadas`); const encontrados = todos.filter(s => s.ip === privateIP); return { @@ -1402,6 +1360,73 @@ class TopSappManager { } } + /** + * Busca qual sessão estava ativa num determinado horário (histórico). + * Como sessões encerradas têm ip:'', filtramos por janela de tempo: + * sessões que iniciaram antes do horário e encerraram depois (ou ainda ativas). + * @param {string} privateIP + * @param {string} dataInicial DD/MM/YYYY + * @param {string} dataFinal DD/MM/YYYY + * @param {string} horaRef HH:MM (opcional) — filtra sessões ativas naquele momento + * @param {string} location + */ + async buscarSessaoPorHorario(privateIP, dataInicial, dataFinal, horaRef = null, location = '') { + if (!this.isReady()) { + return { success: false, error: 'TopSapp não está configurado' }; + } + try { + const credentials = this.config.getCredentialsByLocation(location); + const instance = this.api.getInstanceByLocation(location); + const params = {}; + if (dataInicial) params.dataInicial = dataInicial; + if (dataFinal) params.dataFinal = dataFinal; + const result = await this.api.obterAcessos(params, instance, credentials); + + if (!result.success) return result; + + const todos = result.data?.dados || []; + console.log(`🔍 CGNAT histórico: ${todos.length} registros retornados para o período`); + + // Sessões com IP preenchido (ativas durante o período) + const comIp = todos.filter(a => a.ip === privateIP); + + // Se não achamos pelo IP (sessões encerradas têm ip:''), tentar por janela de tempo + // Sessões ativas no horário: inicio <= horaRef <= termino (ou termino vazio) + let porHorario = []; + if (horaRef && comIp.length === 0) { + const [hh, mm] = horaRef.split(':').map(Number); + const refTotalMin = hh * 60 + mm; + + porHorario = todos.filter(a => { + if (!a.inicio) return false; + const inicioMatch = a.inicio.match(/(\d{2}):(\d{2})/); + if (!inicioMatch) return false; + const inicioMin = parseInt(inicioMatch[1]) * 60 + parseInt(inicioMatch[2]); + if (inicioMin > refTotalMin) return false; // ainda não havia iniciado + + if (!a.termino) return true; // sessão ainda ativa + + const terminoMatch = a.termino.match(/(\d{2}):(\d{2})/); + if (!terminoMatch) return true; + const terminoMin = parseInt(terminoMatch[1]) * 60 + parseInt(terminoMatch[2]); + return terminoMin >= refTotalMin; + }); + } + + const encontrados = comIp.length > 0 ? comIp : porHorario; + return { + success: true, + dados: encontrados, + total: encontrados.length, + totalGeral: todos.length, + modoFallback: comIp.length === 0 && porHorario.length > 0, + instance: result.instance + }; + } catch (error) { + return { success: false, error: error.message }; + } + } + } module.exports = TopSappManager;