diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..c6da2c5 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(node --check \"c:\\\\Users\\\\rodri\\\\build\\\\rodrigonet\\\\src\\\\bot\\\\handler.js\")", + "Bash(node --check \"c:\\\\Users\\\\rodri\\\\build\\\\rodrigonet\\\\src\\\\topsapp\\\\manager.js\")" + ] + } +} diff --git a/src/bot/handler.js b/src/bot/handler.js index 43cf61f..15c2a39 100644 --- a/src/bot/handler.js +++ b/src/bot/handler.js @@ -2152,6 +2152,27 @@ Digite /help para ver todos os comandos disponíveis! await this._configurarRoteadorPorServico(chatId, userId, servico, location); } } + } else if (data.startsWith('cgnat_ativas_')) { + console.log(`🔍 Condição cgnat_ativas_ atendida`); + await this.bot.answerCallbackQuery(callbackQuery.id); + const privateIP = data.replace('cgnat_ativas_', ''); + await this.handleCgnatConsultarAtivas(chatId, privateIP); + } else if (data.startsWith('cgnat_pedir_data_')) { + console.log(`🔍 Condição cgnat_pedir_data_ atendida`); + await this.bot.answerCallbackQuery(callbackQuery.id); + const privateIP = data.replace('cgnat_pedir_data_', ''); + this.userSessions.set(userId, { + type: 'cgnat_aguardando_datetime', + privateIP, + publicIP: null, + porta: null + }); + await this.bot.sendMessage(chatId, + `📅 Digite a data e hora do acesso para \`${privateIP}\`:\n` + + `Formato: \`DD/MM/AAAA HH:MM\` ou \`DD/MM/AAAA\`\n\n` + + `Ou envie *cancelar* para encerrar.`, + { parse_mode: 'Markdown' } + ); } else if (data.startsWith('buscar_cgnat_user_')) { console.log(`🔍 Condição buscar_cgnat_user_ atendida`); await this.bot.answerCallbackQuery(callbackQuery.id); @@ -8916,26 +8937,26 @@ Digite /help para ver todos os comandos disponíveis! return; } + const keyboard = { + inline_keyboard: [ + [ + { text: '🟢 Conexões ativas agora', callback_data: `cgnat_ativas_${entry.privateIP}` }, + { text: '📅 Informar data e hora', callback_data: `cgnat_pedir_data_${entry.privateIP}` } + ], + [{ text: '❌ Cancelar', callback_data: 'cancel_action' }] + ] + }; + 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' } + `🔍 Deseja consultar quem está/estava usando este IP no TopSapp?`, + { parse_mode: 'Markdown', reply_markup: keyboard } ); - 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); @@ -9031,10 +9052,49 @@ Digite /help para ver todos os comandos disponíveis! return; } - let msg = `🌐 *Acessos CGNAT — \`${session.privateIP}\`*\n`; - msg += `📅 Data: ${dataStr}${horaStr ? ' ± 1h de ' + horaStr : ''}\n\n`; + const titulo = `Acessos em ${dataStr}${horaStr ? ' ± 1h de ' + horaStr : ''}`; + await this._exibirAcessosCgnat(chatId, session.privateIP, filtrados, titulo); + } - for (const a of filtrados.slice(0, 10)) { + /** + * Consulta conexões ATIVAS agora para o IP privado CGNAT (status: on) + */ + async handleCgnatConsultarAtivas(chatId, privateIP) { + await this.bot.sendMessage(chatId, + `🔍 Consultando conexões ativas para \`${privateIP}\`...`, + { parse_mode: 'Markdown' } + ); + + const result = await this.topsapp.obterAcessosPorEstacao(privateIP, null, null, '', 'on'); + + 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, + `ℹ️ Nenhuma conexão ativa encontrada para o IP \`${privateIP}\` no momento.`, + { parse_mode: 'Markdown' } + ); + return; + } + + await this._exibirAcessosCgnat(chatId, privateIP, acessos, '🟢 Conexões ativas'); + } + + /** + * Exibe os acessos CGNAT e oferece botão para ver dados do cliente + */ + async _exibirAcessosCgnat(chatId, privateIP, acessos, titulo) { + let msg = `🌐 *${titulo} — \`${privateIP}\`*\n\n`; + + for (const a of acessos.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`; @@ -9044,14 +9104,14 @@ Digite /help para ver todos os comandos disponíveis! msg += '\n'; } - if (filtrados.length > 10) { - msg += `_... e mais ${filtrados.length - 10} registro(s)._\n`; + if (acessos.length > 10) { + msg += `_... e mais ${acessos.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))]; + // Se único login, oferecer busca no TopSapp + const logins = [...new Set(acessos.slice(0, 10).map(a => a.usuario).filter(Boolean))]; if (logins.length === 1) { const keyboard = { inline_keyboard: [[ diff --git a/src/topsapp/manager.js b/src/topsapp/manager.js index 11eace1..a7298b5 100644 --- a/src/topsapp/manager.js +++ b/src/topsapp/manager.js @@ -1331,19 +1331,26 @@ class TopSappManager { /** * Busca acessos por estação/IP privado CGNAT no TopSapp - * @param {string} estacao - IP privado (ex: 100.110.0.5) ou nome da estação - * @param {string} dataInicial - formato dd/mm/yyyy - * @param {string} dataFinal - formato dd/mm/yyyy + * @param {string} estacao - IP privado (ex: 100.110.0.5) ou nome da estação + * @param {string|null} dataInicial - formato dd/mm/yyyy (ignorado se status='on') + * @param {string|null} dataFinal - formato dd/mm/yyyy (ignorado se status='on') * @param {string} location + * @param {string} status - 'on' para listar apenas conexões ativas (ignora período) */ - async obterAcessosPorEstacao(estacao, dataInicial, dataFinal, location = '') { + async obterAcessosPorEstacao(estacao, dataInicial, dataFinal, location = '', status = null) { 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 = { estacao, dataInicial, dataFinal }; + const params = { estacao }; + if (status) { + params.status = status; + } else { + if (dataInicial) params.dataInicial = dataInicial; + if (dataFinal) params.dataFinal = dataFinal; + } return await this.api.obterAcessos(params, instance, credentials); } catch (error) { return { success: false, error: error.message };