diff --git a/.env.example b/.env.example index b38b330..0029b2a 100644 --- a/.env.example +++ b/.env.example @@ -8,8 +8,32 @@ DEFAULT_SSH_PASSWORD=password # Configurações do bot BOT_DEBUG=true + +# IDs do Telegram com acesso a TODAS as cidades/provedores (admin) ALLOWED_USERS=123456789,987654321 +# ========================================================== +# Acesso por cidade/provedor (IDs do Telegram, separados por vírgula) +# Um usuário pode estar em mais de uma lista; se estiver em mais de +# uma cidade, o bot exibirá um menu para escolher qual atender. +# ========================================================== +GUARAI_ALLOWED_USERS= +PEDRO_AFONSO_ALLOWED_USERS= + +# ========================================================== +# Concentrador Juniper - Guaraí +# ========================================================== +JUNIPER_GUARAI_HOST=131.0.192.254 +JUNIPER_GUARAI_USER=rodrigo +JUNIPER_GUARAI_PASS= + +# ========================================================== +# Concentrador Juniper - Pedro Afonso +# ========================================================== +JUNIPER_PEDRO_AFONSO_HOST=45.189.140.2 +JUNIPER_PEDRO_AFONSO_USER=rodrigo +JUNIPER_PEDRO_AFONSO_PASS= + # Configurações TopSapp - Instância Guarai/Colmeia TOPSAPP_GUARAI_USER=seu_usuario_guarai TOPSAPP_GUARAI_PASS=sua_senha_guarai diff --git a/.env.production b/.env.production index df035f0..803013f 100644 --- a/.env.production +++ b/.env.production @@ -15,10 +15,40 @@ DEFAULT_SSH_PASSWORD=YOUR_SSH_PASSWORD_HERE BOT_DEBUG=false NODE_ENV=production -# IDs dos usuários autorizados (separados por vírgula) +# IDs dos usuários autorizados em TODAS as cidades/provedores (admin) # Para descobrir seu ID, envie /start para @userinfobot ALLOWED_USERS=593272271 +# IDs autorizados por cidade/provedor (deixe vazio se só ALLOWED_USERS for usado) +GUARAI_ALLOWED_USERS= +PEDRO_AFONSO_ALLOWED_USERS= + +# ========================================================== +# Concentrador Juniper - Guaraí +# ========================================================== +JUNIPER_GUARAI_HOST=131.0.192.254 +JUNIPER_GUARAI_USER=rodrigo +JUNIPER_GUARAI_PASS=@102030Rod + +# ========================================================== +# Concentrador Juniper - Pedro Afonso +# ========================================================== +JUNIPER_PEDRO_AFONSO_HOST=45.189.140.2 +JUNIPER_PEDRO_AFONSO_USER=rodrigo +JUNIPER_PEDRO_AFONSO_PASS=2311@Goiania#2017 + +# ========================================================== +# Configurações TopSapp - Instância Guaraí/Colmeia +# ========================================================== +TOPSAPP_GUARAI_USER= +TOPSAPP_GUARAI_PASS= + +# ========================================================== +# Configurações TopSapp - Instância Pedro Afonso +# ========================================================== +TOPSAPP_PEDRO_AFONSO_USER=rodrigo +TOPSAPP_PEDRO_AFONSO_PASS=34641747 + # ========================================================== # Configurações GenieACS (TR-069 ACS) # ========================================================== diff --git a/src/bot/handler.js b/src/bot/handler.js index f3c55ee..9bb2b9f 100644 --- a/src/bot/handler.js +++ b/src/bot/handler.js @@ -5,6 +5,7 @@ const FiberhomeManager = require('../fiberhome/manager'); const FiberhomeTL1Manager = require('../fiberhome/manager_tl1'); const { GenieACSManager } = require('../genieacs'); const config = require('../config/config'); +const sites = require('../config/sites'); const logger = require('../utils/logger'); const DateProcessor = require('../utils/dateProcessor'); const cgnatParser = require('../cgn_list/parser'); @@ -23,19 +24,89 @@ class BotHandler { this.suporteSessions = new Map(); // Armazena sessões de suporte em andamento this.pendingRestarts = new Map(); // Armazena dados de reinicialização pendentes this._restartIdCounter = 0; - + this.activeSite = new Map(); // chatId -> siteId (cidade/provedor ativo) + logger.info('BotHandler inicializado'); console.log('🎯 BotHandler inicializado'); } /** - * Verifica se o usuário tem permissão + * Verifica se o usuário tem permissão (em pelo menos uma cidade/provedor) */ isAuthorized(userId) { - if (config.bot.allowedUsers.length === 0) { - return true; // Se não há lista de usuários, permite todos + return sites.getUserSiteIds(userId).length > 0; + } + + /** + * Cidade/provedor ativo para este chat. Pode ser null se ainda não + * foi resolvido (usuário com acesso a mais de um site e que ainda não + * escolheu via menu). + */ + getActiveSiteId(chatId) { + return this.activeSite.get(chatId) || null; + } + + /** + * Site (objeto completo) ativo para este chat, com fallback seguro + * para Guaraí caso ainda não haja site resolvido (mantém o + * comportamento histórico do bot, que sempre assumiu Guaraí). + */ + getActiveSite(chatId) { + return sites.getSite(this.getActiveSiteId(chatId)) || sites.getSite('guarai'); + } + + /** + * Label de localização usado pelas heurísticas existentes do TopSapp + * (substitui a antiga detecção por regex no texto da mensagem). + */ + getActiveLocation(chatId) { + return this.getActiveSite(chatId).topsappLocationLabel; + } + + /** + * Garante que o usuário tenha uma cidade/provedor ativa antes de + * processar qualquer ação. Se o usuário tiver acesso a mais de um + * site e ainda não tiver escolhido, exibe o menu de seleção e + * retorna null (o chamador deve interromper o processamento). + */ + async resolveActiveSite(chatId, userId) { + const siteIds = sites.getUserSiteIds(userId); + + if (siteIds.length === 0) { + return null; } - return config.bot.allowedUsers.includes(userId); + + if (siteIds.length === 1) { + this.activeSite.set(chatId, siteIds[0]); + return siteIds[0]; + } + + const current = this.activeSite.get(chatId); + if (current && siteIds.includes(current)) { + return current; + } + + await this.sendSiteSelectionMenu(chatId, siteIds); + return null; + } + + /** + * Exibe o menu de seleção de cidade/provedor + */ + async sendSiteSelectionMenu(chatId, siteIds) { + const keyboard = siteIds.map(id => { + const site = sites.getSite(id); + return [{ text: `📍 ${site.name}`, callback_data: `select_site_${site.id}` }]; + }); + + await this.bot.sendMessage(chatId, + `🏙️ *Selecione a cidade/provedor*\n\n` + + `Você tem acesso a mais de uma cidade. Escolha qual deseja atender agora:`, + { + parse_mode: 'Markdown', + reply_markup: { inline_keyboard: keyboard } + } + ); } /** @@ -64,6 +135,12 @@ class BotHandler { return; } + // Resolver cidade/provedor ativo (exibe menu se houver mais de um e ainda não escolhido) + const siteId = await this.resolveActiveSite(chatId, userId); + if (!siteId) { + return; + } + try { // Verificar se há uma sessão de suporte ativa const processouSuporte = await this.processarMensagemSuporte(msg); @@ -124,9 +201,13 @@ class BotHandler { break; case 'help': - await this.handleHelp(chatId); + await this.handleHelp(chatId, userId); break; - + + case 'cidade': + await this.handleCidade(chatId, userId); + break; + case 'ssh': await this.handleSSH(chatId, target); break; @@ -352,12 +433,30 @@ Digite /help para ver todos os comandos disponíveis! await this.bot.sendMessage(chatId, welcomeMessage, { parse_mode: 'Markdown' }); } + /** + * Comando /cidade - permite trocar a cidade/provedor ativo + */ + async handleCidade(chatId, userId) { + const siteIds = sites.getUserSiteIds(userId); + + if (siteIds.length <= 1) { + const site = sites.getSite(siteIds[0]) || this.getActiveSite(chatId); + await this.bot.sendMessage(chatId, `📍 Você só tem acesso a *${site.name}*.`, { parse_mode: 'Markdown' }); + return; + } + + await this.sendSiteSelectionMenu(chatId, siteIds); + } + /** * Comando /help */ - async handleHelp(chatId) { + async handleHelp(chatId, userId = null) { + const siteName = this.getActiveSite(chatId).name; const helpMessage = `🤖 **BOT DE AJUDA TÉCNICA** +📍 Cidade/provedor ativo: *${siteName}* + 🔧 **COMANDOS PRINCIPAIS:** • /start - Inicia o bot • /help - Esta ajuda @@ -408,7 +507,12 @@ Digite /help para ver todos os comandos disponíveis! { text: '🛡️ Admin', callback_data: 'help_admin' } ] ]; - + + if (userId && sites.getUserSiteIds(userId).length > 1) { + keyboard.push([{ text: '🏙️ Trocar cidade', callback_data: 'open_site_menu' }]); + } + + await this.bot.sendMessage(chatId, helpMessage, { parse_mode: 'Markdown', reply_markup: { @@ -431,7 +535,7 @@ Digite /help para ver todos os comandos disponíveis! switch (helpType) { case 'main': - await this.handleHelp(chatId); + await this.handleHelp(chatId, callbackQuery.from.id); return; case 'topsapp': @@ -657,7 +761,7 @@ Digite /help para ver todos os comandos disponíveis! * Listar equipamentos */ async handleListEquipments(chatId) { - const equipments = this.ssh.listEquipments(); + const equipments = this.ssh.listEquipments(this.getActiveSite(chatId).id); if (equipments.length === 0) { await this.bot.sendMessage(chatId, '📋 Nenhum equipamento configurado.'); @@ -830,8 +934,8 @@ Digite /help para ver todos os comandos disponíveis! await this.bot.sendMessage(chatId, `🔄 Desconectando usuário ${username} do concentrador...`); try { - // Usar concentrador Juniper como padrão - const result = await this.ssh.disconnectJuniperUser('juniper', username); + const juniperId = this.getActiveSite(chatId).juniperEquipmentId; + const result = await this.ssh.disconnectJuniperUser(juniperId, username); if (result.success) { await this.bot.sendMessage(chatId, `✅ ${result.message}`); @@ -857,11 +961,12 @@ Digite /help para ver todos os comandos disponíveis! /** * Listar usuários conectados no concentrador */ - async handleListUsers(chatId, target = 'juniper') { + async handleListUsers(chatId) { await this.bot.sendMessage(chatId, '🔍 Listando usuários conectados no concentrador...'); try { - const result = await this.ssh.listJuniperUsers(target); + const juniperId = this.getActiveSite(chatId).juniperEquipmentId; + const result = await this.ssh.listJuniperUsers(juniperId); if (result.success) { let message = '📋 *Usuários Conectados:*\n\n'; @@ -926,7 +1031,8 @@ Digite /help para ver todos os comandos disponíveis! await this.bot.sendMessage(chatId, `🔍 Consultando usuário *${username}* no concentrador...`, { parse_mode: 'Markdown' }); try { - const result = await this.ssh.getJuniperSubscriberInfo('juniper', username); + const juniperId = this.getActiveSite(chatId).juniperEquipmentId; + const result = await this.ssh.getJuniperSubscriberInfo(juniperId, username); if (!result.success) { await this.bot.sendMessage(chatId, `❌ ${result.message}`); @@ -972,7 +1078,7 @@ Digite /help para ver todos os comandos disponíveis! try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); - let location = ''; + let location = this.getActiveLocation(chatId); let searchQuery = query; if (/guarai|colmeia/i.test(query)) { @@ -1128,13 +1234,15 @@ Digite /help para ver todos os comandos disponíveis! // Executar consultas em paralelo: Juniper + ONU + GenieACS (por TAG/login) // No concentrador Juniper usa-se só o usuário (antes do @) + const activeSite = this.getActiveSite(chatId); + const juniperId = activeSite.juniperEquipmentId; const pppoeUser = pppoeLogin ? pppoeLogin.split('@')[0] : null; let [juniperResult, onuResult, genieResult] = await Promise.all([ pppoeUser - ? this.ssh.getJuniperSubscriberInfo('juniper', pppoeUser).catch(e => ({ success: false, error: e.message })) + ? this.ssh.getJuniperSubscriberInfo(juniperId, pppoeUser).catch(e => ({ success: false, error: e.message })) : Promise.resolve({ success: false, skipped: true }), serial - ? this._diagnosticarONU(serial, baseInfo).catch(e => ({ success: false, error: e.message })) + ? this._diagnosticarONU(serial, baseInfo, activeSite.id).catch(e => ({ success: false, error: e.message })) : Promise.resolve({ success: false, skipped: true }), pppoeLogin && this.genieacs.isReady() ? this.genieacs.buscarRoteador(pppoeLogin).catch(() => ({ success: false })) @@ -1144,7 +1252,7 @@ Digite /help para ver todos os comandos disponíveis! // Fallback: se PPPoE não encontrou sessão ativa e há circuit-id (IPoE), buscar pelo circuit-id if (circuitId && (!juniperResult.connected)) { try { - const circuitResult = await this.ssh.getJuniperSubscriberInfo('juniper', circuitId); + const circuitResult = await this.ssh.getJuniperSubscriberInfo(juniperId, circuitId); if (circuitResult.success && circuitResult.connected) { juniperResult = { ...circuitResult, viaCircuitId: true }; } @@ -1286,11 +1394,13 @@ Digite /help para ver todos os comandos disponíveis! /** * Mapeia o campo base/AP do TopSapp para o ID da OLT Datacom (ou 'fiberhome') + * As bases DITO/COLMEIA/CANAA só existem em Guaraí; em outro site, ignora a dica. */ - _resolverOLTporBase(baseInfo) { + _resolverOLTporBase(baseInfo, siteId = 'guarai') { if (!baseInfo) return null; const b = String(baseInfo).toUpperCase(); if (/INTERNET\s+FIBRA\s+OPTICA/i.test(b)) return 'fiberhome'; + if (siteId !== 'guarai') return null; if (/COLMEIA/i.test(b)) return 'olt_colmeia'; if (/DITO/i.test(b)) return 'olt_dito'; if (/CANAA\s*2/i.test(b)) return 'olt_canaa2'; @@ -1302,10 +1412,11 @@ Digite /help para ver todos os comandos disponíveis! * Diagnóstico de ONU: detecta tipo (Datacom/FiberHome) e retorna dados estruturados * @param {string} serial - Serial da ONU * @param {string|null} baseInfo - Campo base/AP do TopSapp para rotear para OLT correta + * @param {string} siteId - Cidade/provedor ativo, usado para restringir a busca */ - async _diagnosticarONU(serial, baseInfo) { + async _diagnosticarONU(serial, baseInfo, siteId = 'guarai') { const serialUpper = serial.toUpperCase(); - const oltHint = this._resolverOLTporBase(baseInfo); + const oltHint = this._resolverOLTporBase(baseInfo, siteId); // Usar base description para decidir se é FiberHome if (oltHint === 'fiberhome' || @@ -1313,10 +1424,10 @@ Digite /help para ver todos os comandos disponíveis! return await this._diagnosticarONUFiberhome(serial); } - return await this._diagnosticarONUDatacom(serial, oltHint); + return await this._diagnosticarONUDatacom(serial, oltHint, siteId); } - async _diagnosticarONUDatacom(serial, oltIdHint) { + async _diagnosticarONUDatacom(serial, oltIdHint, siteId = 'guarai') { let found = null; // 1. Tentar na OLT indicada pelo campo base/AP do cadastro @@ -1328,9 +1439,9 @@ Digite /help para ver todos os comandos disponíveis! } } - // 2. Fallback: buscar em todas as OLTs Datacom + // 2. Fallback: buscar em todas as OLTs Datacom do site ativo if (!found) { - const olts = this.ssh.listOLTs(); + const olts = this.ssh.listOLTs(siteId); if (olts.length === 0) { return { success: false, error: 'Nenhuma OLT Datacom configurada' }; } @@ -1445,8 +1556,9 @@ Digite /help para ver todos os comandos disponíveis! return; } - // Obter lista de OLTs disponíveis - const olts = this.ssh.listOLTs(); + // Obter lista de OLTs disponíveis no site ativo + const activeSiteId = this.getActiveSite(chatId).id; + const olts = this.ssh.listOLTs(activeSiteId); console.log(`📋 OLTs Datacom encontradas: ${olts.length}`); // Criar menu de seleção de OLT @@ -1454,10 +1566,12 @@ Digite /help para ver todos os comandos disponíveis! inline_keyboard: [] }; - // Adicionar OLT Fiberhome como primeira opção - keyboard.inline_keyboard.push([ - { text: '🔶 OLT FIBERHOME', callback_data: `check_signal_fiberhome_${serialNumber}` } - ]); + // Adicionar OLT Fiberhome como primeira opção (existe apenas em Guaraí) + if (activeSiteId === 'guarai') { + keyboard.inline_keyboard.push([ + { text: '🔶 OLT FIBERHOME', callback_data: `check_signal_fiberhome_${serialNumber}` } + ]); + } // Adicionar separador visual if (olts.length > 0) { @@ -1579,9 +1693,9 @@ Digite /help para ver todos os comandos disponíveis! return; } - // Perguntar em qual OLT buscar - const oltOptions = this.ssh.listOLTs(); - + // Perguntar em qual OLT buscar (apenas OLTs do site ativo) + const oltOptions = this.ssh.listOLTs(this.getActiveSite(chatId).id); + if (oltOptions.length === 0) { await this.bot.sendMessage(chatId, '❌ Nenhuma OLT disponível configurada.'); return; @@ -1620,9 +1734,9 @@ Digite /help para ver todos os comandos disponíveis! return; } - // Perguntar em qual OLT adicionar - const oltOptions = this.ssh.listOLTs(); - + // Perguntar em qual OLT adicionar (apenas OLTs do site ativo) + const oltOptions = this.ssh.listOLTs(this.getActiveSite(chatId).id); + if (oltOptions.length === 0) { await this.bot.sendMessage(chatId, '❌ Nenhuma OLT disponível configurada.'); return; @@ -1666,9 +1780,9 @@ Digite /help para ver todos os comandos disponíveis! return; } - // Perguntar em qual OLT adicionar - const oltOptions = this.ssh.listOLTs(); - + // Perguntar em qual OLT adicionar (apenas OLTs do site ativo) + const oltOptions = this.ssh.listOLTs(this.getActiveSite(chatId).id); + if (oltOptions.length === 0) { await this.bot.sendMessage(chatId, '❌ Nenhuma OLT disponível configurada.'); return; @@ -2004,6 +2118,35 @@ Digite /help para ver todos os comandos disponíveis! return; } + // Seleção/troca de cidade — tratado antes de resolver o site ativo, + // pois é a própria ação que define o site ativo. + if (data.startsWith('select_site_')) { + const siteId = data.replace('select_site_', ''); + const site = sites.getSite(siteId); + if (!site || !sites.getUserSiteIds(userId).includes(siteId)) { + await this.bot.answerCallbackQuery(callbackQuery.id, '🚫 Cidade inválida'); + return; + } + this.activeSite.set(chatId, siteId); + await this.bot.answerCallbackQuery(callbackQuery.id, `📍 ${site.name}`); + await this.bot.sendMessage(chatId, `📍 Cidade/provedor definido: *${site.name}*`, { parse_mode: 'Markdown' }); + await this.handleHelp(chatId, userId); + return; + } + + if (data === 'open_site_menu') { + await this.bot.answerCallbackQuery(callbackQuery.id); + await this.sendSiteSelectionMenu(chatId, sites.getUserSiteIds(userId)); + return; + } + + // Garante que haja uma cidade/provedor ativa antes de qualquer outra ação + const activeSiteId = await this.resolveActiveSite(chatId, userId); + if (!activeSiteId) { + await this.bot.answerCallbackQuery(callbackQuery.id); + return; + } + try { console.log(`🔍 Callback recebido: ${data}`); console.log(`🔍 Verifica confirm_remove_: ${data.startsWith('confirm_remove_')}`); @@ -4250,7 +4393,7 @@ Digite /help para ver todos os comandos disponíveis! await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); // Detectar se especificou uma localização - let location = ''; + let location = this.getActiveLocation(chatId); let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { @@ -4386,7 +4529,7 @@ Digite /help para ver todos os comandos disponíveis! await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); // Detectar se especificou uma localização - let location = ''; + let location = this.getActiveLocation(chatId); let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { @@ -4496,7 +4639,7 @@ Digite /help para ver todos os comandos disponíveis! await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); // Detectar se especificou uma localização - let location = ''; + let location = this.getActiveLocation(chatId); let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { @@ -4617,7 +4760,7 @@ Digite /help para ver todos os comandos disponíveis! await this.bot.sendMessage(chatId, '🔍 Buscando serviços...'); // Detectar se especificou uma localização - let location = ''; + let location = this.getActiveLocation(chatId); let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { @@ -4994,7 +5137,7 @@ Digite /help para ver todos os comandos disponíveis! try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); - let location = ''; + let location = this.getActiveLocation(chatId); let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { @@ -5723,7 +5866,7 @@ Digite /help para ver todos os comandos disponíveis! try { // Primeiro, buscar o cliente - let location = ''; + let location = this.getActiveLocation(chatId); let query = target; if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { @@ -6859,7 +7002,7 @@ Digite /help para ver todos os comandos disponíveis! } session.dados.clienteSelecionado = session.dados.clientesEncontrados[numero - 1]; - session.dados.location = ''; + session.dados.location = this.getActiveLocation(session.chatId); await this.processarEtapaFecharBuscarSuportes(sessionId); } @@ -7519,7 +7662,7 @@ Digite /help para ver todos os comandos disponíveis! } session.dados.clienteSelecionado = clientes[index]; - session.dados.location = ''; + session.dados.location = this.getActiveLocation(chatId); await this.processarEtapaFecharBuscarSuportes(sessionId); } @@ -8232,7 +8375,7 @@ Digite /help para ver todos os comandos disponíveis! try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); - let location = ''; + let location = this.getActiveLocation(chatId); let query = target; if (/guarai|colmeia/i.test(target)) { @@ -8601,7 +8744,7 @@ Digite /help para ver todos os comandos disponíveis! try { await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); - let location = ''; + let location = this.getActiveLocation(chatId); let searchQuery = query; if (/guarai|colmeia/i.test(query)) { @@ -8721,7 +8864,7 @@ Digite /help para ver todos os comandos disponíveis! return; } - const oltHint = this._resolverOLTporBase(base); + const oltHint = this._resolverOLTporBase(base, this.getActiveSite(chatId).id); const tipoOLT = oltHint === 'fiberhome' ? '🔶 Fiberhome (TL1)' : '🔷 Datacom (SSH)'; const serialEscaped = serial.replace(/_/g, '\\_'); @@ -8812,7 +8955,8 @@ Digite /help para ver todos os comandos disponíveis! this.pendingRestarts.delete(restartId); const { serial, base } = pending; - const oltHint = this._resolverOLTporBase(base); + const siteId = this.getActiveSite(chatId).id; + const oltHint = this._resolverOLTporBase(base, siteId); const serialEscaped = serial.replace(/_/g, '\\_'); await this.bot.editMessageText( @@ -8839,7 +8983,7 @@ Digite /help para ver todos os comandos disponíveis! // Sem base definida — tentar Fiberhome primeiro, depois Datacom result = await this.fiberhomeTL1.reiniciarOnu(serial); if (!result.success) { - result = await this._reiniciarOnuDatacomAutoDetect(serial); + result = await this._reiniciarOnuDatacomAutoDetect(serial, siteId); } } @@ -9176,8 +9320,8 @@ Digite /help para ver todos os comandos disponíveis! /** * Reiniciar ONU Datacom — busca em todas as OLTs */ - async _reiniciarOnuDatacomAutoDetect(serial) { - const olts = this.ssh.listOLTs(); + async _reiniciarOnuDatacomAutoDetect(serial, siteId = 'guarai') { + const olts = this.ssh.listOLTs(siteId); for (const olt of olts) { try { const findResult = await this.ssh.findOnuBySerial(olt.id, serial); diff --git a/src/config/sites.js b/src/config/sites.js new file mode 100644 index 0000000..cd04a15 --- /dev/null +++ b/src/config/sites.js @@ -0,0 +1,78 @@ +/** + * Registro central de sites (cidades/provedores) atendidos pelo bot. + * Para adicionar uma nova cidade: acrescente um objeto aqui e configure a + * variável de ambiente correspondente (allowedUsersEnv) — nenhum outro + * módulo precisa saber quantos sites existem. + */ +const SITES = [ + { + id: 'guarai', + name: 'Guaraí', + allowedUsersEnv: 'GUARAI_ALLOWED_USERS', + topsappKey: 'guarai_colmeia', + topsappLocationLabel: 'Guarai/Colmeia', + juniperEquipmentId: 'juniper', + oltEquipmentIds: ['olt_dito', 'olt_colmeia', 'olt_canaa', 'olt_canaa2'] + }, + { + id: 'pedro_afonso', + name: 'Pedro Afonso', + allowedUsersEnv: 'PEDRO_AFONSO_ALLOWED_USERS', + topsappKey: 'pedro_afonso', + topsappLocationLabel: 'Pedro Afonso', + juniperEquipmentId: 'juniper_pedro_afonso', + oltEquipmentIds: ['olt_pedro_afonso', 'olt_canavieiras'] + } +]; + +function parseUserIds(envValue) { + if (!envValue) return []; + return envValue.split(',').map(id => parseInt(id.trim(), 10)).filter(id => !isNaN(id)); +} + +function getAllSites() { + return SITES; +} + +function getSite(id) { + return SITES.find(site => site.id === id) || null; +} + +/** + * Retorna os ids dos sites aos quais o usuário do Telegram tem acesso. + * - Quem está em ALLOWED_USERS (config global) tem acesso a todos os sites. + * - Caso contrário, cada site é liberado se o userId estiver na env var + * `site.allowedUsersEnv` correspondente. + * - Se nenhuma das variáveis (global ou por site) estiver configurada, + * mantém o comportamento histórico de "sem lista = libera todo mundo", + * liberando acesso a todos os sites. + */ +function getUserSiteIds(userId) { + const config = require('./config'); + const globalUsers = config.bot.allowedUsers; + + if (globalUsers.length > 0 && globalUsers.includes(userId)) { + return SITES.map(site => site.id); + } + + const siteAllowLists = SITES.map(site => parseUserIds(process.env[site.allowedUsersEnv])); + const anyPerSiteListConfigured = siteAllowLists.some(list => list.length > 0); + + if (globalUsers.length === 0 && !anyPerSiteListConfigured) { + return SITES.map(site => site.id); + } + + const result = []; + SITES.forEach((site, index) => { + if (siteAllowLists[index].includes(userId)) { + result.push(site.id); + } + }); + return result; +} + +module.exports = { + getAllSites, + getSite, + getUserSiteIds +}; diff --git a/src/ssh/manager.js b/src/ssh/manager.js index 5d2ae5d..da3e603 100644 --- a/src/ssh/manager.js +++ b/src/ssh/manager.js @@ -22,51 +22,41 @@ class SSHManager { }); } - // Concentrador Juniper principal - this.equipamentos.set('juniper', { + // Concentrador Juniper - Guaraí + const juniperGuarai = { name: 'Concentrador Juniper', - host: '131.0.192.254', - username: 'rodrigo', - password: '@102030Rod', + host: process.env.JUNIPER_GUARAI_HOST || '131.0.192.254', + username: process.env.JUNIPER_GUARAI_USER || 'rodrigo', + password: process.env.JUNIPER_GUARAI_PASS || '', port: 22, - type: 'juniper' - }); + type: 'juniper', + site: 'guarai' + }; + this.equipamentos.set('juniper', juniperGuarai); + this.equipamentos.set('concentrador', juniperGuarai); // alias - // Aliases para o concentrador - this.equipamentos.set('concentrador', { - name: 'Concentrador Juniper', - host: '131.0.192.254', - username: 'rodrigo', - password: '@102030Rod', + // Concentrador Juniper - Pedro Afonso + const juniperPedroAfonso = { + name: 'Concentrador Juniper Pedro Afonso', + host: process.env.JUNIPER_PEDRO_AFONSO_HOST || '45.189.140.2', + username: process.env.JUNIPER_PEDRO_AFONSO_USER || 'rodrigo', + password: process.env.JUNIPER_PEDRO_AFONSO_PASS || '', port: 22, - type: 'juniper' - }); + type: 'juniper', + site: 'pedro_afonso' + }; + this.equipamentos.set('juniper_pedro_afonso', juniperPedroAfonso); + this.equipamentos.set('concentrador_pedro_afonso', juniperPedroAfonso); // alias - // Exemplos de equipamentos (você pode configurar mais) - this.equipamentos.set('router1', { - name: 'Router Principal', - host: '192.168.1.1', - username: 'admin', - password: 'admin', - port: 22 - }); - - this.equipamentos.set('switch1', { - name: 'Switch Core', - host: '192.168.1.10', - username: 'admin', - password: 'admin', - port: 22 - }); - - // OLTs para verificação de sinal + // OLTs Guaraí this.equipamentos.set('olt_dito', { name: 'OLT DITO', host: '192.168.190.7', username: 'admin', password: 'admin', port: 22, - type: 'olt' + type: 'olt', + site: 'guarai' }); this.equipamentos.set('olt_colmeia', { @@ -75,7 +65,8 @@ class SSHManager { username: 'admin', password: 'admin', port: 22, - type: 'olt' + type: 'olt', + site: 'guarai' }); this.equipamentos.set('olt_canaa', { @@ -84,7 +75,8 @@ class SSHManager { username: 'admin', password: 'admin', port: 22, - type: 'olt' + type: 'olt', + site: 'guarai' }); this.equipamentos.set('olt_canaa2', { @@ -93,16 +85,19 @@ class SSHManager { username: 'admin', password: 'admin', port: 22, - type: 'olt' + type: 'olt', + site: 'guarai' }); + // OLTs Pedro Afonso this.equipamentos.set('olt_pedro_afonso', { name: 'OLT PEDRO AFONSO 1', host: '192.168.100.2', username: 'admin', password: 'admin', port: 22, - type: 'olt' + type: 'olt', + site: 'pedro_afonso' }); this.equipamentos.set('olt_canavieiras', { @@ -111,7 +106,8 @@ class SSHManager { username: 'admin', password: 'admin', port: 22, - type: 'olt' + type: 'olt', + site: 'pedro_afonso' }); } @@ -259,10 +255,11 @@ class SSHManager { /** * Lista equipamentos disponíveis */ - listEquipments() { + listEquipments(siteId = null) { const equipments = []; - + for (const [id, equipment] of this.equipamentos) { + if (siteId && equipment.site !== siteId) continue; equipments.push({ id: id, name: equipment.name, @@ -270,7 +267,7 @@ class SSHManager { port: equipment.port || 22 }); } - + return equipments; } @@ -696,20 +693,20 @@ class SSHManager { * Lista todos os equipamentos OLT disponíveis * @returns {Array} - Array com OLTs disponíveis */ - listOLTs() { + listOLTs(siteId = null) { const olts = []; - + for (const [id, equipment] of this.equipamentos) { - if (equipment.type === 'olt') { - olts.push({ - id: id, - name: equipment.name, - host: equipment.host, - port: equipment.port || 22 - }); - } + if (equipment.type !== 'olt') continue; + if (siteId && equipment.site !== siteId) continue; + olts.push({ + id: id, + name: equipment.name, + host: equipment.host, + port: equipment.port || 22 + }); } - + return olts; } diff --git a/src/topsapp/config.js b/src/topsapp/config.js index e748cbe..39f5b0c 100644 --- a/src/topsapp/config.js +++ b/src/topsapp/config.js @@ -48,6 +48,16 @@ class TopSappConfig { return this.credentials[instanceKey] || null; } + /** + * Obter credenciais a partir do site ativo (cidade/provedor) + */ + getCredentialsBySite(siteId) { + const sites = require('../config/sites'); + const site = sites.getSite(siteId); + if (!site) return this.credentials.guarai_colmeia; + return this.credentials[site.topsappKey] || null; + } + /** * Obter credenciais por localização */