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
+1687
-104
No files matched your search
Generated
+39
@@ -9,7 +9,9 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^1.6.0",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
|
"form-data": "^4.0.0",
|
||||||
"node-telegram-bot-api": "^0.64.0",
|
"node-telegram-bot-api": "^0.64.0",
|
||||||
"ssh2": "^1.15.0"
|
"ssh2": "^1.15.0"
|
||||||
},
|
},
|
||||||
@@ -227,6 +229,17 @@
|
|||||||
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
|
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -781,6 +794,26 @@
|
|||||||
"node": ">=8"
|
"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": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
"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==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/psl": {
|
||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
|
||||||
|
|||||||
+932
-54
File diff suppressed because it is too large.
Load diff
+27
-3
@@ -169,9 +169,9 @@ class NLPProcessor {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detectar comandos TopSapp - bloquear cliente
|
// Detectar comandos TopSapp - bloquear cliente/serviço
|
||||||
if (/bloquear|suspender|cortar/i.test(action) && /cliente|user/i.test(fullMatch)) {
|
if (/bloquear|suspender|cortar/i.test(action)) {
|
||||||
let clienteId = match[3] || match[2]; // ID do cliente
|
let clienteId = match[3] || match[2]; // ID/CPF/Nome do cliente
|
||||||
|
|
||||||
return {
|
return {
|
||||||
action: 'bloquear_cliente',
|
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
|
// Detectar comandos TopSapp - liberar cliente
|
||||||
if (/liberar|desbloquear|reconectar/i.test(action) && /cliente|user/i.test(fullMatch)) {
|
if (/liberar|desbloquear|reconectar/i.test(action) && /cliente|user/i.test(fullMatch)) {
|
||||||
let clienteId = match[3] || match[2]; // ID do cliente
|
let clienteId = match[3] || match[2]; // ID do cliente
|
||||||
|
|||||||
+152
-28
@@ -1,5 +1,6 @@
|
|||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const https = require('https');
|
const https = require('https');
|
||||||
|
const logger = require('../utils/logger');
|
||||||
|
|
||||||
class TopSappAPI {
|
class TopSappAPI {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -23,10 +24,8 @@ class TopSappAPI {
|
|||||||
httpsAgent: new https.Agent({
|
httpsAgent: new https.Agent({
|
||||||
rejectUnauthorized: false
|
rejectUnauthorized: false
|
||||||
}),
|
}),
|
||||||
timeout: 30000,
|
timeout: 30000
|
||||||
headers: {
|
// Não definir Content-Type, deixar o FormData fazer isso automaticamente
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('🔗 TopSappAPI inicializado');
|
console.log('🔗 TopSappAPI inicializado');
|
||||||
@@ -53,31 +52,45 @@ class TopSappAPI {
|
|||||||
async login(instance, credentials) {
|
async login(instance, credentials) {
|
||||||
try {
|
try {
|
||||||
console.log(`🔐 Fazendo login na instância: ${instance.name}`);
|
console.log(`🔐 Fazendo login na instância: ${instance.name}`);
|
||||||
|
console.log(`📍 URL: ${instance.baseURL}/Login`);
|
||||||
|
console.log(`👤 Usuario: ${credentials.usuario}`);
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('usuario', credentials.usuario);
|
formData.append('usuario', credentials.usuario);
|
||||||
formData.append('senha', credentials.senha);
|
formData.append('senha', credentials.senha);
|
||||||
formData.append('identificador', credentials.identificador || 'BotTelegram');
|
formData.append('identificador', credentials.identificador || 'BotTelegram');
|
||||||
formData.append('idUsuario', credentials.idUsuario || '1');
|
|
||||||
|
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${instance.baseURL}/Login`,
|
`${instance.baseURL}/Login`,
|
||||||
formData,
|
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 sessionId = response.data.sessao;
|
||||||
|
const idUsuario = response.data.id_usuario;
|
||||||
|
|
||||||
this.sessions.set(instance.baseURL, {
|
this.sessions.set(instance.baseURL, {
|
||||||
sessionId,
|
sessionId,
|
||||||
|
idUsuario,
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
instance
|
instance
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`✅ Login realizado com sucesso na instância: ${instance.name}`);
|
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 {
|
} 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) {
|
} catch (error) {
|
||||||
@@ -127,15 +140,19 @@ class TopSappAPI {
|
|||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('sessao', session.sessionId);
|
formData.append('sessao', session.sessionId);
|
||||||
|
formData.append('identificador', credentials.identificador || 'BotTelegram');
|
||||||
|
|
||||||
// Adicionar parâmetros extras
|
// Adicionar parâmetros extras
|
||||||
Object.keys(data).forEach(key => {
|
Object.keys(data).forEach(key => {
|
||||||
if (data[key] !== undefined && data[key] !== null) {
|
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(`📡 Fazendo requisição para: ${instance.baseURL}/${endpoint}`);
|
||||||
|
console.log(`📊 Parâmetros:`, data);
|
||||||
|
|
||||||
const response = await axios.post(
|
const response = await axios.post(
|
||||||
`${instance.baseURL}/${endpoint}`,
|
`${instance.baseURL}/${endpoint}`,
|
||||||
@@ -143,14 +160,19 @@ class TopSappAPI {
|
|||||||
this.axiosConfig
|
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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: response.data,
|
data: response.data,
|
||||||
instance: instance.name
|
instance: instance.name
|
||||||
};
|
};
|
||||||
} else {
|
} 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) {
|
} catch (error) {
|
||||||
@@ -170,19 +192,26 @@ class TopSappAPI {
|
|||||||
const instance = this.getInstanceByLocation(location);
|
const instance = this.getInstanceByLocation(location);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Obter sessão válida para pegar o idUsuario
|
||||||
|
const session = await this.getValidSession(instance, credentials);
|
||||||
|
|
||||||
// Tentar buscar por diferentes critérios
|
// 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
|
// Se é número, pode ser CPF ou ID
|
||||||
if (/^\d+$/.test(query)) {
|
if (/^\d+$/.test(query)) {
|
||||||
if (query.length === 11) {
|
if (query.length === 11 || query.length === 14) {
|
||||||
searchParams.cpf = query;
|
// CPF (11 dígitos) ou CNPJ (14 dígitos)
|
||||||
|
searchParams.cpfCnpj = query;
|
||||||
} else {
|
} else {
|
||||||
|
// ID do cliente
|
||||||
searchParams.idCliente = query;
|
searchParams.idCliente = query;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Buscar por nome
|
// CPF formatado ou nome - usar o endpoint de busca geral
|
||||||
searchParams.nome = query;
|
searchParams.cpfCnpj = query.replace(/[^\d]/g, ''); // Remove formatação
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this.makeRequest('ObterClientes', searchParams, instance, credentials);
|
const result = await this.makeRequest('ObterClientes', searchParams, instance, credentials);
|
||||||
@@ -190,7 +219,7 @@ class TopSappAPI {
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
clientes: result.data.clientes || [],
|
clientes: result.data.dados || [], // Usar 'dados' em vez de 'clientes'
|
||||||
instance: result.instance
|
instance: result.instance
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -221,7 +250,85 @@ class TopSappAPI {
|
|||||||
async obterServicosCliente(idCliente, location = '', credentials) {
|
async obterServicosCliente(idCliente, location = '', credentials) {
|
||||||
const instance = this.getInstanceByLocation(location);
|
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) {
|
async obterFaturas(idCliente, location = '', credentials) {
|
||||||
const instance = this.getInstanceByLocation(location);
|
const instance = this.getInstanceByLocation(location);
|
||||||
|
|
||||||
|
// Obter o idUsuario da sessão atual
|
||||||
|
const session = await this.getValidSession(instance, credentials);
|
||||||
|
|
||||||
return await this.makeRequest('ObterDocReceber', {
|
return await this.makeRequest('ObterDocReceber', {
|
||||||
idCliente,
|
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);
|
}, instance, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obter dados para pagamento da fatura (linha digitável)
|
* 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);
|
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)
|
* 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);
|
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
|
// Status: 1 = Ativo, 2 = Bloqueado, 3 = Desativado
|
||||||
return await this.makeRequest('AlterarServicoStatus', {
|
return await this.makeRequest('AlterarServicoStatus', {
|
||||||
idServico,
|
idServico,
|
||||||
status
|
idCliente,
|
||||||
|
idUsuario: session.idUsuario || 1,
|
||||||
|
idStatus: status
|
||||||
}, instance, credentials);
|
}, instance, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bloquear serviço do cliente
|
* Bloquear serviço do cliente
|
||||||
*/
|
*/
|
||||||
async bloquearServico(idServico, location = '', credentials) {
|
async bloquearServico(idServico, idCliente, location = '', credentials) {
|
||||||
return await this.alterarStatusServico(idServico, 2, location, credentials);
|
return await this.alterarStatusServico(idServico, idCliente, 2, location, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Liberar serviço do cliente
|
* Liberar serviço do cliente
|
||||||
*/
|
*/
|
||||||
async liberarServico(idServico, location = '', credentials) {
|
async liberarServico(idServico, idCliente, location = '', credentials) {
|
||||||
return await this.alterarStatusServico(idServico, 1, location, credentials);
|
return await this.alterarStatusServico(idServico, idCliente, 1, location, credentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ class TopSappConfig {
|
|||||||
idUsuario: process.env.TOPSAPP_GUARAI_USERID || '1'
|
idUsuario: process.env.TOPSAPP_GUARAI_USERID || '1'
|
||||||
},
|
},
|
||||||
pedro_afonso: {
|
pedro_afonso: {
|
||||||
usuario: process.env.TOPSAPP_PEDRO_USER || '',
|
usuario: process.env.TOPSAPP_PEDRO_AFONSO_USER || '',
|
||||||
senha: process.env.TOPSAPP_PEDRO_PASS || '',
|
senha: process.env.TOPSAPP_PEDRO_AFONSO_PASS || '',
|
||||||
identificador: process.env.TOPSAPP_PEDRO_ID || 'BotTelegram',
|
identificador: process.env.TOPSAPP_PEDRO_AFONSO_ID || 'BotTelegram',
|
||||||
idUsuario: process.env.TOPSAPP_PEDRO_USERID || '1'
|
idUsuario: process.env.TOPSAPP_PEDRO_AFONSO_USERID || '1'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+360
-15
@@ -1,5 +1,6 @@
|
|||||||
const TopSappAPI = require('./api');
|
const TopSappAPI = require('./api');
|
||||||
const TopSappConfig = require('./config');
|
const TopSappConfig = require('./config');
|
||||||
|
const logger = require('../utils/logger');
|
||||||
const fs = require('fs').promises;
|
const fs = require('fs').promises;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
@@ -8,6 +9,8 @@ class TopSappManager {
|
|||||||
this.api = new TopSappAPI();
|
this.api = new TopSappAPI();
|
||||||
this.config = new TopSappConfig();
|
this.config = new TopSappConfig();
|
||||||
|
|
||||||
|
logger.info('TopSappManager inicializado');
|
||||||
|
|
||||||
// Limpar sessões expiradas a cada 10 minutos
|
// Limpar sessões expiradas a cada 10 minutos
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.api.clearExpiredSessions();
|
this.api.clearExpiredSessions();
|
||||||
@@ -149,21 +152,29 @@ class TopSappManager {
|
|||||||
|
|
||||||
const boletos = [];
|
const boletos = [];
|
||||||
|
|
||||||
for (const fatura of faturas.data.faturas || []) {
|
for (const fatura of faturas.data.dados || []) {
|
||||||
// Obter dados da fatura (linha digitável)
|
// 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 = {
|
const boleto = {
|
||||||
id: fatura.id,
|
id: fatura.id,
|
||||||
valor: fatura.valor,
|
valor: fatura.valor,
|
||||||
vencimento: fatura.vencimento,
|
vencimento: fatura.vencimento,
|
||||||
status: fatura.status,
|
status: fatura.pago === 'SIM' ? 'Pago' : 'Pendente',
|
||||||
descricao: fatura.descricao,
|
descricao: fatura.historico,
|
||||||
linhaDigitavel: null
|
periodo: this.formatarPeriodo(fatura.periodo_inicio, fatura.periodo_fim),
|
||||||
|
linhaDigitavel: null,
|
||||||
|
pixCopiaCola: null
|
||||||
};
|
};
|
||||||
|
|
||||||
if (dadosFatura.success && dadosFatura.data.linhaDigitavel) {
|
// Adicionar linha digitável e PIX copia e cola se obtida com sucesso
|
||||||
boleto.linhaDigitavel = dadosFatura.data.linhaDigitavel;
|
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);
|
boletos.push(boleto);
|
||||||
@@ -285,12 +296,12 @@ class TopSappManager {
|
|||||||
const resultados = [];
|
const resultados = [];
|
||||||
|
|
||||||
// Bloquear todos os serviços ativos
|
// Bloquear todos os serviços ativos
|
||||||
for (const servico of servicos.data.servicos || []) {
|
for (const servico of servicos.data.dados || []) {
|
||||||
if (servico.status === 1) { // Apenas serviços ativos
|
if (servico.status === 1 || servico.estado === 'liberado') { // Apenas serviços ativos
|
||||||
const resultado = await this.api.bloquearServico(servico.id, location, credentials);
|
const resultado = await this.api.bloquearServico(servico.id, idCliente, location, credentials);
|
||||||
resultados.push({
|
resultados.push({
|
||||||
servicoId: servico.id,
|
servicoId: servico.id,
|
||||||
descricao: servico.descricao,
|
descricao: servico.descricao || servico.nome,
|
||||||
resultado
|
resultado
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -335,12 +346,12 @@ class TopSappManager {
|
|||||||
const resultados = [];
|
const resultados = [];
|
||||||
|
|
||||||
// Liberar todos os serviços bloqueados
|
// Liberar todos os serviços bloqueados
|
||||||
for (const servico of servicos.data.servicos || []) {
|
for (const servico of servicos.data.dados || []) {
|
||||||
if (servico.status === 2) { // Apenas serviços bloqueados
|
if (servico.status === 2 || servico.estado === 'bloqueado') { // Apenas serviços bloqueados
|
||||||
const resultado = await this.api.liberarServico(servico.id, location, credentials);
|
const resultado = await this.api.liberarServico(servico.id, idCliente, location, credentials);
|
||||||
resultados.push({
|
resultados.push({
|
||||||
servicoId: servico.id,
|
servicoId: servico.id,
|
||||||
descricao: servico.descricao,
|
descricao: servico.descricao || servico.nome,
|
||||||
resultado
|
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
|
* Obter instâncias disponíveis
|
||||||
*/
|
*/
|
||||||
getInstances() {
|
getInstances() {
|
||||||
return this.api.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;
|
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