topsapp: estruturar suporte
Signed-off-by: Rodrigo B. de Sousa Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
1 parent
723ad8c268
commit
8dc6c594c9
7 files changed
+1685
-102
No files matched your search
Generated
+39
@@ -9,7 +9,9 @@
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"form-data": "^4.0.0",
|
||||
"node-telegram-bot-api": "^0.64.0",
|
||||
"ssh2": "^1.15.0"
|
||||
},
|
||||
@@ -227,6 +229,17 @@
|
||||
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
|
||||
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
@@ -781,6 +794,26 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||
@@ -1819,6 +1852,12 @@
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/psl": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||
|
||||
+930
-52
File diff suppressed because it is too large.
Load diff
+27
-3
@@ -169,9 +169,9 @@ class NLPProcessor {
|
||||
};
|
||||
}
|
||||
|
||||
// Detectar comandos TopSapp - bloquear cliente
|
||||
if (/bloquear|suspender|cortar/i.test(action) && /cliente|user/i.test(fullMatch)) {
|
||||
let clienteId = match[3] || match[2]; // ID do cliente
|
||||
// Detectar comandos TopSapp - bloquear cliente/serviço
|
||||
if (/bloquear|suspender|cortar/i.test(action)) {
|
||||
let clienteId = match[3] || match[2]; // ID/CPF/Nome do cliente
|
||||
|
||||
return {
|
||||
action: 'bloquear_cliente',
|
||||
@@ -181,6 +181,30 @@ class NLPProcessor {
|
||||
};
|
||||
}
|
||||
|
||||
// Detectar comandos TopSapp - listar serviços
|
||||
if (/listar|ver|mostrar/i.test(action) && /serviços?|produtos?|planos?/i.test(fullMatch)) {
|
||||
let clienteId = match[3] || match[2]; // ID/CPF/Nome do cliente
|
||||
|
||||
return {
|
||||
action: 'listar_servicos',
|
||||
target: clienteId ? clienteId.trim() : null,
|
||||
confidence: 0.95,
|
||||
type: 'topsapp_services_pattern'
|
||||
};
|
||||
}
|
||||
|
||||
// Detectar comandos TopSapp - liberar cliente/serviço
|
||||
if (/liberar|ativar|religar|reconectar/i.test(action)) {
|
||||
let clienteId = match[3] || match[2]; // ID/CPF/Nome do cliente
|
||||
|
||||
return {
|
||||
action: 'liberar_cliente',
|
||||
target: clienteId ? clienteId.trim() : null,
|
||||
confidence: 0.95,
|
||||
type: 'topsapp_unblock_pattern'
|
||||
};
|
||||
}
|
||||
|
||||
// Detectar comandos TopSapp - liberar cliente
|
||||
if (/liberar|desbloquear|reconectar/i.test(action) && /cliente|user/i.test(fullMatch)) {
|
||||
let clienteId = match[3] || match[2]; // ID do cliente
|
||||
|
||||
+152
-28
@@ -1,5 +1,6 @@
|
||||
const axios = require('axios');
|
||||
const https = require('https');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
class TopSappAPI {
|
||||
constructor() {
|
||||
@@ -23,10 +24,8 @@ class TopSappAPI {
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
}),
|
||||
timeout: 30000,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
timeout: 30000
|
||||
// Não definir Content-Type, deixar o FormData fazer isso automaticamente
|
||||
};
|
||||
|
||||
console.log('🔗 TopSappAPI inicializado');
|
||||
@@ -53,31 +52,45 @@ class TopSappAPI {
|
||||
async login(instance, credentials) {
|
||||
try {
|
||||
console.log(`🔐 Fazendo login na instância: ${instance.name}`);
|
||||
console.log(`📍 URL: ${instance.baseURL}/Login`);
|
||||
console.log(`👤 Usuario: ${credentials.usuario}`);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('usuario', credentials.usuario);
|
||||
formData.append('senha', credentials.senha);
|
||||
formData.append('identificador', credentials.identificador || 'BotTelegram');
|
||||
formData.append('idUsuario', credentials.idUsuario || '1');
|
||||
|
||||
const response = await axios.post(
|
||||
`${instance.baseURL}/Login`,
|
||||
formData,
|
||||
this.axiosConfig
|
||||
{
|
||||
...this.axiosConfig,
|
||||
timeout: 10000
|
||||
}
|
||||
);
|
||||
|
||||
if (response.data && response.data.status === 'success') {
|
||||
console.log(`📊 Status da resposta: ${response.status}`);
|
||||
console.log(`📋 Dados da resposta:`, response.data);
|
||||
|
||||
if (response.data && response.data.resultado === true) {
|
||||
const sessionId = response.data.sessao;
|
||||
const idUsuario = response.data.id_usuario;
|
||||
|
||||
this.sessions.set(instance.baseURL, {
|
||||
sessionId,
|
||||
idUsuario,
|
||||
timestamp: Date.now(),
|
||||
instance
|
||||
});
|
||||
|
||||
console.log(`✅ Login realizado com sucesso na instância: ${instance.name}`);
|
||||
return { success: true, sessionId, instance };
|
||||
console.log(`🎫 Sessão: ${sessionId}`);
|
||||
console.log(`👤 ID do usuário: ${idUsuario}`);
|
||||
return { success: true, sessionId, idUsuario, instance };
|
||||
} else {
|
||||
throw new Error(response.data?.message || 'Login falhou');
|
||||
const errorMsg = response.data?.msg || 'Login falhou';
|
||||
console.log(`❌ Erro no login: ${errorMsg}`);
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -127,15 +140,19 @@ class TopSappAPI {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('sessao', session.sessionId);
|
||||
formData.append('identificador', credentials.identificador || 'BotTelegram');
|
||||
|
||||
// Adicionar parâmetros extras
|
||||
Object.keys(data).forEach(key => {
|
||||
if (data[key] !== undefined && data[key] !== null) {
|
||||
formData.append(key, data[key]);
|
||||
// Converter idCliente para inteiro se necessário
|
||||
const value = key === 'idCliente' ? parseInt(data[key], 10) : data[key];
|
||||
formData.append(key, value.toString());
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`📡 Fazendo requisição para: ${instance.baseURL}/${endpoint}`);
|
||||
console.log(`📊 Parâmetros:`, data);
|
||||
|
||||
const response = await axios.post(
|
||||
`${instance.baseURL}/${endpoint}`,
|
||||
@@ -143,14 +160,19 @@ class TopSappAPI {
|
||||
this.axiosConfig
|
||||
);
|
||||
|
||||
if (response.data && response.data.status === 'success') {
|
||||
console.log(`📊 Status da resposta: ${response.status}`);
|
||||
console.log(`📋 Dados da resposta:`, response.data);
|
||||
|
||||
if (response.data && response.data.resultado === true) {
|
||||
return {
|
||||
success: true,
|
||||
data: response.data,
|
||||
instance: instance.name
|
||||
};
|
||||
} else {
|
||||
throw new Error(response.data?.message || 'Requisição falhou');
|
||||
const errorMsg = response.data?.msg || 'Requisição falhou';
|
||||
console.log(`❌ Erro na API: ${errorMsg}`);
|
||||
throw new Error(errorMsg);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
@@ -170,19 +192,26 @@ class TopSappAPI {
|
||||
const instance = this.getInstanceByLocation(location);
|
||||
|
||||
try {
|
||||
// Obter sessão válida para pegar o idUsuario
|
||||
const session = await this.getValidSession(instance, credentials);
|
||||
|
||||
// Tentar buscar por diferentes critérios
|
||||
const searchParams = {};
|
||||
const searchParams = {
|
||||
idUsuario: session.idUsuario || 1 // Usar ID do usuário da sessão
|
||||
};
|
||||
|
||||
// Se é número, pode ser CPF ou ID
|
||||
if (/^\d+$/.test(query)) {
|
||||
if (query.length === 11) {
|
||||
searchParams.cpf = query;
|
||||
if (query.length === 11 || query.length === 14) {
|
||||
// CPF (11 dígitos) ou CNPJ (14 dígitos)
|
||||
searchParams.cpfCnpj = query;
|
||||
} else {
|
||||
// ID do cliente
|
||||
searchParams.idCliente = query;
|
||||
}
|
||||
} else {
|
||||
// Buscar por nome
|
||||
searchParams.nome = query;
|
||||
// CPF formatado ou nome - usar o endpoint de busca geral
|
||||
searchParams.cpfCnpj = query.replace(/[^\d]/g, ''); // Remove formatação
|
||||
}
|
||||
|
||||
const result = await this.makeRequest('ObterClientes', searchParams, instance, credentials);
|
||||
@@ -190,7 +219,7 @@ class TopSappAPI {
|
||||
if (result.success) {
|
||||
return {
|
||||
success: true,
|
||||
clientes: result.data.clientes || [],
|
||||
clientes: result.data.dados || [], // Usar 'dados' em vez de 'clientes'
|
||||
instance: result.instance
|
||||
};
|
||||
}
|
||||
@@ -221,7 +250,85 @@ class TopSappAPI {
|
||||
async obterServicosCliente(idCliente, location = '', credentials) {
|
||||
const instance = this.getInstanceByLocation(location);
|
||||
|
||||
return await this.makeRequest('ObterClientesServicos', { idCliente }, instance, credentials);
|
||||
// Obter o idUsuario da sessão atual
|
||||
const session = await this.getValidSession(instance, credentials);
|
||||
|
||||
console.log(`🔍 Obtendo serviços para cliente: ${idCliente} (tipo: ${typeof idCliente})`);
|
||||
|
||||
const clienteIdInt = parseInt(idCliente, 10);
|
||||
|
||||
// Tentar diferentes abordagens para obter serviços do cliente
|
||||
let response = null;
|
||||
let tentativas = [
|
||||
// Tentativa 1: Endpoint padrão com idCliente
|
||||
{ endpoint: 'ObterClientesServicos', data: { idCliente: clienteIdInt, idUsuario: session.idUsuario || 1 } },
|
||||
// Tentativa 2: Endpoint alternativo (se existir)
|
||||
{ endpoint: 'ObterServicosCliente', data: { idCliente: clienteIdInt, idUsuario: session.idUsuario || 1 } },
|
||||
// Tentativa 3: Com parâmetros alternativos
|
||||
{ endpoint: 'ObterClientesServicos', data: { cliente_id: clienteIdInt, idUsuario: session.idUsuario || 1 } }
|
||||
];
|
||||
|
||||
logger.debug('Iniciando tentativas de busca de serviços', {
|
||||
clienteIdInt,
|
||||
sessionId: session.idUsuario,
|
||||
tentativasCount: tentativas.length
|
||||
});
|
||||
|
||||
for (let i = 0; i < tentativas.length; i++) {
|
||||
const tentativa = tentativas[i];
|
||||
try {
|
||||
console.log(`📋 Tentativa ${i + 1} - Endpoint: ${tentativa.endpoint}, Dados:`, tentativa.data);
|
||||
logger.debug(`Tentativa ${i + 1}`, {
|
||||
endpoint: tentativa.endpoint,
|
||||
data: tentativa.data
|
||||
});
|
||||
|
||||
response = await this.makeRequest(tentativa.endpoint, tentativa.data, instance, credentials);
|
||||
logger.apiCall(tentativa.endpoint, tentativa.data, response);
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
// Verificar se os dados são relevantes para o cliente
|
||||
const dados = response.data.dados || response.data;
|
||||
if (Array.isArray(dados) && dados.length > 0) {
|
||||
// Verificar quantos itens realmente pertencem ao cliente
|
||||
const servicosDoCliente = dados.filter(item => {
|
||||
const itemClienteId = parseInt(item.idCliente || item.cliente_id || item.id_cliente || 0, 10);
|
||||
return itemClienteId === clienteIdInt;
|
||||
});
|
||||
|
||||
console.log(`🎯 Serviços filtrados para cliente ${clienteIdInt}: ${servicosDoCliente.length} de ${dados.length} total`);
|
||||
logger.debug('Filtragem de serviços', {
|
||||
clienteIdBuscado: clienteIdInt,
|
||||
servicosEncontrados: servicosDoCliente.length,
|
||||
totalServicos: dados.length,
|
||||
primeiroItem: dados[0] ? {
|
||||
id_cliente: dados[0].id_cliente,
|
||||
idCliente: dados[0].idCliente,
|
||||
cliente_id: dados[0].cliente_id
|
||||
} : null
|
||||
});
|
||||
|
||||
// Se encontrou serviços específicos do cliente, usar essa resposta
|
||||
if (servicosDoCliente.length > 0 || i === tentativas.length - 1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`❌ Falha na tentativa ${i + 1}: ${error.message}`);
|
||||
if (i === tentativas.length - 1) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,19 +337,31 @@ class TopSappAPI {
|
||||
async obterFaturas(idCliente, location = '', credentials) {
|
||||
const instance = this.getInstanceByLocation(location);
|
||||
|
||||
// Obter o idUsuario da sessão atual
|
||||
const session = await this.getValidSession(instance, credentials);
|
||||
|
||||
return await this.makeRequest('ObterDocReceber', {
|
||||
idCliente,
|
||||
situacao: 1 // Faturas em aberto
|
||||
idUsuario: session.idUsuario || 1, // Usar ID do usuário da sessão
|
||||
status: 0, // 0 = Faturas abertas
|
||||
situacaoFatura: 0 // 0 = Faturas ativas
|
||||
}, instance, credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter dados para pagamento da fatura (linha digitável)
|
||||
*/
|
||||
async obterDadosFatura(idFatura, location = '', credentials) {
|
||||
async obterDadosFatura(idFatura, idCliente, location = '', credentials) {
|
||||
const instance = this.getInstanceByLocation(location);
|
||||
|
||||
return await this.makeRequest('ObterDocReceberDadosFatura', { idFatura }, instance, credentials);
|
||||
// Obter o idUsuario da sessão atual
|
||||
const session = await this.getValidSession(instance, credentials);
|
||||
|
||||
return await this.makeRequest('ObterDocReceberDadosFatura', {
|
||||
idFatura,
|
||||
idCliente,
|
||||
idUsuario: session.idUsuario || 1
|
||||
}, instance, credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,28 +376,33 @@ class TopSappAPI {
|
||||
/**
|
||||
* Alterar status do serviço (bloquear/liberar)
|
||||
*/
|
||||
async alterarStatusServico(idServico, status, location = '', credentials) {
|
||||
async alterarStatusServico(idServico, idCliente, status, location = '', credentials) {
|
||||
const instance = this.getInstanceByLocation(location);
|
||||
|
||||
// Obter o idUsuario da sessão atual
|
||||
const session = await this.getValidSession(instance, credentials);
|
||||
|
||||
// Status: 1 = Ativo, 2 = Bloqueado, 3 = Desativado
|
||||
return await this.makeRequest('AlterarServicoStatus', {
|
||||
idServico,
|
||||
status
|
||||
idCliente,
|
||||
idUsuario: session.idUsuario || 1,
|
||||
idStatus: status
|
||||
}, instance, credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bloquear serviço do cliente
|
||||
*/
|
||||
async bloquearServico(idServico, location = '', credentials) {
|
||||
return await this.alterarStatusServico(idServico, 2, location, credentials);
|
||||
async bloquearServico(idServico, idCliente, location = '', credentials) {
|
||||
return await this.alterarStatusServico(idServico, idCliente, 2, location, credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Liberar serviço do cliente
|
||||
*/
|
||||
async liberarServico(idServico, location = '', credentials) {
|
||||
return await this.alterarStatusServico(idServico, 1, location, credentials);
|
||||
async liberarServico(idServico, idCliente, location = '', credentials) {
|
||||
return await this.alterarStatusServico(idServico, idCliente, 1, location, credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,10 +11,10 @@ class TopSappConfig {
|
||||
idUsuario: process.env.TOPSAPP_GUARAI_USERID || '1'
|
||||
},
|
||||
pedro_afonso: {
|
||||
usuario: process.env.TOPSAPP_PEDRO_USER || '',
|
||||
senha: process.env.TOPSAPP_PEDRO_PASS || '',
|
||||
identificador: process.env.TOPSAPP_PEDRO_ID || 'BotTelegram',
|
||||
idUsuario: process.env.TOPSAPP_PEDRO_USERID || '1'
|
||||
usuario: process.env.TOPSAPP_PEDRO_AFONSO_USER || '',
|
||||
senha: process.env.TOPSAPP_PEDRO_AFONSO_PASS || '',
|
||||
identificador: process.env.TOPSAPP_PEDRO_AFONSO_ID || 'BotTelegram',
|
||||
idUsuario: process.env.TOPSAPP_PEDRO_AFONSO_USERID || '1'
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+360
-15
@@ -1,5 +1,6 @@
|
||||
const TopSappAPI = require('./api');
|
||||
const TopSappConfig = require('./config');
|
||||
const logger = require('../utils/logger');
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
|
||||
@@ -8,6 +9,8 @@ class TopSappManager {
|
||||
this.api = new TopSappAPI();
|
||||
this.config = new TopSappConfig();
|
||||
|
||||
logger.info('TopSappManager inicializado');
|
||||
|
||||
// Limpar sessões expiradas a cada 10 minutos
|
||||
setInterval(() => {
|
||||
this.api.clearExpiredSessions();
|
||||
@@ -149,21 +152,29 @@ class TopSappManager {
|
||||
|
||||
const boletos = [];
|
||||
|
||||
for (const fatura of faturas.data.faturas || []) {
|
||||
for (const fatura of faturas.data.dados || []) {
|
||||
// Obter dados da fatura (linha digitável)
|
||||
const dadosFatura = await this.api.obterDadosFatura(fatura.id, location, credentials);
|
||||
const dadosFatura = await this.api.obterDadosFatura(fatura.id, fatura.codigo_cliente, location, credentials);
|
||||
|
||||
const boleto = {
|
||||
id: fatura.id,
|
||||
valor: fatura.valor,
|
||||
vencimento: fatura.vencimento,
|
||||
status: fatura.status,
|
||||
descricao: fatura.descricao,
|
||||
linhaDigitavel: null
|
||||
status: fatura.pago === 'SIM' ? 'Pago' : 'Pendente',
|
||||
descricao: fatura.historico,
|
||||
periodo: this.formatarPeriodo(fatura.periodo_inicio, fatura.periodo_fim),
|
||||
linhaDigitavel: null,
|
||||
pixCopiaCola: null
|
||||
};
|
||||
|
||||
if (dadosFatura.success && dadosFatura.data.linhaDigitavel) {
|
||||
boleto.linhaDigitavel = dadosFatura.data.linhaDigitavel;
|
||||
// Adicionar linha digitável e PIX copia e cola se obtida com sucesso
|
||||
if (dadosFatura.success && dadosFatura.data.fatura) {
|
||||
if (dadosFatura.data.fatura.linha_digitavel) {
|
||||
boleto.linhaDigitavel = dadosFatura.data.fatura.linha_digitavel;
|
||||
}
|
||||
if (dadosFatura.data.fatura.qrcode) {
|
||||
boleto.pixCopiaCola = dadosFatura.data.fatura.qrcode;
|
||||
}
|
||||
}
|
||||
|
||||
boletos.push(boleto);
|
||||
@@ -285,12 +296,12 @@ class TopSappManager {
|
||||
const resultados = [];
|
||||
|
||||
// Bloquear todos os serviços ativos
|
||||
for (const servico of servicos.data.servicos || []) {
|
||||
if (servico.status === 1) { // Apenas serviços ativos
|
||||
const resultado = await this.api.bloquearServico(servico.id, location, credentials);
|
||||
for (const servico of servicos.data.dados || []) {
|
||||
if (servico.status === 1 || servico.estado === 'liberado') { // Apenas serviços ativos
|
||||
const resultado = await this.api.bloquearServico(servico.id, idCliente, location, credentials);
|
||||
resultados.push({
|
||||
servicoId: servico.id,
|
||||
descricao: servico.descricao,
|
||||
descricao: servico.descricao || servico.nome,
|
||||
resultado
|
||||
});
|
||||
}
|
||||
@@ -335,12 +346,12 @@ class TopSappManager {
|
||||
const resultados = [];
|
||||
|
||||
// Liberar todos os serviços bloqueados
|
||||
for (const servico of servicos.data.servicos || []) {
|
||||
if (servico.status === 2) { // Apenas serviços bloqueados
|
||||
const resultado = await this.api.liberarServico(servico.id, location, credentials);
|
||||
for (const servico of servicos.data.dados || []) {
|
||||
if (servico.status === 2 || servico.estado === 'bloqueado') { // Apenas serviços bloqueados
|
||||
const resultado = await this.api.liberarServico(servico.id, idCliente, location, credentials);
|
||||
resultados.push({
|
||||
servicoId: servico.id,
|
||||
descricao: servico.descricao,
|
||||
descricao: servico.descricao || servico.nome,
|
||||
resultado
|
||||
});
|
||||
}
|
||||
@@ -361,12 +372,346 @@ class TopSappManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter lista de serviços do cliente
|
||||
*/
|
||||
async obterServicosCliente(idCliente, location = '') {
|
||||
logger.debug('obterServicosCliente chamado', {
|
||||
idCliente,
|
||||
location,
|
||||
tipoIdCliente: typeof idCliente
|
||||
});
|
||||
|
||||
if (!this.isReady()) {
|
||||
logger.warn('TopSapp não está configurado');
|
||||
return {
|
||||
success: false,
|
||||
error: 'TopSapp não está configurado'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const credentials = this.config.getCredentialsByLocation(location);
|
||||
logger.debug('Credenciais obtidas', { location, hasCredentials: !!credentials });
|
||||
|
||||
const servicos = await this.api.obterServicosCliente(idCliente, location, credentials);
|
||||
logger.debug('Resposta da API obterServicosCliente', {
|
||||
success: servicos.success,
|
||||
dataLength: servicos.data?.dados?.length,
|
||||
error: servicos.error
|
||||
});
|
||||
|
||||
if (!servicos.success) {
|
||||
logger.warn('API retornou erro', servicos.error);
|
||||
return servicos;
|
||||
}
|
||||
|
||||
// Converter idCliente para número para comparação
|
||||
console.log(`🔍 Recebido idCliente:`, idCliente, `(tipo: ${typeof idCliente})`);
|
||||
logger.debug('Processando idCliente', {
|
||||
originalValue: idCliente,
|
||||
originalType: typeof idCliente
|
||||
});
|
||||
|
||||
const clienteId = parseInt(idCliente, 10);
|
||||
logger.debug('idCliente convertido', {
|
||||
clienteId,
|
||||
isValid: !isNaN(clienteId)
|
||||
});
|
||||
console.log(`🔍 Convertido para clienteId:`, clienteId, `(tipo: ${typeof clienteId})`);
|
||||
|
||||
// Verificar se a conversão foi bem-sucedida
|
||||
if (isNaN(clienteId)) {
|
||||
console.log(`❌ ERRO: idCliente inválido. Valor original:`, idCliente);
|
||||
return {
|
||||
success: false,
|
||||
error: `ID do cliente inválido: ${idCliente}`
|
||||
};
|
||||
}
|
||||
|
||||
console.log(`🔍 Manager - Filtrando serviços para cliente ID: ${clienteId}`);
|
||||
|
||||
// Acessar os dados da resposta
|
||||
let dadosServicos = servicos.data?.dados || servicos.data || [];
|
||||
|
||||
// Se não é array, tentar converter
|
||||
if (!Array.isArray(dadosServicos)) {
|
||||
console.log(`⚠️ Dados retornados não são um array:`, typeof dadosServicos);
|
||||
dadosServicos = [];
|
||||
}
|
||||
|
||||
console.log(`📊 Total de registros retornados pela API: ${dadosServicos.length}`);
|
||||
|
||||
if (dadosServicos.length > 100) {
|
||||
console.log(`⚠️ ALERTA: API retornou ${dadosServicos.length} registros - muito mais que o esperado para um cliente`);
|
||||
|
||||
// Log dos primeiros registros para debug
|
||||
const amostra = dadosServicos.slice(0, 5).map(item => ({
|
||||
id: item.id,
|
||||
idCliente: item.idCliente || item.cliente_id || item.id_cliente,
|
||||
nome: item.nome || item.descricao
|
||||
}));
|
||||
console.log(`🔍 Amostra dos primeiros registros:`, amostra);
|
||||
}
|
||||
|
||||
// Filtrar RIGOROSAMENTE apenas os serviços do cliente específico
|
||||
const servicosDoCliente = dadosServicos.filter(servico => {
|
||||
// Tentar diferentes campos para o ID do cliente
|
||||
const possiveisIds = [
|
||||
servico.idCliente,
|
||||
servico.cliente_id,
|
||||
servico.id_cliente,
|
||||
servico.clienteId,
|
||||
servico.cod_cliente,
|
||||
servico.codigo_cliente
|
||||
];
|
||||
|
||||
// Verificar se algum dos IDs corresponde ao cliente
|
||||
for (const id of possiveisIds) {
|
||||
if (id !== undefined && id !== null) {
|
||||
const servicoClienteId = parseInt(id, 10);
|
||||
if (servicoClienteId === clienteId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
console.log(`🎯 Serviços filtrados para o cliente ${clienteId}: ${servicosDoCliente.length}`);
|
||||
|
||||
// Se ainda assim temos muitos resultados, algo está errado
|
||||
if (servicosDoCliente.length > 50) {
|
||||
console.log(`❌ ERRO: Ainda temos ${servicosDoCliente.length} serviços após filtro - possível problema na API`);
|
||||
|
||||
// Log detalhado para investigação
|
||||
const idsEncontrados = servicosDoCliente.map(s => ({
|
||||
id: s.id,
|
||||
idCliente: s.idCliente || s.cliente_id || s.id_cliente,
|
||||
nome: s.nome || s.descricao
|
||||
})).slice(0, 10);
|
||||
|
||||
console.log(`🔍 IDs encontrados (primeiros 10):`, idsEncontrados);
|
||||
}
|
||||
|
||||
const servicosFormatados = servicosDoCliente.map(servico => ({
|
||||
id: servico.id || servico.id_servico,
|
||||
nome: servico.nome || servico.descricao || 'Serviço sem nome',
|
||||
status: servico.estado || (servico.status === 1 ? 'liberado' : 'bloqueado'),
|
||||
statusNumerico: servico.status,
|
||||
valorMensal: servico.valor_mensal || '0.00',
|
||||
tipoServico: servico.tipo_servico || 'Não informado',
|
||||
dataAtivacao: servico.data_ativacao,
|
||||
observacoes: servico.observacoes,
|
||||
clienteId: servico.idCliente || servico.cliente_id || servico.id_cliente
|
||||
}));
|
||||
|
||||
return {
|
||||
success: true,
|
||||
servicos: servicosFormatados,
|
||||
total: servicosFormatados.length,
|
||||
instancia: servicos.instance,
|
||||
debug: {
|
||||
totalRetornadoAPI: dadosServicos.length,
|
||||
totalFiltrado: servicosFormatados.length,
|
||||
clienteId: clienteId
|
||||
}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Erro ao obter serviços do cliente ${idCliente}:`, error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bloquear serviço específico
|
||||
*/
|
||||
async bloquearServicoEspecifico(idCliente, idServico, location = '') {
|
||||
if (!this.isReady()) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'TopSapp não está configurado'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const credentials = this.config.getCredentialsByLocation(location);
|
||||
const resultado = await this.api.bloquearServico(idServico, idCliente, location, credentials);
|
||||
|
||||
return {
|
||||
...resultado,
|
||||
instancia: location
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liberar serviço específico
|
||||
*/
|
||||
async liberarServicoEspecifico(idCliente, idServico, location = '') {
|
||||
if (!this.isReady()) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'TopSapp não está configurado'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const credentials = this.config.getCredentialsByLocation(location);
|
||||
const resultado = await this.api.liberarServico(idServico, idCliente, location, credentials);
|
||||
|
||||
return {
|
||||
...resultado,
|
||||
instancia: location
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alterar status de um serviço específico
|
||||
*/
|
||||
async alterarStatusServico(idServico, acao, location = '') {
|
||||
if (!this.isReady()) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'TopSapp não está configurado'
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const credentials = this.config.getCredentialsByLocation(location);
|
||||
|
||||
// Determinar o status numérico baseado na ação
|
||||
let status;
|
||||
if (acao === 'bloquear') {
|
||||
status = 2; // Bloqueado
|
||||
} else if (acao === 'liberar') {
|
||||
status = 1; // Ativo
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Ação inválida. Use "bloquear" ou "liberar"'
|
||||
};
|
||||
}
|
||||
|
||||
// Primeiro, obter informações do serviço para saber o idCliente
|
||||
const servicos = await this.api.obterServicosCliente(null, location, credentials);
|
||||
|
||||
if (!servicos.success) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Erro ao obter informações do serviço'
|
||||
};
|
||||
}
|
||||
|
||||
// Procurar o serviço específico
|
||||
const servicoEncontrado = (servicos.data.dados || []).find(s =>
|
||||
s.id_servico == idServico || s.id == idServico
|
||||
);
|
||||
|
||||
if (!servicoEncontrado) {
|
||||
return {
|
||||
success: false,
|
||||
error: 'Serviço não encontrado'
|
||||
};
|
||||
}
|
||||
|
||||
const idCliente = servicoEncontrado.id_cliente;
|
||||
|
||||
logger.debug('Alterando status do serviço', {
|
||||
idServico,
|
||||
idCliente,
|
||||
status,
|
||||
acao
|
||||
});
|
||||
|
||||
const resultado = await this.api.alterarStatusServico(idServico, idCliente, status, location, credentials);
|
||||
|
||||
return {
|
||||
...resultado,
|
||||
instancia: location
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Erro ao alterar status do serviço', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obter instâncias disponíveis
|
||||
*/
|
||||
getInstances() {
|
||||
return this.api.getInstances();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatar período de datas para DD/MM/YYYY
|
||||
*/
|
||||
formatarPeriodo(dataInicio, dataFim) {
|
||||
const formatarData = (data) => {
|
||||
if (!data) return '';
|
||||
|
||||
// Se já está no formato DD/MM/YYYY, retornar como está
|
||||
if (data.match(/^\d{2}\/\d{2}\/\d{4}$/)) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// Se está no formato YYYY-MM-DD, converter
|
||||
if (data.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
||||
const [ano, mes, dia] = data.split('-');
|
||||
return `${dia}/${mes}/${ano}`;
|
||||
}
|
||||
|
||||
// Tentar converter outros formatos
|
||||
try {
|
||||
const dataObj = new Date(data);
|
||||
if (!isNaN(dataObj.getTime())) {
|
||||
const dia = String(dataObj.getDate()).padStart(2, '0');
|
||||
const mes = String(dataObj.getMonth() + 1).padStart(2, '0');
|
||||
const ano = dataObj.getFullYear();
|
||||
return `${dia}/${mes}/${ano}`;
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Erro ao formatar data:', data, error);
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const inicio = formatarData(dataInicio);
|
||||
const fim = formatarData(dataFim);
|
||||
|
||||
if (inicio && fim) {
|
||||
return `${inicio} a ${fim}`;
|
||||
} else if (inicio) {
|
||||
return inicio;
|
||||
} else if (fim) {
|
||||
return fim;
|
||||
} else {
|
||||
return 'Período não informado';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TopSappManager;
|
||||
@@ -0,0 +1,173 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
class Logger {
|
||||
constructor() {
|
||||
this.logDir = path.join(__dirname, '../../logs');
|
||||
this.logFile = path.join(this.logDir, 'bot-debug.log');
|
||||
this.maxLogSize = 10 * 1024 * 1024; // 10MB
|
||||
this.maxBackups = 5;
|
||||
|
||||
// Criar diretório de logs se não existir
|
||||
this.ensureLogDir();
|
||||
|
||||
// Só ativa logs se estiver em modo debug
|
||||
this.isDebug = process.env.NODE_ENV === 'development' || process.env.DEBUG === 'true';
|
||||
}
|
||||
|
||||
ensureLogDir() {
|
||||
if (!fs.existsSync(this.logDir)) {
|
||||
fs.mkdirSync(this.logDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
formatMessage(level, message, data = null) {
|
||||
const timestamp = new Date().toISOString();
|
||||
let logLine = `[${timestamp}] [${level}] ${message}`;
|
||||
|
||||
if (data) {
|
||||
if (typeof data === 'object') {
|
||||
logLine += '\n' + JSON.stringify(data, null, 2);
|
||||
} else {
|
||||
logLine += ` - ${data}`;
|
||||
}
|
||||
}
|
||||
|
||||
return logLine + '\n';
|
||||
}
|
||||
|
||||
rotateLogIfNeeded() {
|
||||
if (!fs.existsSync(this.logFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stats = fs.statSync(this.logFile);
|
||||
if (stats.size > this.maxLogSize) {
|
||||
// Fazer backup dos logs existentes
|
||||
for (let i = this.maxBackups - 1; i >= 0; i--) {
|
||||
const oldFile = i === 0 ? this.logFile : `${this.logFile}.${i}`;
|
||||
const newFile = `${this.logFile}.${i + 1}`;
|
||||
|
||||
if (fs.existsSync(oldFile)) {
|
||||
if (i === this.maxBackups - 1) {
|
||||
fs.unlinkSync(oldFile); // Deletar o mais antigo
|
||||
} else {
|
||||
fs.renameSync(oldFile, newFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writeLog(level, message, data = null) {
|
||||
if (!this.isDebug) {
|
||||
return; // Só loga em modo debug
|
||||
}
|
||||
|
||||
try {
|
||||
this.rotateLogIfNeeded();
|
||||
const logMessage = this.formatMessage(level, message, data);
|
||||
|
||||
// Escrever no arquivo de log
|
||||
fs.appendFileSync(this.logFile, logMessage);
|
||||
|
||||
// Também mostrar no console se for erro ou warning
|
||||
if (level === 'ERROR' || level === 'WARN') {
|
||||
console.log(logMessage.trim());
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao escrever log:', error);
|
||||
}
|
||||
}
|
||||
|
||||
debug(message, data = null) {
|
||||
this.writeLog('DEBUG', message, data);
|
||||
}
|
||||
|
||||
info(message, data = null) {
|
||||
this.writeLog('INFO', message, data);
|
||||
}
|
||||
|
||||
warn(message, data = null) {
|
||||
this.writeLog('WARN', message, data);
|
||||
}
|
||||
|
||||
error(message, data = null) {
|
||||
this.writeLog('ERROR', message, data);
|
||||
}
|
||||
|
||||
// Métodos específicos para o bot
|
||||
botCommand(userId, command, data = null) {
|
||||
this.debug(`Comando recebido - User: ${userId}, Command: ${command}`, data);
|
||||
}
|
||||
|
||||
apiCall(endpoint, params = null, response = null) {
|
||||
this.debug(`API Call - Endpoint: ${endpoint}`, {
|
||||
params,
|
||||
responseStatus: response?.status,
|
||||
responseDataLength: response?.data?.length
|
||||
});
|
||||
}
|
||||
|
||||
botError(userId, error, context = null) {
|
||||
this.error(`Bot Error - User: ${userId}`, {
|
||||
error: error.message,
|
||||
stack: error.stack,
|
||||
context
|
||||
});
|
||||
}
|
||||
|
||||
topsappOperation(operation, clientId, result = null) {
|
||||
this.debug(`TopSapp Operation - ${operation} - Cliente: ${clientId}`, {
|
||||
success: result?.success,
|
||||
dataCount: result?.data?.length || result?.servicos?.length || result?.clientes?.length,
|
||||
error: result?.error
|
||||
});
|
||||
}
|
||||
|
||||
// Limpar logs antigos (método utilitário)
|
||||
clearLogs() {
|
||||
if (!this.isDebug) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (fs.existsSync(this.logFile)) {
|
||||
fs.unlinkSync(this.logFile);
|
||||
}
|
||||
|
||||
// Limpar backups também
|
||||
for (let i = 1; i <= this.maxBackups; i++) {
|
||||
const backupFile = `${this.logFile}.${i}`;
|
||||
if (fs.existsSync(backupFile)) {
|
||||
fs.unlinkSync(backupFile);
|
||||
}
|
||||
}
|
||||
|
||||
this.info('Logs limpos com sucesso');
|
||||
} catch (error) {
|
||||
console.error('Erro ao limpar logs:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Obter últimas linhas do log
|
||||
getRecentLogs(lines = 100) {
|
||||
if (!this.isDebug || !fs.existsSync(this.logFile)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(this.logFile, 'utf8');
|
||||
const allLines = content.split('\n').filter(line => line.trim());
|
||||
return allLines.slice(-lines);
|
||||
} catch (error) {
|
||||
console.error('Erro ao ler logs:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance
|
||||
const logger = new Logger();
|
||||
|
||||
module.exports = logger;
|
||||
Reference in new issue
Block a user