From b794418d81856a7ed9df0727641bcf6067e5efd0 Mon Sep 17 00:00:00 2001 From: Rodrigo Bertoldo de Sousa Martins Date: Fri, 12 Dec 2025 12:17:16 -0300 Subject: [PATCH] TopSapp: fix API show blocked services from client Signed-off-by: Rodrigo Bertoldo de Sousa Martins --- .github/copilot-instructions.md | 48 + src/bot/handler.js | 2579 +++++++++++-------------------- src/topsapp/api.js | 77 +- src/topsapp/manager.js | 43 + 4 files changed, 1026 insertions(+), 1721 deletions(-) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..b026a91 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,48 @@ +# GitHub Copilot Instructions + +You are an expert senior software engineer focused on reliability, security, and clean architecture. Your primary goal is to implement exactly what the user requests without unnecessary bloat, ensuring zero regressions. + +## 1. Core Principles (Non-Negotiable) + +- **Safety First:** Never output code that could break the build, introduce security vulnerabilities, or delete existing functionality unless explicitly requested. +- **Minimal Footprint:** When generating files, aim for the smallest number of files necessary. Do not create multiple small files if a single, well-organized module suffices. Keep the file structure logical and flat where possible. +- **High Fidelity:** Implement *exactly* what is requested. Do not add "nice-to-have" features, speculative code, or extra dependencies unless they are critical for the requested functionality to work. +- **Idempotency:** Ensure that scripts and database migrations are idempotent (can be run multiple times without side effects). + +## 2. TopSapp API Integration + +**CRITICAL:** Whenever the user request involves interacting with the TopSapp system, you MUST consult the documentation located at: +`material/ManualApiTopSapp.txt` + +- **Do not guess endpoints.** Use only the endpoints, methods (GET/POST), and payload structures defined in that manual. +- **Error Handling:** Always implement robust error handling for API calls (timeouts, 4xx/5xx responses) based on the patterns found in the manual. +- **Authentication:** Ensure the correct authentication headers/tokens are used as described in the manual. + +## 3. Code Generation Rules + +### A. Refactoring & Modification +- When modifying existing files, preserve the existing coding style (indentation, naming conventions). +- **Context Awareness:** Read the surrounding code to understand the scope. Do not overwrite imports or global variables unless necessary. +- **Comments:** Add concise comments explaining *why* a complex change was made, not just *what* the code does. + +### B. New Features +- **Modularity:** Encapsulate new logic in functions or classes. Avoid polluting the global namespace. +- **Validation:** Always validate inputs before processing them, especially when dealing with external APIs like TopSapp. + +### C. Testing +- If asked to generate tests, prioritize integration tests for API calls (mocking the actual TopSapp API) and unit tests for business logic. + +## 4. Response Format + +- **Plan First:** Before generating code for complex tasks, briefly outline the files you will create or modify. +- **Complete Code:** Do not use placeholders like `// ... rest of code`. Output the full, functional block of code needed for the file. +- **Verification:** After generating code, review it against the `material/ManualApiTopSapp.txt` to ensure compliance. + +## 5. "Do No Harm" Checklist + +Before outputting the final response, ask yourself: +1. Does this break existing features? +2. Did I invent an API endpoint that doesn't exist in the manual? +3. Is this the simplest way to solve the problem? + +If the answer to 1 or 2 is "Yes", or 3 is "No", revise your solution immediately. diff --git a/src/bot/handler.js b/src/bot/handler.js index 3e5643f..fe42cb3 100644 --- a/src/bot/handler.js +++ b/src/bot/handler.js @@ -1689,7 +1689,7 @@ Digite /help para ver todos os comandos disponíveis! } /** - * Processa callback de atualizar firmware da ONU + * Processa callback de atualização de firmware da ONU */ async handleUpdateOnuCallback(callbackQuery, data) { const parts = data.split('_'); @@ -2649,6 +2649,815 @@ Digite /help para ver todos os comandos disponíveis! } } + /** + * Bloquear cliente + */ + async handleBloquearCliente(chatId, target) { + if (!target) { + await this.bot.sendMessage(chatId, + `🔒 **Bloquear Cliente**\n\n` + + `💡 **Como usar:**\n` + + `• "bloquear cliente João da Silva"\n` + + `• "bloquear cliente 123.456.789-10"\n` + + `• "bloquear Guarai João" (específica instância)` + ); + return; + } + + try { + await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); + + // Detectar se especificou uma localização + let location = ''; + let query = target; + + if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { + location = 'Guarai/Colmeia'; + query = target.replace(/(guarai|colmeia)/gi, '').trim(); + } else if (target.toLowerCase().includes('pedro afonso')) { + location = 'Pedro Afonso'; + query = target.replace(/pedro afonso/gi, '').trim(); + } + + const resultado = await this.topsapp.buscarCliente(query, location); + + if (!resultado.success) { + await this.bot.sendMessage(chatId, + `❌ **Erro na busca**\n\n` + + `⚠️ ${resultado.error}`, + { parse_mode: 'Markdown' } + ); + return; + } + + if (!resultado.clientes || resultado.clientes.length === 0) { + await this.bot.sendMessage(chatId, + `🔍 **Nenhum cliente encontrado**\n\n` + + `📝 Termo buscado: "${query}"\n` + + `🌐 Instância: ${resultado.instancia || 'Todas'}`, + { parse_mode: 'Markdown' } + ); + return; + } + + const cliente = resultado.clientes[0]; + + // Obter serviços do cliente para bloquear + const servicosResult = await this.topsapp.obterServicos(cliente.id, location); + + if (!servicosResult.success) { + await this.bot.sendMessage(chatId, + `❌ **Erro ao buscar serviços**\n\n` + + `⚠️ ${servicosResult.error}`, + { parse_mode: 'Markdown' } + ); + return; + } + + const servicos = servicosResult.data?.dados || servicosResult.data?.servicos || []; + const servicosAtivos = servicos.filter(s => { + const status = this.topsapp.api.interpretarStatusServico(s); + return status === 1; + }); + + if (servicosAtivos.length === 0) { + await this.bot.sendMessage(chatId, + `⚠️ **Nenhum serviço ativo**\n\n` + + `O cliente **${cliente.nome}** não possui serviços ativos para bloquear.`, + { parse_mode: 'Markdown' } + ); + return; + } + + let message = `🔒 **Bloquear Cliente**\n\n`; + message += `👤 **Cliente**: ${cliente.nome}\n`; + message += `🆔 **ID**: ${cliente.id}\n`; + message += `📊 **Serviços ativos**: ${servicosAtivos.length}\n\n`; + message += `⚠️ **Confirma o bloqueio de todos os serviços?**`; + + const keyboard = [ + [ + { text: '✅ Confirmar Bloqueio', callback_data: `confirm_block_all_${cliente.id}_${location}` }, + { text: '❌ Cancelar', callback_data: 'cancel_action' } + ] + ]; + + await this.bot.sendMessage(chatId, message, { + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: keyboard + } + }); + + } catch (error) { + console.error('❌ Erro ao bloquear cliente:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao bloquear cliente**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Liberar cliente + */ + async handleLiberarCliente(chatId, target) { + if (!target) { + await this.bot.sendMessage(chatId, + `🔓 **Liberar Cliente**\n\n` + + `💡 **Como usar:**\n` + + `• "liberar cliente João da Silva"\n` + + `• "liberar cliente 123.456.789-10"\n` + + `• "liberar Guarai João" (específica instância)` + ); + return; + } + + try { + await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); + + // Detectar se especificou uma localização + let location = ''; + let query = target; + + if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { + location = 'Guarai/Colmeia'; + query = target.replace(/(guarai|colmeia)/gi, '').trim(); + } else if (target.toLowerCase().includes('pedro afonso')) { + location = 'Pedro Afonso'; + query = target.replace(/pedro afonso/gi, '').trim(); + } + + const resultado = await this.topsapp.buscarCliente(query, location); + + if (!resultado.success) { + await this.bot.sendMessage(chatId, + `❌ **Erro na busca**\n\n` + + `⚠️ ${resultado.error}`, + { parse_mode: 'Markdown' } + ); + return; + } + + if (!resultado.clientes || resultado.clientes.length === 0) { + await this.bot.sendMessage(chatId, + `🔍 **Nenhum cliente encontrado**\n\n` + + `📝 Termo buscado: "${query}"\n` + + `🌐 Instância: ${resultado.instancia || 'Todas'}`, + { parse_mode: 'Markdown' } + ); + return; + } + + const cliente = resultado.clientes[0]; + + // Obter serviços do cliente para liberar + const servicosResult = await this.topsapp.obterServicos(cliente.id, location); + + if (!servicosResult.success) { + await this.bot.sendMessage(chatId, + `❌ **Erro ao buscar serviços**\n\n` + + `⚠️ ${servicosResult.error}`, + { parse_mode: 'Markdown' } + ); + return; + } + + const servicos = servicosResult.data?.dados || servicosResult.data?.servicos || []; + const servicosBloqueados = servicos.filter(s => { + const status = this.topsapp.api.interpretarStatusServico(s); + return status === 2; + }); + + if (servicosBloqueados.length === 0) { + await this.bot.sendMessage(chatId, + `⚠️ **Nenhum serviço bloqueado**\n\n` + + `O cliente **${cliente.nome}** não possui serviços bloqueados para liberar.`, + { parse_mode: 'Markdown' } + ); + return; + } + + let message = `🔓 **Liberar Cliente**\n\n`; + message += `👤 **Cliente**: ${cliente.nome}\n`; + message += `🆔 **ID**: ${cliente.id}\n`; + message += `📊 **Serviços bloqueados**: ${servicosBloqueados.length}\n\n`; + message += `⚠️ **Confirma a liberação de todos os serviços?**`; + + const keyboard = [ + [ + { text: '✅ Confirmar Liberação', callback_data: `confirm_unblock_all_${cliente.id}_${location}` }, + { text: '❌ Cancelar', callback_data: 'cancel_action' } + ] + ]; + + await this.bot.sendMessage(chatId, message, { + parse_mode: 'Markdown', + reply_markup: { + inline_keyboard: keyboard + } + }); + + } catch (error) { + console.error('❌ Erro ao liberar cliente:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao liberar cliente**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Listar serviços do cliente + */ + async handleListarServicos(chatId, target) { + if (!target) { + await this.bot.sendMessage(chatId, + `📋 **Listar Serviços**\n\n` + + `💡 **Como usar:**\n` + + `• "listar serviços João da Silva"\n` + + `• "serviços do cliente 123.456.789-10"\n` + + `• "serviços Guarai João" (específica instância)` + ); + return; + } + + try { + await this.bot.sendMessage(chatId, '🔍 Buscando serviços...'); + + // Detectar se especificou uma localização + let location = ''; + let query = target; + + if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { + location = 'Guarai/Colmeia'; + query = target.replace(/(guarai|colmeia)/gi, '').trim(); + } else if (target.toLowerCase().includes('pedro afonso')) { + location = 'Pedro Afonso'; + query = target.replace(/pedro afonso/gi, '').trim(); + } + + const resultado = await this.topsapp.buscarCliente(query, location); + + if (!resultado.success || !resultado.clientes || resultado.clientes.length === 0) { + await this.bot.sendMessage(chatId, + `🔍 **Nenhum cliente encontrado**\n\n` + + `📝 Termo buscado: "${query}"`, + { parse_mode: 'Markdown' } + ); + return; + } + + const cliente = resultado.clientes[0]; + + // Obter serviços do cliente + const servicosResult = await this.topsapp.obterServicos(cliente.id, location); + + if (!servicosResult.success) { + await this.bot.sendMessage(chatId, + `❌ **Erro ao buscar serviços**\n\n` + + `⚠️ ${servicosResult.error}`, + { parse_mode: 'Markdown' } + ); + return; + } + + const servicos = servicosResult.data?.dados || servicosResult.data?.servicos || []; + + if (servicos.length === 0) { + await this.bot.sendMessage(chatId, + `📋 **Nenhum serviço encontrado**\n\n` + + `O cliente **${cliente.nome}** não possui serviços cadastrados.`, + { parse_mode: 'Markdown' } + ); + return; + } + + // Separar serviços por status + const servicosAtivos = servicos.filter(s => { + const status = this.topsapp.api.interpretarStatusServico(s); + return status === 1; + }); + + const servicosBloqueados = servicos.filter(s => { + const status = this.topsapp.api.interpretarStatusServico(s); + return status === 2; + }); + + const servicosDesativados = servicos.filter(s => { + const status = this.topsapp.api.interpretarStatusServico(s); + return status === 3; + }); + + let message = `📋 **SERVIÇOS DO CLIENTE**\n\n`; + message += `👤 **Cliente**: ${cliente.nome}\n`; + message += `🆔 **ID**: ${cliente.id}\n\n`; + + // Exibir serviços ativos + if (servicosAtivos.length > 0) { + message += `✅ **SERVIÇOS ATIVOS** (${servicosAtivos.length})\n\n`; + servicosAtivos.forEach((servico, index) => { + message += `${index + 1}. 🟢 **${servico.descricao || servico.plano || 'Serviço'}**\n`; + message += ` 🆔 ID: \`${servico.id_servico || servico.id}\`\n`; + + if (servico.usuario) { + message += ` 👤 Login: \`${servico.usuario}\`\n`; + } + + if (servico.servico_valor || servico.valor) { + const valor = parseFloat(servico.servico_valor || servico.valor); + message += ` 💰 Valor: R$ ${valor.toFixed(2)}\n`; + } + + if (servico.vencimento) { + message += ` 📅 Vencimento: dia ${servico.vencimento}\n`; + } + + message += `\n`; + }); + } + + // Exibir serviços bloqueados + if (servicosBloqueados.length > 0) { + message += `\n🔒 **SERVIÇOS BLOQUEADOS** (${servicosBloqueados.length})\n\n`; + servicosBloqueados.forEach((servico, index) => { + message += `${index + 1}. 🔴 **${servico.descricao || servico.plano || 'Serviço'}**\n`; + message += ` 🆔 ID: \`${servico.id_servico || servico.id}\`\n`; + + if (servico.usuario) { + message += ` 👤 Login: \`${servico.usuario}\`\n`; + } + + if (servico.servico_valor || servico.valor) { + const valor = parseFloat(servico.servico_valor || servico.valor); + message += ` 💰 Valor: R$ ${valor.toFixed(2)}\n`; + } + + if (servico.data_bloqueio && servico.data_bloqueio !== '0000-00-00') { + message += ` 🕒 Bloqueado em: ${servico.data_bloqueio}\n`; + } + + message += `\n`; + }); + } + + // Exibir serviços desativados + if (servicosDesativados.length > 0) { + message += `\n❌ **SERVIÇOS DESATIVADOS** (${servicosDesativados.length})\n\n`; + servicosDesativados.forEach((servico, index) => { + message += `${index + 1}. ⚫ **${servico.descricao || servico.plano || 'Serviço'}**\n`; + message += ` 🆔 ID: \`${servico.id_servico || servico.id}\`\n`; + + if (servico.data_desativacao && servico.data_desativacao !== '0000-00-00') { + message += ` 🕒 Desativado em: ${servico.data_desativacao}\n`; + } + + message += `\n`; + }); + } + + // Botões de ação + const keyboard = []; + + if (servicosAtivos.length > 0) { + keyboard.push([ + { text: '🔒 Bloquear Todos', callback_data: `confirm_block_all_${cliente.id}_${location}` } + ]); + } + + if (servicosBloqueados.length > 0) { + keyboard.push([ + { text: '🔓 Liberar Todos', callback_data: `confirm_unblock_all_${cliente.id}_${location}` } + ]); + } + + await this.bot.sendMessage(chatId, message, { + parse_mode: 'Markdown', + reply_markup: keyboard.length > 0 ? { + inline_keyboard: keyboard + } : undefined + }); + + } catch (error) { + console.error('❌ Erro ao listar serviços:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao listar serviços**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Ver faturas do cliente + */ + async handleVerFaturas(chatId, targetOrId, location = '') { + try { + let idCliente = targetOrId; + + // Se não for um número, buscar cliente primeiro + if (isNaN(targetOrId)) { + await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); + + const resultado = await this.topsapp.buscarCliente(targetOrId, location); + + if (!resultado.success || !resultado.clientes || resultado.clientes.length === 0) { + await this.bot.sendMessage(chatId, + `🔍 **Nenhum cliente encontrado**\n\n` + + `📝 Termo buscado: "${targetOrId}"`, + { parse_mode: 'Markdown' } + ); + return; + } + + idCliente = resultado.clientes[0].id; + location = location || resultado.instancia; + } + + await this.bot.sendMessage(chatId, '📄 Buscando faturas...'); + + const resultado = await this.topsapp.obterFaturas(idCliente, location); + + if (!resultado.success) { + await this.bot.sendMessage(chatId, + `❌ **Erro ao buscar faturas**\n\n` + + `⚠️ ${resultado.error}`, + { parse_mode: 'Markdown' } + ); + return; + } + + const faturas = resultado.data?.faturas || resultado.data || []; + + if (faturas.length === 0) { + await this.bot.sendMessage(chatId, + `📄 **Nenhuma fatura em aberto**\n\n` + + `✅ O cliente não possui faturas pendentes.`, + { parse_mode: 'Markdown' } + ); + return; + } + + let message = `📄 **FATURAS DO CLIENTE**\n\n`; + message += `🆔 **ID Cliente**: ${idCliente}\n`; + message += `📊 **Total de faturas**: ${faturas.length}\n\n`; + + faturas.forEach((fatura, index) => { + message += `${index + 1}. 📋 **Fatura #${fatura.id || fatura.numero}**\n`; + + if (fatura.valor) { + const valor = parseFloat(fatura.valor); + message += ` 💰 Valor: R$ ${valor.toFixed(2)}\n`; + } + + if (fatura.vencimento) { + message += ` 📅 Vencimento: ${fatura.vencimento}\n`; + } + + if (fatura.status) { + const statusEmoji = fatura.status === 'aberto' || fatura.status === 'pendente' ? '🔴' : '✅'; + message += ` ${statusEmoji} Status: ${fatura.status}\n`; + } + + message += `\n`; + }); + + await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); + + } catch (error) { + console.error('❌ Erro ao buscar faturas:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao buscar faturas**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Gerar boleto + */ + async handleGerarBoleto(chatId, target) { + if (!target) { + await this.bot.sendMessage(chatId, + `💸 **Gerar Boleto**\n\n` + + `💡 **Como usar:**\n` + + `• "gerar boleto João da Silva"\n` + + `• "boleto do cliente 123.456.789-10"` + ); + return; + } + + try { + await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); + + let location = ''; + let query = target; + + if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { + location = 'Guarai/Colmeia'; + query = target.replace(/(guarai|colmeia)/gi, '').trim(); + } else if (target.toLowerCase().includes('pedro afonso')) { + location = 'Pedro Afonso'; + query = target.replace(/pedro afonso/gi, '').trim(); + } + + const resultado = await this.topsapp.buscarCliente(query, location); + + if (!resultado.success || !resultado.clientes || resultado.clientes.length === 0) { + await this.bot.sendMessage(chatId, + `🔍 **Nenhum cliente encontrado**\n\n` + + `📝 Termo buscado: "${query}"`, + { parse_mode: 'Markdown' } + ); + return; + } + + const cliente = resultado.clientes[0]; + + await this.handleVerFaturas(chatId, cliente.id, location); + + } catch (error) { + console.error('❌ Erro ao gerar boleto:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao gerar boleto**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Callback para confirmar bloqueio de todos os serviços + */ + async handleConfirmBlockAllCallback(callbackQuery, data) { + const chatId = callbackQuery.message.chat.id; + const parts = data.replace('confirm_block_all_', '').split('_'); + const idCliente = parts[0]; + const location = parts.slice(1).join('_'); + + try { + await this.bot.answerCallbackQuery(callbackQuery.id); + await this.bot.editMessageText('⏳ Bloqueando serviços...', { + chat_id: chatId, + message_id: callbackQuery.message.message_id + }); + + // Obter serviços do cliente + const servicosResult = await this.topsapp.obterServicos(idCliente, location); + + if (!servicosResult.success) { + await this.bot.sendMessage(chatId, + `❌ **Erro ao buscar serviços**\n\n` + + `⚠️ ${servicosResult.error}`, + { parse_mode: 'Markdown' } + ); + return; + } + + const servicos = servicosResult.data?.dados || servicosResult.data?.servicos || []; + const servicosAtivos = servicos.filter(s => { + const status = this.topsapp.api.interpretarStatusServico(s); + return status === 1; + }); + + let sucesso = 0; + let falhas = 0; + + for (const servico of servicosAtivos) { + const resultado = await this.topsapp.bloquearServico(servico.id_servico || servico.id, idCliente, location); + if (resultado.success) { + sucesso++; + } else { + falhas++; + } + } + + let message = `🔒 **Bloqueio Concluído**\n\n`; + message += `✅ **Bloqueados**: ${sucesso}\n`; + if (falhas > 0) { + message += `❌ **Falhas**: ${falhas}\n`; + } + + await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); + + } catch (error) { + console.error('❌ Erro ao bloquear serviços:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao bloquear serviços**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Callback para confirmar liberação de todos os serviços + */ + async handleConfirmUnblockAllCallback(callbackQuery, data) { + const chatId = callbackQuery.message.chat.id; + const parts = data.replace('confirm_unblock_all_', '').split('_'); + const idCliente = parts[0]; + const location = parts.slice(1).join('_'); + + try { + await this.bot.answerCallbackQuery(callbackQuery.id); + await this.bot.editMessageText('⏳ Liberando serviços...', { + chat_id: chatId, + message_id: callbackQuery.message.message_id + }); + + // Obter serviços do cliente + const servicosResult = await this.topsapp.obterServicos(idCliente, location); + + if (!servicosResult.success) { + await this.bot.sendMessage(chatId, + `❌ **Erro ao buscar serviços**\n\n` + + `⚠️ ${servicosResult.error}`, + { parse_mode: 'Markdown' } + ); + return; + } + + const servicos = servicosResult.data?.dados || servicosResult.data?.servicos || []; + const servicosBloqueados = servicos.filter(s => { + const status = this.topsapp.api.interpretarStatusServico(s); + return status === 2; + }); + + let sucesso = 0; + let falhas = 0; + + for (const servico of servicosBloqueados) { + const resultado = await this.topsapp.liberarServico(servico.id_servico || servico.id, idCliente, location); + if (resultado.success) { + sucesso++; + } else { + falhas++; + } + } + + let message = `🔓 **Liberação Concluída**\n\n`; + message += `✅ **Liberados**: ${sucesso}\n`; + if (falhas > 0) { + message += `❌ **Falhas**: ${falhas}\n`; + } + + await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); + + } catch (error) { + console.error('❌ Erro ao liberar serviços:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao liberar serviços**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Callback para confirmar bloqueio de cliente individual + */ + async handleConfirmBlockCallback(callbackQuery, data) { + await this.handleConfirmBlockAllCallback(callbackQuery, data.replace('confirm_block_', 'confirm_block_all_')); + } + + /** + * Callback para confirmar liberação de cliente individual + */ + async handleConfirmUnblockCallback(callbackQuery, data) { + await this.handleConfirmUnblockAllCallback(callbackQuery, data.replace('confirm_unblock_', 'confirm_unblock_all_')); + } + + /** + * Callback para confirmar bloqueio de serviço individual + */ + async handleConfirmBlockServiceCallback(callbackQuery, data) { + const chatId = callbackQuery.message.chat.id; + // Formato: confirm_block_service_idServico_idCliente_location + const parts = data.replace('confirm_block_service_', '').split('_'); + const idServico = parts[0]; + const idCliente = parts[1]; + const location = parts.slice(2).join('_'); + + try { + await this.bot.answerCallbackQuery(callbackQuery.id); + await this.bot.editMessageText('⏳ Bloqueando serviço...', { + chat_id: chatId, + message_id: callbackQuery.message.message_id + }); + + const resultado = await this.topsapp.bloquearServico(idServico, idCliente, location); + + if (resultado.success) { + await this.bot.sendMessage(chatId, + `✅ **Serviço bloqueado com sucesso!**\n\n` + + `🆔 **ID do Serviço**: ${idServico}`, + { parse_mode: 'Markdown' } + ); + } else { + await this.bot.sendMessage(chatId, + `❌ **Erro ao bloquear serviço**\n\n` + + `⚠️ ${resultado.error}`, + { parse_mode: 'Markdown' } + ); + } + + } catch (error) { + console.error('❌ Erro ao bloquear serviço:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao bloquear serviço**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Callback para confirmar liberação de serviço individual + */ + async handleConfirmUnblockServiceCallback(callbackQuery, data) { + const chatId = callbackQuery.message.chat.id; + // Formato: confirm_unblock_service_idServico_idCliente_location + const parts = data.replace('confirm_unblock_service_', '').split('_'); + const idServico = parts[0]; + const idCliente = parts[1]; + const location = parts.slice(2).join('_'); + + try { + await this.bot.answerCallbackQuery(callbackQuery.id); + await this.bot.editMessageText('⏳ Liberando serviço...', { + chat_id: chatId, + message_id: callbackQuery.message.message_id + }); + + const resultado = await this.topsapp.liberarServico(idServico, idCliente, location); + + if (resultado.success) { + await this.bot.sendMessage(chatId, + `✅ **Serviço liberado com sucesso!**\n\n` + + `🆔 **ID do Serviço**: ${idServico}`, + { parse_mode: 'Markdown' } + ); + } else { + await this.bot.sendMessage(chatId, + `❌ **Erro ao liberar serviço**\n\n` + + `⚠️ ${resultado.error}`, + { parse_mode: 'Markdown' } + ); + } + + } catch (error) { + console.error('❌ Erro ao liberar serviço:', error); + await this.bot.sendMessage(chatId, + `❌ **Erro ao liberar serviço**\n\n` + + `⚠️ ${error.message}`, + { parse_mode: 'Markdown' } + ); + } + } + + /** + * Callback para dados do cliente + */ + async handleDadosClienteCallback(callbackQuery, data) { + const chatId = callbackQuery.message.chat.id; + const parts = data.replace('dados_cliente_', '').split('_'); + const idCliente = parts[0]; + const location = parts.slice(1).join('_'); + + await this.bot.answerCallbackQuery(callbackQuery.id); + await this.handleDadosCliente(chatId, idCliente, location); + } + + /** + * Callback para faturas do cliente + */ + async handleFaturasClienteCallback(callbackQuery, data) { + const chatId = callbackQuery.message.chat.id; + const parts = data.replace('faturas_cliente_', '').split('_'); + const idCliente = parts[0]; + const location = parts.slice(1).join('_'); + + await this.bot.answerCallbackQuery(callbackQuery.id); + await this.handleVerFaturas(chatId, idCliente, location); + } + + /** + * Callback para serviços do cliente + */ + async handleServicosClienteCallback(callbackQuery, data) { + const chatId = callbackQuery.message.chat.id; + const parts = data.replace('servicos_cliente_', '').split('_'); + const idCliente = parts[0]; + const location = parts.slice(1).join('_'); + + await this.bot.answerCallbackQuery(callbackQuery.id); + await this.handleListarServicos(chatId, idCliente); + } + /** * Escapar caracteres problemáticos para Markdown do Telegram */ @@ -2763,7 +3572,7 @@ Digite /help para ver todos os comandos disponíveis! // Dados dos serviços if (servicos.length > 0) { - message += `⚙️ SERVIÇOS CONTRATADOS\n\n`; + message += `⚙️ **SERVIÇOS CONTRATADOS**\n\n`; servicos.forEach((servico, index) => { message += `📦 Serviço ${index + 1}\n`; @@ -2888,1675 +3697,6 @@ Digite /help para ver todos os comandos disponíveis! ] ]; - console.log('📝 Enviando dados do cliente'); - - try { - await this.bot.sendMessage(chatId, message, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - } catch (telegramError) { - console.error('❌ Erro de Markdown no Telegram:', telegramError.message); - console.log('🔧 Enviando sem Markdown como fallback...'); - - // Fallback: enviar sem Markdown, removendo apenas os backticks - const fallbackMessage = message.replace(/`/g, ''); - - await this.bot.sendMessage(chatId, fallbackMessage, { - reply_markup: { - inline_keyboard: keyboard - } - }); - } - - } catch (error) { - console.error('❌ Erro ao buscar dados do cliente:', error); - await this.bot.sendMessage(chatId, - `❌ Erro ao buscar dados do cliente\n\n` + - `⚠️ ${error.message}` - ); - } - } - - /** - * Ver faturas do cliente - */ - async handleVerFaturas(chatId, target) { - if (!target) { - await this.bot.sendMessage(chatId, - `📄 **Ver Faturas**\n\n` + - `💡 **Como usar:**\n` + - `• "ver faturas 123.456.789-10"\n` + - `• "ver faturas cliente123"\n` + - `• "ver faturas Guarai 123.456.789-10"` - ); - return; - } - - try { - await this.bot.sendMessage(chatId, '📄 Buscando faturas...'); - - // Detectar se especificou uma localização - let location = ''; - let clienteId = target; - - if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { - location = 'Guarai/Colmeia'; - clienteId = target.replace(/(guarai|colmeia)/gi, '').trim(); - } else if (target.toLowerCase().includes('pedro afonso')) { - location = 'Pedro Afonso'; - clienteId = target.replace(/pedro afonso/gi, '').trim(); - } - - // Se é CPF ou nome, buscar o cliente primeiro - let idCliente = null; - - // Verificar se é número puro (CPF sem formatação) - if (/^\d{11}$/.test(clienteId)) { - // É CPF sem formatação - const busca = await this.topsapp.buscarCliente(clienteId, location); - if (busca.success && busca.clientes && busca.clientes.length > 0) { - idCliente = busca.clientes[0].id; - location = busca.instancia; - } else { - await this.bot.sendMessage(chatId, - `❌ **Cliente não encontrado**\n\n` + - `📝 CPF: "${clienteId}"`, - { parse_mode: 'Markdown' } - ); - return; - } - } else if (clienteId.includes('.') || clienteId.includes('-') || isNaN(clienteId)) { - // É CPF formatado ou nome - const busca = await this.topsapp.buscarCliente(clienteId, location); - if (busca.success && busca.clientes && busca.clientes.length > 0) { - idCliente = busca.clientes[0].id; - location = busca.instancia; - } else { - await this.bot.sendMessage(chatId, - `❌ **Cliente não encontrado**\n\n` + - `📝 CPF/Nome: "${clienteId}"`, - { parse_mode: 'Markdown' } - ); - return; - } - } else { - // É ID numérico - idCliente = clienteId; - } - - const resultado = await this.topsapp.obterBoletos(idCliente, location); - - if (!resultado.success) { - await this.bot.sendMessage(chatId, - `❌ **Erro ao buscar faturas**\n\n` + - `⚠️ ${resultado.error}`, - { parse_mode: 'Markdown' } - ); - return; - } - - if (!resultado.boletos || resultado.boletos.length === 0) { - await this.bot.sendMessage(chatId, - `📄 **Nenhuma fatura encontrada**\n\n` + - `👤 Cliente ID: ${idCliente}\n` + - `🌐 Instância: ${resultado.instancia || location}`, - { parse_mode: 'Markdown' } - ); - return; - } - - let message = `📄 **Faturas do Cliente**\n\n`; - message += `📊 **Total**: ${resultado.total} fatura(s)\n`; - message += `🌐 **Instância**: ${resultado.instancia}\n\n`; - - resultado.boletos.slice(0, 10).forEach((boleto, index) => { - message += `━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`; - message += `💸 **${index + 1}. Fatura #${boleto.id}**\n`; - message += `💰 **Valor:** R$ ${boleto.valor}\n`; - message += `📅 **Vencimento:** ${this.formatarData(boleto.vencimento)}\n`; - message += `📊 **Status:** ${this.getStatusFatura(boleto.status)}\n`; - if (boleto.descricao) { - message += `📝 **Descrição:** ${boleto.descricao}\n`; - } - if (boleto.periodo) { - message += `📆 **Período:** ${boleto.periodo}\n`; - } - - if (boleto.linhaDigitavel) { - message += `\n🔢 **Linha Digitável:**\n\`${boleto.linhaDigitavel}\`\n`; - } - - if (boleto.pixCopiaCola) { - message += `\n💳 **PIX Copia e Cola:**\n\`${boleto.pixCopiaCola}\`\n`; - } - - message += '\n'; - }); - - if (resultado.total > 10) { - message += `📋 *Mostrando apenas as primeiras 10 faturas de ${resultado.total}*\n\n`; - } - - if (resultado.boletos.length > 0) { - message += `💡 **Gerar boleto:**\n`; - message += `• "gerar boleto ${resultado.boletos[0].id}"`; - } - - await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); - - } catch (error) { - console.error('❌ Erro ao buscar faturas:', error); - await this.bot.sendMessage(chatId, - `❌ **Erro ao buscar faturas**\n\n` + - `⚠️ ${error.message}`, - { parse_mode: 'Markdown' } - ); - } - } - - /** - * Gerar boleto PDF - */ - async handleGerarBoleto(chatId, target) { - if (!target) { - await this.bot.sendMessage(chatId, - `💸 **Gerar Boleto**\n\n` + - `💡 **Como usar:**\n` + - `• "gerar boleto 12345"\n` + - `• "gerar boleto fatura123"` - ); - return; - } - - try { - await this.bot.sendMessage(chatId, '💸 Gerando boleto PDF...'); - - const resultado = await this.topsapp.gerarBoletoPDF(target); - - if (!resultado.success) { - await this.bot.sendMessage(chatId, - `❌ **Erro ao gerar boleto**\n\n` + - `⚠️ ${resultado.error}`, - { parse_mode: 'Markdown' } - ); - return; - } - - // Enviar o PDF - await this.bot.sendDocument(chatId, resultado.buffer, { - filename: resultado.filename, - caption: `💸 **Boleto Gerado**\n\n` + - `📄 Arquivo: ${resultado.filename}\n` + - `🌐 Instância: ${resultado.instancia}`, - parse_mode: 'Markdown' - }); - - await this.bot.sendMessage(chatId, - `✅ **Boleto enviado com sucesso!**\n\n` + - `📄 Arquivo: ${resultado.filename}\n` + - `🌐 Instância: ${resultado.instancia}`, - { parse_mode: 'Markdown' } - ); - - } catch (error) { - console.error('❌ Erro ao gerar boleto:', error); - await this.bot.sendMessage(chatId, - `❌ **Erro ao gerar boleto**\n\n` + - `⚠️ ${error.message}`, - { parse_mode: 'Markdown' } - ); - } - } - - /** - * Bloquear cliente - */ - async handleBloquearCliente(chatId, target) { - console.log(`🔍 handleBloquearCliente iniciado com target:`, target, `(tipo: ${typeof target})`); - - if (!target) { - await this.bot.sendMessage(chatId, - `🔒 **Bloquear Cliente**\n\n` + - `💡 **Como usar:**\n` + - `• "bloquear 123.456.789-10"\n` + - `• "bloquear cliente João"\n` + - `• "bloquear Guarai 123.456.789-10"` - ); - return; - } - - try { - await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); - - // Detectar se especificou uma localização - let location = ''; - let clienteQuery = target; - - if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { - location = 'Guarai/Colmeia'; - clienteQuery = target.replace(/(guarai|colmeia)/gi, '').trim(); - } else if (target.toLowerCase().includes('pedro afonso')) { - location = 'Pedro Afonso'; - clienteQuery = target.replace(/pedro afonso/gi, '').trim(); - } - - // Buscar cliente primeiro - let idCliente = null; - let nomeCliente = ''; - - if (/^\d{11}$/.test(clienteQuery) || clienteQuery.includes('.') || clienteQuery.includes('-') || isNaN(clienteQuery)) { - // É CPF ou nome - const busca = await this.topsapp.buscarCliente(clienteQuery, location); - if (busca.success && busca.clientes && busca.clientes.length > 0) { - idCliente = busca.clientes[0].id; - nomeCliente = busca.clientes[0].nome; - location = busca.instancia; - } else { - await this.bot.sendMessage(chatId, - `❌ **Cliente não encontrado**\n\n` + - `📝 CPF/Nome: "${clienteQuery}"`, - { parse_mode: 'Markdown' } - ); - return; - } - } else { - // É ID numérico - idCliente = clienteQuery; - } - - // Buscar serviços do cliente para mostrar opções - const servicosResult = await this.topsapp.obterServicosCliente(idCliente, location); - - if (!servicosResult.success) { - await this.bot.sendMessage(chatId, - `❌ **Erro ao buscar serviços**\n\n` + - `⚠️ ${servicosResult.error}`, - { parse_mode: 'Markdown' } - ); - return; - } - - if (!servicosResult.servicos || servicosResult.servicos.length === 0) { - await this.bot.sendMessage(chatId, - `📋 **Nenhum serviço encontrado**\n\n` + - `👤 Cliente: ${nomeCliente}\n` + - `🆔 ID: ${idCliente}`, - { parse_mode: 'Markdown' } - ); - return; - } - - // Verificar se há serviços liberados - const servicosLiberados = servicosResult.servicos.filter(s => - s.status === 'liberado' || s.statusNumerico === 1 - ); - - if (servicosLiberados.length === 0) { - await this.bot.sendMessage(chatId, - `🔒 **Nenhum serviço liberado**\n\n` + - `👤 Cliente: ${nomeCliente}\n` + - `🆔 ID: ${idCliente}\n` + - `📊 Todos os ${servicosResult.servicos.length} serviços já estão bloqueados.`, - { parse_mode: 'Markdown' } - ); - return; - } - - // Criar menu de seleção de serviços - let message = `🔒 **Selecionar Serviços para Bloquear**\n\n`; - message += `👤 **Cliente**: ${nomeCliente}\n`; - message += `🆔 **ID**: ${idCliente}\n`; - message += `🌐 **Instância**: ${location}\n\n`; - message += `✅ **Serviços liberados** (${servicosLiberados.length}):\n\n`; - - const keyboard = []; - - servicosLiberados.forEach((servico, index) => { - message += `${index + 1}. **${servico.nome}**\n`; - message += ` 💰 Valor: R$ ${servico.valorMensal}\n`; - message += ` 🆔 ID: ${servico.id}\n\n`; - - // Botão para bloquear este serviço específico - keyboard.push([{ - text: `🔒 Bloquear ${servico.nome}`, - callback_data: `confirm_block_service_${servico.id}_${idCliente}_${location.replace(/\s+/g, '_')}` - }]); - }); - - // Botão para bloquear todos - if (servicosLiberados.length > 1) { - keyboard.push([{ - text: `🔒 Bloquear TODOS os serviços (${servicosLiberados.length})`, - callback_data: `confirm_block_all_${idCliente}_${location.replace(/\s+/g, '_')}` - }]); - } - - // Botão cancelar - keyboard.push([{ - text: '❌ Cancelar', - callback_data: 'cancel_action' - }]); - - await this.bot.sendMessage(chatId, message, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - - } catch (error) { - console.error('❌ Erro ao preparar bloqueio:', error); - await this.bot.sendMessage(chatId, - `❌ **Erro ao preparar bloqueio**\n\n` + - `⚠️ ${error.message}`, - { parse_mode: 'Markdown' } - ); - } - } - - /** - * Liberar cliente - */ - async handleLiberarCliente(chatId, target) { - if (!target) { - await this.bot.sendMessage(chatId, - `🔓 **Liberar Cliente**\n\n` + - `💡 **Como usar:**\n` + - `• "liberar 123.456.789-10"\n` + - `• "liberar cliente João"\n` + - `• "liberar Guarai 123.456.789-10"` - ); - return; - } - - try { - await this.bot.sendMessage(chatId, '🔍 Buscando cliente...'); - - // Detectar se especificou uma localização - let location = ''; - let clienteQuery = target; - - if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { - location = 'Guarai/Colmeia'; - clienteQuery = target.replace(/(guarai|colmeia)/gi, '').trim(); - } else if (target.toLowerCase().includes('pedro afonso')) { - location = 'Pedro Afonso'; - clienteQuery = target.replace(/pedro afonso/gi, '').trim(); - } - - // Buscar cliente primeiro - let idCliente = null; - let nomeCliente = ''; - - if (/^\d{11}$/.test(clienteQuery) || clienteQuery.includes('.') || clienteQuery.includes('-') || isNaN(clienteQuery)) { - // É CPF ou nome - const busca = await this.topsapp.buscarCliente(clienteQuery, location); - if (busca.success && busca.clientes && busca.clientes.length > 0) { - idCliente = busca.clientes[0].id; - nomeCliente = busca.clientes[0].nome; - location = busca.instancia; - } else { - await this.bot.sendMessage(chatId, - `❌ **Cliente não encontrado**\n\n` + - `📝 CPF/Nome: "${clienteQuery}"`, - { parse_mode: 'Markdown' } - ); - return; - } - } else { - // É ID numérico - idCliente = clienteQuery; - } - - // Buscar serviços do cliente para mostrar opções - const servicosResult = await this.topsapp.obterServicosCliente(idCliente, location); - - if (!servicosResult.success) { - await this.bot.sendMessage(chatId, - `❌ **Erro ao buscar serviços**\n\n` + - `⚠️ ${servicosResult.error}`, - { parse_mode: 'Markdown' } - ); - return; - } - - if (!servicosResult.servicos || servicosResult.servicos.length === 0) { - await this.bot.sendMessage(chatId, - `📋 **Nenhum serviço encontrado**\n\n` + - `👤 Cliente: ${nomeCliente}\n` + - `🆔 ID: ${idCliente}`, - { parse_mode: 'Markdown' } - ); - return; - } - - // Verificar se há serviços bloqueados - const servicosBloqueados = servicosResult.servicos.filter(s => - s.status === 'bloqueado' || s.statusNumerico === 2 - ); - - if (servicosBloqueados.length === 0) { - await this.bot.sendMessage(chatId, - `✅ **Nenhum serviço bloqueado**\n\n` + - `👤 Cliente: ${nomeCliente}\n` + - `🆔 ID: ${idCliente}\n` + - `📊 Todos os ${servicosResult.servicos.length} serviços estão liberados.`, - { parse_mode: 'Markdown' } - ); - return; - } - - // Criar menu de seleção de serviços - let message = `🔓 **Selecionar Serviços para Liberar**\n\n`; - message += `👤 **Cliente**: ${nomeCliente}\n`; - message += `🆔 **ID**: ${idCliente}\n`; - message += `🌐 **Instância**: ${location}\n\n`; - message += `🔒 **Serviços bloqueados** (${servicosBloqueados.length}):\n\n`; - - const keyboard = []; - - servicosBloqueados.forEach((servico, index) => { - message += `${index + 1}. **${servico.nome}**\n`; - message += ` 💰 Valor: R$ ${servico.valorMensal}\n`; - message += ` 🆔 ID: ${servico.id}\n\n`; - - // Botão para liberar este serviço específico - keyboard.push([{ - text: `🔓 Liberar ${servico.nome}`, - callback_data: `confirm_unblock_service_${servico.id}_${idCliente}_${location.replace(/\s+/g, '_')}` - }]); - }); - - // Botão para liberar todos - if (servicosBloqueados.length > 1) { - keyboard.push([{ - text: `🔓 Liberar TODOS os serviços (${servicosBloqueados.length})`, - callback_data: `confirm_unblock_all_${idCliente}_${location.replace(/\s+/g, '_')}` - }]); - } - - // Botão cancelar - keyboard.push([{ - text: '❌ Cancelar', - callback_data: 'cancel_action' - }]); - - await this.bot.sendMessage(chatId, message, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - - } catch (error) { - console.error('❌ Erro ao preparar liberação:', error); - await this.bot.sendMessage(chatId, - `❌ **Erro ao preparar liberação**\n\n` + - `⚠️ ${error.message}`, - { parse_mode: 'Markdown' } - ); - } - } - - /** - * Utilitário para obter status da fatura - */ - getStatusFatura(status) { - // Se já é uma string formatada, retornar diretamente - if (typeof status === 'string') { - switch (status.toLowerCase()) { - case 'pago': return '✅ Pago'; - case 'pendente': return '⏳ Pendente'; - case 'em aberto': return '⏳ Pendente'; - case 'vencida': return '❌ Vencida'; - case 'processando': return '🔄 Em processamento'; - default: return `📊 ${status}`; - } - } - - // Números (legado) - switch (status) { - case 1: return '✅ Pago'; - case 2: return '⏳ Pendente'; - case 3: return '❌ Vencida'; - case 4: return '🔄 Em processamento'; - default: return '❓ Status desconhecido'; - } - } - - /** - * Callback para confirmar bloqueio de cliente - */ - async handleConfirmBlockCallback(callbackQuery, data) { - const chatId = callbackQuery.message.chat.id; - - try { - // Extrair dados: confirm_block_ID_LOCATION - const parts = data.replace('confirm_block_', '').split('_'); - const clienteId = parts[0]; - const location = parts[1] ? parts[1].replace(/_/g, ' ') : ''; - - await this.bot.answerCallbackQuery(callbackQuery.id, 'Processando bloqueio...'); - - await this.bot.editMessageText( - `🔒 **Bloqueando cliente ID: ${clienteId}**\n\n⏳ Aguarde...`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - - const resultado = await this.topsapp.bloquearCliente(clienteId, location); - - if (!resultado.success) { - await this.bot.editMessageText( - `❌ **Erro ao bloquear cliente**\n\n` + - `🆔 Cliente ID: ${clienteId}\n` + - `🌐 Instância: ${location}\n` + - `⚠️ Erro: ${resultado.error}`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - return; - } - - let message = `🔒 **Cliente bloqueado com sucesso!**\n\n`; - message += `🆔 Cliente ID: ${clienteId}\n`; - message += `🌐 Instância: ${resultado.instancia}\n`; - message += `📊 Serviços bloqueados: ${resultado.total}\n\n`; - - if (resultado.servicosBloqueados && resultado.servicosBloqueados.length > 0) { - message += `📋 **Detalhes dos bloqueios:**\n`; - resultado.servicosBloqueados.forEach((servico, index) => { - message += `${index + 1}. ${servico.descricao}: ${servico.resultado.success ? '✅ Bloqueado' : '❌ Erro'}\n`; - }); - } - - await this.bot.editMessageText(message, { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - }); - - } catch (error) { - console.error('❌ Erro ao bloquear cliente:', error); - await this.bot.editMessageText( - `❌ **Erro interno ao bloquear cliente**\n\n` + - `🆔 Cliente ID: ${clienteId}\n` + - `⚠️ ${error.message}`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - } - } - - /** - * Callback para confirmar liberação de cliente - */ - async handleConfirmUnblockCallback(callbackQuery, data) { - const chatId = callbackQuery.message.chat.id; - - try { - // Extrair dados: confirm_unblock_ID_LOCATION - const parts = data.replace('confirm_unblock_', '').split('_'); - const clienteId = parts[0]; - const location = parts[1] ? parts[1].replace(/_/g, ' ') : ''; - - await this.bot.answerCallbackQuery(callbackQuery.id, 'Carregando opções...'); - - // Usar a mesma lógica do comando NLP para mostrar opções de serviços - // Em vez de liberar tudo diretamente, mostrar menu de seleção - const servicosResult = await this.topsapp.obterServicosCliente(clienteId, location); - - if (!servicosResult.success) { - await this.bot.editMessageText( - `❌ **Erro ao buscar serviços**\n\n` + - `⚠️ ${servicosResult.error}`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - return; - } - - if (!servicosResult.servicos || servicosResult.servicos.length === 0) { - await this.bot.editMessageText( - `📋 **Nenhum serviço encontrado**\n\n` + - `🆔 Cliente ID: ${clienteId}`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - return; - } - - // Verificar se há serviços bloqueados - const servicosBloqueados = servicosResult.servicos.filter(s => - s.status === 'bloqueado' || s.statusNumerico === 2 - ); - - if (servicosBloqueados.length === 0) { - await this.bot.editMessageText( - `✅ **Nenhum serviço bloqueado**\n\n` + - `🆔 Cliente ID: ${clienteId}\n` + - `📊 Todos os ${servicosResult.servicos.length} serviços estão liberados.`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - return; - } - - // Criar menu de seleção de serviços - let message = `🔓 **Selecionar Serviços para Liberar**\n\n`; - message += `🆔 **Cliente ID**: ${clienteId}\n`; - message += `🌐 **Instância**: ${location}\n\n`; - message += `� **Serviços bloqueados** (${servicosBloqueados.length}):\n\n`; - - const keyboard = []; - - servicosBloqueados.forEach((servico, index) => { - message += `${index + 1}. **${servico.nome}**\n`; - message += ` 💰 Valor: R$ ${servico.valorMensal}\n`; - message += ` 🆔 ID: ${servico.id}\n\n`; - - // Botão para liberar este serviço específico - keyboard.push([{ - text: `🔓 Liberar ${servico.nome}`, - callback_data: `confirm_unblock_service_${servico.id}_${clienteId}_${location.replace(/\s+/g, '_')}` - }]); - }); - - // Botão para liberar todos - if (servicosBloqueados.length > 1) { - keyboard.push([{ - text: `🔓 Liberar TODOS os serviços (${servicosBloqueados.length})`, - callback_data: `confirm_unblock_all_${clienteId}_${location.replace(/\s+/g, '_')}` - }]); - } - - // Botão cancelar - keyboard.push([{ - text: '❌ Cancelar', - callback_data: 'cancel_action' - }]); - - await this.bot.editMessageText(message, { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - - } catch (error) { - console.error('❌ Erro ao preparar liberação:', error); - await this.bot.editMessageText( - `❌ **Erro ao preparar liberação**\n\n` + - `🆔 Cliente ID: ${clienteId}\n` + - `⚠️ ${error.message}`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - } - } - - /** - * Comando para listar serviços do cliente - */ - async handleListarServicos(chatId, target) { - logger.botCommand(chatId, 'listar_servicos', { target }); - - if (!target) { - await this.bot.sendMessage(chatId, - `📋 **Listar Serviços**\n\n` + - `💡 **Como usar:**\n` + - `• "listar serviços 123.456.789-10"\n` + - `• "ver serviços cliente123"\n` + - `• "mostrar serviços Guarai 123.456.789-10"` - ); - return; - } - - try { - await this.bot.sendMessage(chatId, '📋 Buscando serviços...'); - - // Detectar se especificou uma localização - let location = ''; - let clienteQuery = target; - - logger.debug('Processando busca de serviços', { - originalTarget: target, - chatId - }); - - if (target.toLowerCase().includes('guarai') || target.toLowerCase().includes('colmeia')) { - location = 'Guarai/Colmeia'; - clienteQuery = target.replace(/(guarai|colmeia)/gi, '').trim(); - } else if (target.toLowerCase().includes('pedro afonso')) { - location = 'Pedro Afonso'; - clienteQuery = target.replace(/pedro afonso/gi, '').trim(); - } - - logger.debug('Detectada localização e query', { - location, - clienteQuery, - isNumeric: !isNaN(clienteQuery), - isCPF: /^\d{11}$/.test(clienteQuery) - }); - - // Buscar cliente primeiro - let idCliente = null; - let nomeCliente = ''; - - if (/^\d{11}$/.test(clienteQuery) || clienteQuery.includes('.') || clienteQuery.includes('-') || isNaN(clienteQuery)) { - // É CPF ou nome - logger.debug('Buscando por CPF/Nome', { clienteQuery }); - const busca = await this.topsapp.buscarCliente(clienteQuery, location); - logger.debug('Resultado da busca de cliente', busca); - - if (busca.success && busca.clientes && busca.clientes.length > 0) { - idCliente = busca.clientes[0].id; - nomeCliente = busca.clientes[0].nome; - location = busca.instancia; - logger.debug('Cliente encontrado', { idCliente, nomeCliente, location }); - } else { - logger.warn('Cliente não encontrado', { clienteQuery, location }); - await this.bot.sendMessage(chatId, - `❌ **Cliente não encontrado**\n\n` + - `📝 CPF/Nome: "${clienteQuery}"`, - { parse_mode: 'Markdown' } - ); - return; - } - } else { - // É ID numérico - idCliente = clienteQuery; - logger.debug('Usando ID direto', { idCliente: clienteQuery }); - } - - logger.debug('Chamando obterServicosCliente', { - idCliente, - location, - tipoIdCliente: typeof idCliente - }); - - const resultado = await this.topsapp.obterServicosCliente(idCliente, location); - logger.topsappOperation('obterServicosCliente', idCliente, resultado); - - if (!resultado.success) { - logger.error('Erro ao buscar serviços', resultado.error); - await this.bot.sendMessage(chatId, - `❌ **Erro ao buscar serviços**\n\n` + - `⚠️ ${resultado.error}`, - { parse_mode: 'Markdown' } - ); - return; - return; - } - - // Debug: verificar se houve problema na filtragem - if (resultado.debug && resultado.debug.totalRetornadoAPI > 100) { - await this.bot.sendMessage(chatId, - `⚠️ **DEBUG: Possível problema na API**\n\n` + - `🔍 Total retornado pela API: ${resultado.debug.totalRetornadoAPI}\n` + - `🎯 Total filtrado para cliente ${resultado.debug.clienteId}: ${resultado.debug.totalFiltrado}\n` + - `🌐 Instância: ${resultado.instancia}\n\n` + - `💡 Se este número ainda está alto, há problema na API do TopSapp.`, - { parse_mode: 'Markdown' } - ); - } - - if (!resultado.servicos || resultado.servicos.length === 0) { - await this.bot.sendMessage(chatId, - `📋 **Nenhum serviço encontrado**\n\n` + - `👤 Cliente ID: ${idCliente}\n` + - `🌐 Instância: ${resultado.instancia}`, - { parse_mode: 'Markdown' } - ); - return; - } - - // Criar mensagem com lista de serviços - let message = `📋 **Serviços do Cliente**\n\n`; - if (nomeCliente) { - message += `👤 **Cliente**: ${nomeCliente}\n`; - } - message += `🆔 **ID**: ${idCliente}\n`; - message += `📊 **Total**: ${resultado.total} serviço(s)\n`; - message += `🌐 **Instância**: ${resultado.instancia}\n\n`; - - // Criar botões inline para cada serviço - const keyboard = []; - - resultado.servicos.forEach((servico, index) => { - message += `🔧 **${index + 1}. ${servico.nome}**\n`; - message += `🆔 ID: ${servico.id}\n`; - message += `📊 Status: ${this.getStatusServico(servico.status)}\n`; - message += `💰 Valor: R$ ${servico.valorMensal}\n`; - - if (servico.dataAtivacao) { - message += `📅 Ativado em: ${servico.dataAtivacao}\n`; - } - - message += '\n'; - - // Adicionar botões de ação baseados no status - const row = []; - if (servico.status === 'liberado') { - row.push({ - text: `🔒 Bloquear ${servico.nome}`, - callback_data: `block_service_${servico.id}_${idCliente}_${location.replace(/\s+/g, '_')}` - }); - } else { - row.push({ - text: `🔓 Liberar ${servico.nome}`, - callback_data: `unblock_service_${servico.id}_${idCliente}_${location.replace(/\s+/g, '_')}` - }); - } - keyboard.push(row); - }); - - await this.bot.sendMessage(chatId, message, { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - - } catch (error) { - console.error('❌ Erro ao listar serviços:', error); - await this.bot.sendMessage(chatId, - `❌ **Erro interno ao listar serviços**\n\n` + - `⚠️ ${error.message}`, - { parse_mode: 'Markdown' } - ); - } - } - - /** - * Utilitário para obter status do serviço - */ - getStatusServico(status) { - if (typeof status === 'string') { - switch (status.toLowerCase()) { - case 'liberado': return '✅ Liberado'; - case 'bloqueado': return '🔒 Bloqueado'; - case 'desativado': return '❌ Desativado'; - default: return `📊 ${status}`; - } - } - - switch (status) { - case 1: return '✅ Liberado'; - case 2: return '🔒 Bloqueado'; - case 3: return '❌ Desativado'; - default: return '❓ Status desconhecido'; - } - } - - async handleConfirmBlockServiceCallback(callbackQuery, data) { - try { - // Extrair dados: confirm_block_service_SERVICEID_CLIENTEID_LOCATION - const parts = data.replace('confirm_block_service_', '').split('_'); - const serviceId = parts[0]; - const idCliente = parts[1]; - const location = parts.slice(2).join('_').replace(/_/g, ' '); - - logger.debug('handleConfirmBlockServiceCallback', { - serviceId, - idCliente, - location, - originalData: data - }); - - const chatId = callbackQuery.message.chat.id; - - await this.bot.editMessageText( - '⏳ Bloqueando serviço...', - { - chat_id: chatId, - message_id: callbackQuery.message.message_id - } - ); - - const resultado = await this.topsapp.alterarStatusServico(serviceId, 'bloquear', location); - - let texto; - if (resultado.success) { - texto = '✅ Serviço bloqueado com sucesso!'; - } else { - texto = `❌ Erro ao bloquear serviço: ${resultado.error}`; - } - - await this.bot.editMessageText( - texto, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id - } - ); - - } catch (error) { - logger.botError(callbackQuery.message.chat.id, error, 'handleConfirmBlockServiceCallback'); - console.error('Erro no handleConfirmBlockServiceCallback:', error); - await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno do servidor'); - } - } - - async handleConfirmUnblockServiceCallback(callbackQuery, data) { - try { - // Extrair dados: confirm_unblock_service_SERVICEID_CLIENTEID_LOCATION - const parts = data.replace('confirm_unblock_service_', '').split('_'); - const serviceId = parts[0]; - const idCliente = parts[1]; - const location = parts.slice(2).join('_').replace(/_/g, ' '); - - logger.debug('handleConfirmUnblockServiceCallback', { - serviceId, - idCliente, - location, - originalData: data - }); - - const chatId = callbackQuery.message.chat.id; - - await this.bot.editMessageText( - '⏳ Liberando serviço...', - { - chat_id: chatId, - message_id: callbackQuery.message.message_id - } - ); - - const resultado = await this.topsapp.alterarStatusServico(serviceId, 'liberar', location); - - let texto; - if (resultado.success) { - texto = '✅ Serviço liberado com sucesso!'; - } else { - texto = `❌ Erro ao liberar serviço: ${resultado.error}`; - } - - await this.bot.editMessageText( - texto, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id - } - ); - - } catch (error) { - console.error('Erro no handleConfirmUnblockServiceCallback:', error); - await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno do servidor'); - } - } - - async handleConfirmBlockAllCallback(callbackQuery, data) { - try { - const clienteId = data.replace('confirm_block_all_', ''); - const chatId = callbackQuery.message.chat.id; - - await this.bot.editMessageText( - '⏳ Bloqueando todos os serviços...', - { - chat_id: chatId, - message_id: callbackQuery.message.message_id - } - ); - - const resultado = await this.topsapp.bloquearCliente(clienteId); - - let texto; - if (resultado.sucesso) { - texto = '✅ Todos os serviços foram bloqueados com sucesso!'; - } else { - texto = `❌ Erro ao bloquear serviços: ${resultado.erro}`; - } - - await this.bot.editMessageText( - texto, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id - } - ); - - } catch (error) { - console.error('Erro no handleConfirmBlockAllCallback:', error); - await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno do servidor'); - } - } - - async handleConfirmUnblockAllCallback(callbackQuery, data) { - try { - const clienteId = data.replace('confirm_unblock_all_', ''); - const chatId = callbackQuery.message.chat.id; - - await this.bot.editMessageText( - '⏳ Liberando todos os serviços...', - { - chat_id: chatId, - message_id: callbackQuery.message.message_id - } - ); - - const resultado = await this.topsapp.liberarCliente(clienteId); - - let texto; - if (resultado.sucesso) { - texto = '✅ Todos os serviços foram liberados com sucesso!'; - } else { - texto = `❌ Erro ao liberar serviços: ${resultado.erro}`; - } - - await this.bot.editMessageText( - texto, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id - } - ); - - } catch (error) { - console.error('Erro no handleConfirmUnblockAllCallback:', error); - await this.bot.answerCallbackQuery(callbackQuery.id, 'Erro interno do servidor'); - } - } - - async handleBlockServiceSelection(callbackQuery, data) { - try { - // Extrair dados: block_service_ID_CLIENTE_LOCATION - const parts = data.replace('block_service_', '').split('_'); - const serviceId = parts[0]; - const clienteId = parts[1]; - const location = parts.slice(2).join('_').replace(/_/g, ' '); - - const chatId = callbackQuery.message.chat.id; - - // Criar mensagem de confirmação - const texto = `🔸 **Confirmar Bloqueio de Serviço**\n\n` + - `🆔 **ID do Serviço:** ${serviceId}\n` + - `👤 **Cliente:** ${clienteId}\n` + - `📍 **Localização:** ${location}\n\n` + - `⚠️ Tem certeza que deseja bloquear este serviço?`; - - const keyboard = { - inline_keyboard: [ - [ - { text: '✅ Sim, bloquear', callback_data: `confirm_block_service_${serviceId}_${clienteId}_${location.replace(/\s+/g, '_')}` }, - { text: '❌ Cancelar', callback_data: 'cancel' } - ] - ] - }; - - await this.bot.editMessageText( - texto, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - reply_markup: keyboard, - parse_mode: 'Markdown' - } - ); - - } catch (error) { - console.error('❌ Erro ao selecionar serviço para bloqueio:', error); - await this.bot.editMessageText( - `❌ **Erro interno ao selecionar serviço**\n\n` + - `⚠️ ${error.message}`, - { - chat_id: callbackQuery.message.chat.id, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - } - } - - async handleUnblockServiceSelection(callbackQuery, data) { - try { - // Extrair dados: unblock_service_ID_CLIENTE_LOCATION - const parts = data.replace('unblock_service_', '').split('_'); - const serviceId = parts[0]; - const clienteId = parts[1]; - const location = parts.slice(2).join('_').replace(/_/g, ' '); - - const chatId = callbackQuery.message.chat.id; - - // Criar mensagem de confirmação - const texto = `🔸 **Confirmar Liberação de Serviço**\n\n` + - `🆔 **ID do Serviço:** ${serviceId}\n` + - `👤 **Cliente:** ${clienteId}\n` + - `📍 **Localização:** ${location}\n\n` + - `✅ Tem certeza que deseja liberar este serviço?`; - - const keyboard = { - inline_keyboard: [ - [ - { text: '✅ Sim, liberar', callback_data: `confirm_unblock_service_${serviceId}_${clienteId}_${location.replace(/\s+/g, '_')}` }, - { text: '❌ Cancelar', callback_data: 'cancel' } - ] - ] - }; - - await this.bot.editMessageText( - texto, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - reply_markup: keyboard, - parse_mode: 'Markdown' - } - ); - - } catch (error) { - console.error('❌ Erro ao selecionar serviço para liberação:', error); - await this.bot.editMessageText( - `❌ **Erro interno ao selecionar serviço**\n\n` + - `⚠️ ${error.message}`, - { - chat_id: callbackQuery.message.chat.id, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - } - } - - /** - * Visualizar logs recentes - */ - async handleViewLogs(chatId) { - const userId = chatId; - - if (!this.isAuthorized(userId)) { - await this.bot.sendMessage(chatId, '🚫 Acesso negado.'); - return; - } - - try { - const recentLogs = logger.getRecentLogs(50); - - if (recentLogs.length === 0) { - await this.bot.sendMessage(chatId, - `📋 **Logs do Sistema**\n\n` + - `ℹ️ Nenhum log encontrado ou modo debug desabilitado.`, - { parse_mode: 'Markdown' } - ); - return; - } - - // Dividir logs em mensagens menores (Telegram tem limite de caracteres) - const maxLength = 4000; - let currentMessage = `📋 **Logs Recentes (${recentLogs.length} linhas)**\n\n\`\`\`\n`; - - for (const line of recentLogs) { - const newLine = line + '\n'; - - if ((currentMessage + newLine + '```').length > maxLength) { - // Enviar mensagem atual e começar nova - currentMessage += '```'; - await this.bot.sendMessage(chatId, currentMessage, { parse_mode: 'Markdown' }); - currentMessage = '```\n' + newLine; - } else { - currentMessage += newLine; - } - } - - // Enviar último bloco - if (currentMessage.length > 10) { - currentMessage += '```'; - await this.bot.sendMessage(chatId, currentMessage, { parse_mode: 'Markdown' }); - } - - } catch (error) { - logger.botError(userId, error, 'handleViewLogs'); - await this.bot.sendMessage(chatId, - `❌ **Erro ao visualizar logs**\n\n` + - `⚠️ ${error.message}`, - { parse_mode: 'Markdown' } - ); - } - } - - /** - * Limpar logs do sistema - */ - async handleClearLogs(chatId) { - const userId = chatId; - - if (!this.isAuthorized(userId)) { - await this.bot.sendMessage(chatId, '🚫 Acesso negado.'); - return; - } - - try { - logger.clearLogs(); - logger.info('Logs limpos pelo usuário administrativo', { userId }); - - await this.bot.sendMessage(chatId, - `🧹 **Logs Limpos**\n\n` + - `✅ Todos os logs foram removidos com sucesso.`, - { parse_mode: 'Markdown' } - ); - - } catch (error) { - logger.botError(userId, error, 'handleClearLogs'); - await this.bot.sendMessage(chatId, - `❌ **Erro ao limpar logs**\n\n` + - `⚠️ ${error.message}`, - { parse_mode: 'Markdown' } - ); - } - } - - /** - * Callback para selecionar cliente para suporte - */ - async handleSuporteClienteCallback(callbackQuery, data) { - await this.bot.answerCallbackQuery(callbackQuery.id); - - const chatId = callbackQuery.message.chat.id; - const userId = callbackQuery.from.id; - - const parts = data.split('_'); - const clienteIndex = parseInt(parts[2]); - - console.log(`🎫 Cliente selecionado para suporte: índice ${clienteIndex}`); - - // Encontrar sessão ativa para este usuário - const sessaoAtiva = Array.from(this.suporteSessions.values()).find( - session => session.chatId === chatId && session.userId === userId - ); - - if (!sessaoAtiva) { - await this.bot.editMessageText( - '❌ **Sessão expirada**\n\nPor favor, inicie novamente com o comando "abrir suporte".', - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - return; - } - - if (!sessaoAtiva.dados || !sessaoAtiva.dados.clientesEncontrados || clienteIndex >= sessaoAtiva.dados.clientesEncontrados.length) { - await this.bot.editMessageText( - '❌ **Cliente inválido**\n\nPor favor, tente novamente.', - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - return; - } - - // Selecionar cliente - sessaoAtiva.dados.clienteSelecionado = sessaoAtiva.dados.clientesEncontrados[clienteIndex]; - sessaoAtiva.etapa = 'selecionar_servico'; - - console.log(`✅ Cliente selecionado: ${sessaoAtiva.dados.clienteSelecionado.nome}`); - - // Atualizar mensagem para mostrar cliente selecionado - await this.bot.editMessageText( - `✅ **Cliente selecionado:**\n${sessaoAtiva.dados.clienteSelecionado.nome}\n\n🔍 Buscando serviços...`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - - // Continuar para próxima etapa - await this.processarEtapaSelecionarServico(sessaoAtiva.id); - } - - /** - * Callback para selecionar serviço para suporte - */ - async handleSuporteServicoCallback(callbackQuery, data) { - await this.bot.answerCallbackQuery(callbackQuery.id); - - const chatId = callbackQuery.message.chat.id; - const userId = callbackQuery.from.id; - - const parts = data.split('_'); - const servicoIndex = parseInt(parts[2]); - - console.log(`🎫 Serviço selecionado para suporte: índice ${servicoIndex}`); - - // Encontrar sessão ativa para este usuário - const sessaoAtiva = Array.from(this.suporteSessions.values()).find( - session => session.chatId === chatId && session.userId === userId - ); - - if (!sessaoAtiva) { - await this.bot.editMessageText( - '❌ **Sessão expirada**\n\nPor favor, inicie novamente com o comando "abrir suporte".', - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - return; - } - - if (!sessaoAtiva.dados || !sessaoAtiva.dados.servicos || servicoIndex >= sessaoAtiva.dados.servicos.length) { - await this.bot.editMessageText( - '❌ **Serviço inválido**\n\nPor favor, tente novamente.', - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - return; - } - - // Selecionar serviço - sessaoAtiva.dados.servicoSelecionado = sessaoAtiva.dados.servicos[servicoIndex]; - sessaoAtiva.etapa = 'selecionar_categoria'; - - console.log(`✅ Serviço selecionado: ${sessaoAtiva.dados.servicoSelecionado.nome}`); - - // Atualizar mensagem para mostrar serviço selecionado - await this.bot.editMessageText( - `✅ **Serviço selecionado:**\n${sessaoAtiva.dados.servicoSelecionado.nome}\n\n🔍 Buscando categorias de suporte...`, - { - chat_id: chatId, - message_id: callbackQuery.message.message_id, - parse_mode: 'Markdown' - } - ); - - // Continuar para próxima etapa - await this.processarEtapaSelecionarCategoria(sessaoAtiva.id); - } - - /** - * Callback para mostrar dados cadastrais do cliente - */ - async handleDadosClienteCallback(callbackQuery, data) { - await this.bot.answerCallbackQuery(callbackQuery.id); - - const parts = data.split('_'); - const idCliente = parts[2]; - const location = parts[3] || ''; - - const chatId = callbackQuery.message.chat.id; - await this.handleDadosCliente(chatId, idCliente, location); - } - - /** - * Callback para mostrar faturas do cliente - */ - async handleFaturasClienteCallback(callbackQuery, data) { - await this.bot.answerCallbackQuery(callbackQuery.id); - - const parts = data.split('_'); - const idCliente = parts[2]; - const location = parts[3] || ''; - - const chatId = callbackQuery.message.chat.id; - await this.handleVerFaturasById(chatId, idCliente, location); - } - - /** - * Callback para mostrar serviços do cliente - */ - async handleServicosClienteCallback(callbackQuery, data) { - await this.bot.answerCallbackQuery(callbackQuery.id); - - const parts = data.split('_'); - const idCliente = parts[2]; - const location = parts[3] || ''; - - const chatId = callbackQuery.message.chat.id; - await this.handleListarServicos(chatId, idCliente); - } - - /** - * Ver faturas do cliente por ID com organização por serviço - */ - async handleVerFaturasById(chatId, idCliente, location = '') { - try { - await this.bot.sendMessage(chatId, '📄 Buscando faturas detalhadas...'); - - const resultado = await this.topsapp.obterFaturasDetalhadas(idCliente, location); - - if (!resultado.success) { - await this.bot.sendMessage(chatId, - `❌ **Erro ao buscar faturas**\n\n` + - `⚠️ ${resultado.error}`, - { parse_mode: 'Markdown' } - ); - return; - } - - if (!resultado.faturasPorServico || resultado.faturasPorServico.length === 0) { - await this.bot.sendMessage(chatId, - `📄 **Nenhuma fatura encontrada**\n\n` + - `👤 Cliente ID: ${idCliente}\n` + - `🌐 Instância: ${resultado.instancia || location}\n\n` + - `✅ O cliente está em dia com os pagamentos!`, - { parse_mode: 'Markdown' } - ); - return; - } - - // Cabeçalho principal - let headerMessage = `📄 **FATURAS EM ABERTO POR SERVIÇO**\n\n`; - headerMessage += `👤 **Cliente ID**: ${idCliente}\n`; - headerMessage += `📊 **Total de serviços**: ${resultado.totalServicos}\n`; - headerMessage += `💳 **Total de faturas**: ${resultado.totalFaturas}\n`; - headerMessage += `🌐 **Instância**: ${resultado.instancia || location}\n\n`; - - // Enviar cabeçalho - await this.bot.sendMessage(chatId, headerMessage, { parse_mode: 'Markdown' }); - - // Processar cada serviço separadamente - for (let i = 0; i < resultado.faturasPorServico.length; i++) { - const servicoData = resultado.faturasPorServico[i]; - const servico = servicoData.servico; - const faturas = servicoData.faturas; - - // Informações do serviço - let servicoMessage = `🔧 **SERVIÇO ${i + 1}: ${servico.nome}**\n`; - servicoMessage += `🏷️ Base: ${servico.base}\n`; - servicoMessage += `⚙️ Tecnologia: ${servico.tecnologia}\n`; - - if (servico.valor_mensal && servico.valor_mensal > 0) { - servicoMessage += `💰 Valor mensal: R$ ${parseFloat(servico.valor_mensal).toFixed(2)}\n`; - } - - if (servico.vencimento_dia !== 'Variável' && servico.vencimento_dia !== 'Não definido') { - servicoMessage += `📅 Vencimento: Todo dia ${servico.vencimento_dia}\n`; - } - - servicoMessage += `📊 Status do serviço: ${servico.status}\n\n`; - - // Enviar informações do serviço - await this.bot.sendMessage(chatId, servicoMessage, { parse_mode: 'Markdown' }); - - // Dividir faturas em grupos menores para evitar overflow - const faturasPorMensagem = 3; // Máximo 3 faturas por mensagem - for (let j = 0; j < faturas.length; j += faturasPorMensagem) { - const faturasGrupo = faturas.slice(j, j + faturasPorMensagem); - let faturaMessage = ''; - - faturasGrupo.forEach((fatura, index) => { - const valor = parseFloat(fatura.valor || 0); - const faturaIndex = j + index + 1; - - // Destacar a data de vencimento - const dataVencimento = this.formatarData(fatura.vencimento); - const isVencida = new Date(fatura.vencimento) < new Date(); - const statusVencimento = isVencida ? '🔴 VENCIDA' : '🟡 EM ABERTO'; - - faturaMessage += `📄 **Fatura ${faturaIndex}**\n`; - faturaMessage += `🆔 ID: \`${fatura.id}\`\n`; - faturaMessage += `💰 **Valor: R$ ${valor.toFixed(2)}**\n`; - faturaMessage += `⏰ **VENCIMENTO: ${dataVencimento} ${statusVencimento}**\n`; - - if (fatura.periodo) { - faturaMessage += `📅 Período: ${fatura.periodo}\n`; - } - - if (fatura.descricao) { - faturaMessage += `📝 Descrição: ${fatura.descricao}\n`; - } - - faturaMessage += `📊 Status: ${this.getStatusFatura(fatura.status)}\n`; - - // Linha digitável - if (fatura.linhaDigitavel) { - faturaMessage += `\n📋 **Linha Digitável:**\n\`${fatura.linhaDigitavel}\`\n`; - } - - // PIX Copia e Cola - if (fatura.pixCopiaCola) { - faturaMessage += `\n💲 **PIX Copia e Cola:**\n\`${fatura.pixCopiaCola}\`\n`; - } - - faturaMessage += `\n`; - }); - - // Enviar grupo de faturas - await this.bot.sendMessage(chatId, faturaMessage, { parse_mode: 'Markdown' }); - } - } - - // Enviar botões de ação na mensagem final - const keyboard = [ - [ - { text: '📋 Dados Cadastrais', callback_data: `dados_cliente_${idCliente}_${location}` }, - { text: '⚙️ Ver Serviços', callback_data: `servicos_cliente_${idCliente}_${location}` } - ] - ]; - - if (resultado.faturasPorServico.length > 0 && resultado.faturasPorServico[0].faturas.length > 0) { - const primeiraFatura = resultado.faturasPorServico[0].faturas[0]; - keyboard.push([ - { text: '📄 Gerar Boleto PDF', callback_data: `boleto_pdf_${primeiraFatura.id}_${location}` } - ]); - } - - await this.bot.sendMessage(chatId, '🔧 **Ações disponíveis:**', { - parse_mode: 'Markdown', - reply_markup: { - inline_keyboard: keyboard - } - }); - - } catch (error) { - console.error('❌ Erro ao buscar faturas detalhadas:', error); - await this.bot.sendMessage(chatId, - `❌ **Erro ao buscar faturas**\n\n` + - `⚠️ ${error.message}`, - { parse_mode: 'Markdown' } - ); - } - } - - /** - for (let i = 0; i < resultado.faturasPorServico.length; i++) { - const servicoData = resultado.faturasPorServico[i]; - const servico = servicoData.servico; - const faturas = servicoData.faturas; - - message += `🔧 **SERVIÇO ${i + 1}: ${servico.nome}**\n`; - message += `🏷️ Base: ${servico.base}\n`; - message += `⚙️ Tecnologia: ${servico.tecnologia}\n`; - - if (servico.valor_mensal && servico.valor_mensal > 0) { - message += `💰 Valor mensal: R$ ${parseFloat(servico.valor_mensal).toFixed(2)}\n`; - } - - if (servico.vencimento_dia !== 'Variável' && servico.vencimento_dia !== 'Não definido') { - message += `📅 Vencimento: Todo dia ${servico.vencimento_dia}\n`; - } - - message += `📊 Status do serviço: ${servico.status}\n\n`; - - let valorTotalServico = 0; - - // Processar faturas do serviço - faturas.forEach((fatura, index) => { - const valor = parseFloat(fatura.valor || 0); - valorTotalServico += valor; - valorTotalGeral += valor; - - // Destacar a data de vencimento - const dataVencimento = this.formatarData(fatura.vencimento); - const isVencida = new Date(fatura.vencimento) < new Date(); - const statusVencimento = isVencida ? '🔴 VENCIDA' : '🟡 EM ABERTO'; - - message += `� **Fatura ${index + 1}**\n`; - message += `🆔 ID: \`${fatura.id}\`\n`; - message += `💰 **Valor: R$ ${valor.toFixed(2)}**\n`; - message += `⏰ **VENCIMENTO: ${dataVencimento} ${statusVencimento}**\n`; - - if (fatura.periodo) { - message += `📅 Período: ${fatura.periodo}\n`; - } - - if (fatura.descricao) { - message += `📝 Descrição: ${fatura.descricao}\n`; - } - - message += `📊 Status: ${this.getStatusFatura(fatura.status)}\n`; - - // Linha digitável - if (fatura.linhaDigitavel) { - message += `\n📋 **Linha Digitável:**\n\`${fatura.linhaDigitavel}\`\n`; - } - - // PIX Copia e Cola - if (fatura.pixCopiaCola) { - message += `\n💲 **PIX Copia e Cola:**\n\`${fatura.pixCopiaCola}\`\n`; - } - - message += `\n`; - }); - - message += `💰 **Subtotal do serviço: R$ ${valorTotalServico.toFixed(2)}**\n`; - message += `${'-'.repeat(30)}\n\n`; - } - - message += `💰 **TOTAL GERAL EM ABERTO: R$ ${valorTotalGeral.toFixed(2)}**\n\n`; - - // Botões de ação - const keyboard = [ - [ - { text: '📋 Dados Cadastrais', callback_data: `dados_cliente_${idCliente}_${location}` }, - { text: '⚙️ Ver Serviços', callback_data: `servicos_cliente_${idCliente}_${location}` } - ] - ]; - // Adicionar botão para gerar PDF da primeira fatura if (resultado.faturasPorServico.length > 0 && resultado.faturasPorServico[0].faturas.length > 0) { const primeiraFatura = resultado.faturasPorServico[0].faturas[0]; @@ -4742,58 +3882,63 @@ Digite /help para ver todos os comandos disponíveis! resultado = await this.fiberhome.obterStatusONUPorNome(target); } - if (!resultado.success) { - // Escapar caracteres especiais para evitar problemas no Telegram - const errorMsg = resultado.error ? resultado.error.replace(/[_*\[\]()~`>#+=|{}.!-]/g, '\\$&') : 'Erro desconhecido'; - - await this.bot.sendMessage(chatId, - `❌ **Erro ao obter status da ONU**\n\n` + - `🔍 **Busca:** \`${target}\`\n` + - `⚠️ **Erro:** ${errorMsg}`, - { parse_mode: 'Markdown' } - ); - return; - } + if (resultado.success) { + // Formatar informações da ONU + let message = `📡 **Status ONU Fiberhome**\n\n`; + message += `🆔 **Serial:** ${resultado.serial}\n`; + message += `📋 **Device Name:** ${resultado.deviceName}\n`; + message += `📍 **Posição:** Slot ${resultado.slot}, PON ${resultado.pon}, ONU ${resultado.onuId}\n`; + message += `🔌 **Status:** ${resultado.online ? '🟢 Online' : '🔴 Offline'}\n\n`; - // Formatar informações da ONU - let message = `📡 **Status ONU Fiberhome**\n\n`; - message += `🆔 **Serial:** ${resultado.serial}\n`; - message += `📋 **Device Name:** ${resultado.deviceName}\n`; - message += `📍 **Posição:** Slot ${resultado.slot}, PON ${resultado.pon}, ONU ${resultado.onuId}\n`; - message += `🔌 **Status:** ${resultado.online ? '🟢 Online' : '🔴 Offline'}\n\n`; + // Informações de sinal + message += `📶 **Informações de Sinal:**\n`; + message += `📡 **RX Power:** ${resultado.rxPower}\n`; + message += `📤 **TX Power:** ${resultado.txPower}\n`; + message += `📏 **Distância:** ${resultado.distance}\n\n`; - // Informações de sinal - message += `📶 **Informações de Sinal:**\n`; - message += `📡 **RX Power:** ${resultado.rxPower}\n`; - message += `📤 **TX Power:** ${resultado.txPower}\n`; - message += `📏 **Distância:** ${resultado.distance}\n\n`; + // Status das portas Ethernet (resumido) + if (resultado.portasEthernet.length > 0) { + message += `🔗 **Portas Ethernet:**\n`; + resultado.portasEthernet.forEach(porta => { + const statusIcon = porta.status === 'up' ? '🟢' : '🔴'; + message += ` ${statusIcon} **Porta ${porta.porta}:** ${porta.status}\n`; + }); + message += '\n'; + } - // Status das portas Ethernet - if (resultado.portasEthernet.length > 0) { - message += `🔗 **Portas Ethernet:**\n`; - resultado.portasEthernet.forEach(porta => { - const statusIcon = porta.status === 'up' ? '🟢' : '🔴'; - message += ` ${statusIcon} **Porta ${porta.porta}:** ${porta.status}\n`; - }); - message += '\n'; - } + // Serviços WAN + if (resultado.servicosWAN.length > 0) { + message += `🌐 **Serviços WAN:**\n`; + resultado.servicosWAN.forEach(servico => { + const statusIcon = servico.status === 'active' ? '🟢' : '🔴'; + message += ` ${statusIcon} **Serviço ${servico.id}:** ${servico.status}\n`; + if (servico.ip !== 'N/A') { + message += ` 📍 **IP:** ${servico.ip}\n`; + } + }); + } else { + message += `🌐 **Serviços WAN:** Nenhum configurado\n`; + } + + await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); - // Serviços WAN - if (resultado.servicosWAN.length > 0) { - message += `🌐 **Serviços WAN:**\n`; - resultado.servicosWAN.forEach(servico => { - const statusIcon = servico.status === 'active' ? '🟢' : '🔴'; - message += ` ${statusIcon} **Serviço ${servico.id}:** ${servico.status}\n`; - if (servico.ip !== 'N/A') { - message += ` 📍 **IP:** ${servico.ip}\n`; - } - }); } else { - message += `🌐 **Serviços WAN:** Nenhum configurado\n`; + await this.bot.sendMessage(chatId, `❌ ONU ${target} não encontrada na OLT Fiberhome.`); + + // Oferecer pesquisar em OLTs Datacom + const keyboard = { + inline_keyboard: [ + [ + { text: '🔍 Pesquisar em OLT Datacom', callback_data: `ver_sinal_${target}` }, + { text: '❌ Cancelar', callback_data: 'cancel_action' } + ] + ] + }; + + await this.bot.sendMessage(chatId, 'ONU não encontrada na Fiberhome. Deseja pesquisar nas OLTs Datacom?', { + reply_markup: keyboard + }); } - - await this.bot.sendMessage(chatId, message, { parse_mode: 'Markdown' }); - } catch (error) { logger.botError(chatId, error, 'handleStatusONU'); diff --git a/src/topsapp/api.js b/src/topsapp/api.js index 4e5d254..be81b2a 100644 --- a/src/topsapp/api.js +++ b/src/topsapp/api.js @@ -508,10 +508,18 @@ class TopSappAPI { if (response.success && response.data) { console.log(`✅ Sucesso na tentativa ${i + 1} - Total de itens: ${response.data.dados?.length || response.data.length || 0}`); - logger.debug(`Sucesso na tentativa ${i + 1}`, { - totalItems: response.data.dados?.length || response.data.length || 0, - hasData: !!response.data - }); + + // Normalizar os dados de status dos serviços + if (response.data.dados && Array.isArray(response.data.dados)) { + response.data.dados = response.data.dados.map(servico => ({ + ...servico, + // Garantir que id_status está consistente + id_status: this.interpretarStatusServico(servico), + status: this.obterTextoStatus(this.interpretarStatusServico(servico)) + })); + + console.log(`🔄 Status dos serviços normalizados`); + } // Verificar se os dados são relevantes para o cliente const dados = response.data.dados || response.data; @@ -551,6 +559,67 @@ class TopSappAPI { return response; } + /** + * Interpretar status do serviço corretamente + */ + interpretarStatusServico(servico) { + // Verificar diferentes campos de status + + // Se tem id_status explícito, usar ele + if (servico.id_status) { + return parseInt(servico.id_status, 10); + } + + // Interpretar pelo campo status textual + if (servico.status) { + const statusLower = servico.status.toLowerCase(); + + if (statusLower.includes('liberado') || statusLower.includes('ativo')) { + return 1; // Ativo/Liberado + } + + if (statusLower.includes('bloqueado')) { + return 2; // Bloqueado + } + + if (statusLower.includes('desativado') || statusLower.includes('inativo')) { + return 3; // Desativado + } + } + + // Verificar por datas + if (servico.data_desativacao && servico.data_desativacao !== '0000-00-00' && servico.data_desativacao !== null) { + return 3; // Desativado + } + + if (servico.data_bloqueio && servico.data_bloqueio !== '0000-00-00' && servico.data_bloqueio !== null) { + // Verificar se foi desbloqueado depois + if (!servico.data_desbloqueio || servico.data_desbloqueio === '0000-00-00' || servico.data_desbloqueio === null) { + return 2; // Bloqueado + } + + // Se tem data de desbloqueio, verificar qual é mais recente + if (new Date(servico.data_desbloqueio) < new Date(servico.data_bloqueio)) { + return 2; // Bloqueado (bloqueio é mais recente) + } + } + + // Por padrão, considerar ativo + return 1; + } + + /** + * Obter texto descritivo do status + */ + obterTextoStatus(idStatus) { + switch (parseInt(idStatus, 10)) { + case 1: return 'liberado'; + case 2: return 'bloqueado'; + case 3: return 'desativado'; + default: return 'desconhecido'; + } + } + /** * Obter faturas em aberto do cliente */ diff --git a/src/topsapp/manager.js b/src/topsapp/manager.js index 0b05158..d15dac8 100644 --- a/src/topsapp/manager.js +++ b/src/topsapp/manager.js @@ -1165,6 +1165,49 @@ class TopSappManager { return 'Período não informado'; } } + + /** + * Alias para obterServicosCliente + */ + async obterServicos(idCliente, location = '') { + return await this.obterServicosCliente(idCliente, location); + } + + /** + * Alias para bloquearServicoEspecifico + */ + async bloquearServico(idServico, idCliente, location = '') { + return await this.bloquearServicoEspecifico(idCliente, idServico, location); + } + + /** + * Alias para liberarServicoEspecifico + */ + async liberarServico(idServico, idCliente, location = '') { + return await this.liberarServicoEspecifico(idCliente, idServico, location); + } + + /** + * Obter faturas do cliente + */ + async obterFaturas(idCliente, location = '') { + if (!this.isReady()) { + return { + success: false, + error: 'TopSapp não está configurado' + }; + } + + try { + const credentials = this.config.getCredentialsByLocation(location); + return await this.api.obterFaturas(idCliente, location, credentials); + } catch (error) { + return { + success: false, + error: error.message + }; + } + } } module.exports = TopSappManager;