diff --git a/src/bot/handler.js b/src/bot/handler.js index 137b808..eafab75 100644 --- a/src/bot/handler.js +++ b/src/bot/handler.js @@ -1070,6 +1070,20 @@ Digite /help para ver todos os comandos disponíveis! : Promise.resolve({ success: false, skipped: true }) ]); + // Sincronização TAG GenieACS ↔ PPPoE TopSapp (fire-and-forget, silencioso) + // Garante que o login PPPoE do cliente fica registrado como TAG no GenieACS, + // permitindo buscas futuras por "ver roteador ". + if (serial && pppoeLogin && this.genieacs.isReady()) { + this.genieacs.sincronizarTagPPPoE(serial, pppoeLogin) + .then(r => { + if (r.success && !r.already) { + logger.info(`[GenieACS] TAG "${r.tag}" sincronizada em ${r.deviceId}`); + console.log(`🏷️ [GenieACS] TAG "${r.tag}" adicionada ao device ${r.deviceId}`); + } + }) + .catch(e => logger.warn(`[GenieACS] Falha ao sincronizar TAG: ${e.message}`)); + } + // Montar relatório let msg = `🩺 *Diagnóstico do Cliente*\n`; msg += `━━━━━━━━━━━━━━━━━━━━━\n\n`; diff --git a/src/genieacs/api.js b/src/genieacs/api.js index ec420ea..401a376 100644 --- a/src/genieacs/api.js +++ b/src/genieacs/api.js @@ -158,6 +158,34 @@ class GenieACSApi { return this._buscar({ [`Tags.${tag.toLowerCase()}`]: true }, instance, 'tag'); } + /** + * Adiciona uma TAG a um dispositivo na NBI do GenieACS. + * Endpoint: POST /devices/{id}/tags/{tag} + * + * @param {string} deviceId - _id do dispositivo (ex.: "00E0FC-Huawei-VMXQU22716608217") + * @param {string} tag - Tag a adicionar (ex.: "clickcolmeia2025") + * @param {Object} instance - Instância de GenieACSConfig + * @returns {Promise} - { success, error } + */ + async adicionarTag(deviceId, tag, instance) { + const client = this._buildClient(instance); + const encodedId = encodeURIComponent(deviceId); + const encodedTag = encodeURIComponent(tag); + + try { + await client.post(`/devices/${encodedId}/tags/${encodedTag}`); + logger.info(`[GenieACS] TAG "${tag}" adicionada ao device ${deviceId}`); + return { success: true }; + } catch (error) { + const status = error.response?.status; + const msg = status + ? `Erro HTTP ${status} ao adicionar TAG "${tag}" (${instance.name})` + : `Falha ao adicionar TAG: ${error.message}`; + logger.error(`[GenieACS] ${msg}`); + return { success: false, error: msg }; + } + } + /** * Extrai os campos relevantes de um dispositivo bruto retornado pela NBI. * @param {Object} raw - Objeto bruto do GenieACS diff --git a/src/genieacs/manager.js b/src/genieacs/manager.js index 0a55be5..43463f6 100644 --- a/src/genieacs/manager.js +++ b/src/genieacs/manager.js @@ -20,6 +20,7 @@ class GenieACSManager { /** * Busca informações de um roteador/CPE pelo serial number ou pela TAG (username TopSapp). * Tenta por serial primeiro; se não encontrar, tenta por TAG. + * Se o termo contém "@", também tenta a parte antes do @ como TAG. * * @param {string} termo - Serial ou TAG do dispositivo * @param {string} location - Texto de localização (opcional) @@ -36,20 +37,31 @@ class GenieACSManager { const instance = this.config.getInstanceByLocation(location); const termoLimpo = termo.trim(); - // 1ª tentativa: busca por serial + // 1ª tentativa: busca por serial (exato e uppercase) logger.info(`[GenieACS] Tentando como serial: "${termoLimpo}"`); const porSerial = await this.api.buscarDispositivo(termoLimpo, instance); if (porSerial.success) { return { ...porSerial, instance: instance.name }; } - // 2ª tentativa: busca por TAG (usuario/login TopSapp) + // 2ª tentativa: busca por TAG (termo completo) logger.info(`[GenieACS] Não encontrado como serial. Tentando como TAG: "${termoLimpo}"`); const porTag = await this.api.buscarPorTag(termoLimpo, instance); if (porTag.success) { return { ...porTag, instance: instance.name }; } + // 3ª tentativa: se o termo tem "@", tentar só a parte antes do @ como TAG + // (ex.: "clickcolmeia2025@isp" -> tenta "clickcolmeia2025") + if (termoLimpo.includes('@')) { + const tagBase = termoLimpo.split('@')[0].toLowerCase(); + logger.info(`[GenieACS] Tentando TAG sem domínio: "${tagBase}"`); + const porTagBase = await this.api.buscarPorTag(tagBase, instance); + if (porTagBase.success) { + return { ...porTagBase, instance: instance.name }; + } + } + // Nenhum resultado const errConexao = porSerial.error || porTag.error; return { @@ -57,6 +69,52 @@ class GenieACSManager { error: errConexao || `Nenhum dispositivo encontrado com serial ou TAG "${termoLimpo}"` }; } + + /** + * Sincroniza o login PPPoE do TopSapp como TAG no GenieACS. + * + * Fluxo: + * 1. Busca o dispositivo pelo serial (vindo do TopSapp) + * 2. Deriva a TAG do login PPPoE (parte antes do "@", em minúsculo) + * 3. Se a TAG ainda não existir no dispositivo, adiciona via NBI + * + * @param {string} serial - Serial do dispositivo (do cadastro TopSapp) + * @param {string} pppoeLogin - Login PPPoE do cliente (ex.: "clickcolmeia2025" ou "user@isp") + * @param {string} location - Texto de localização (opcional) + * @returns {Promise} - { success, tag, deviceId, already, error } + */ + async sincronizarTagPPPoE(serial, pppoeLogin, location = '') { + if (!this.isReady() || !serial || !pppoeLogin) { + return { success: false, skipped: true }; + } + + const instance = this.config.getInstanceByLocation(location); + + // A TAG usa apenas a parte antes do "@", em minúsculo, + // igualando ao padrão que o GenieACS já exibe na UI (ex.: "clickcolmeia2025") + const tag = pppoeLogin.split('@')[0].toLowerCase(); + + // Localizar dispositivo pelo serial + const result = await this.api.buscarDispositivo(serial.trim(), instance); + if (!result.success || !result.device?.deviceId) { + logger.warn(`[GenieACS] Sync TAG: dispositivo serial "${serial}" não encontrado`); + return { success: false, error: 'Dispositivo não encontrado pelo serial' }; + } + + const deviceId = result.device.deviceId; + const tagsAtuais = result.device.tags || []; + + // Verificar se a tag já está presente (case-insensitive) + if (tagsAtuais.some(t => t.toLowerCase() === tag)) { + logger.info(`[GenieACS] Sync TAG: "${tag}" já existe em ${deviceId}`); + return { success: true, already: true, tag, deviceId }; + } + + // Adicionar a TAG + logger.info(`[GenieACS] Sync TAG: adicionando "${tag}" em ${deviceId}`); + const addResult = await this.api.adicionarTag(deviceId, tag, instance); + return { ...addResult, tag, deviceId }; + } } module.exports = GenieACSManager;