akra35567 commited on
Commit
9124c31
Β·
verified Β·
1 Parent(s): d0c2824

Update modules/BotCore.js

Browse files
Files changed (1) hide show
  1. modules/BotCore.js +132 -1037
modules/BotCore.js CHANGED
@@ -1,1118 +1,213 @@
1
  /**
2
  * ═══════════════════════════════════════════════════════════════════════
3
- * CLASSE: BotCore
4
  * ═══════════════════════════════════════════════════════════════════════
5
- * NΓΊcleo do bot: Baileys wrapper, event handling, orquestraΓ§Γ£o
6
- * Main loop e gerenciamento de conexΓ£o
7
  * ═══════════════════════════════════════════════════════════════════════
8
  */
9
 
10
- // ═══════════════════════════════════════════════════════════════════════
11
- // HF SPACES DNS CORRECTIONS - CORREÇÃO CRÍTICA PARA QR CODE
12
- // ═══════════════════════════════════════════════════════════════════════
13
- const HFCorrections = require('./HFCorrections');
14
-
15
- // Inicializa correΓ§Γ΅es DNS para HF Spaces
16
- HFCorrections.configureDNS();
17
-
18
- // FunΓ§Γ£o helper para obter IP direto do WhatsApp
19
- function getWhatsAppFallbackIP() {
20
- return HFCorrections.getWhatsAppIP();
21
- }
22
-
23
  const {
24
  default: makeWASocket,
25
  useMultiFileAuthState,
26
  fetchLatestBaileysVersion,
27
  Browsers,
28
  delay,
29
- getContentType
30
  } = require('@whiskeysockets/baileys');
31
  const pino = require('pino');
32
- const ConfigManager = require('./ConfigManager');
33
- const APIClient = require('./APIClient');
34
- const AudioProcessor = require('./AudioProcessor');
35
- const MediaProcessor = require('./MediaProcessor');
36
- const MessageProcessor = require('./MessageProcessor');
37
- const ModerationSystem = require('./ModerationSystem');
38
- const LevelSystem = require('./LevelSystem');
39
- const CommandHandler = require('./CommandHandler');
40
 
41
- class BotCore {
42
- constructor() {
43
- this.config = ConfigManager.getInstance();
44
- this.logger = pino({
45
- level: this.config.LOG_LEVEL,
46
- transport: {
47
- target: 'pino-pretty',
48
- options: {
49
- colorize: true,
50
- translateTime: 'SYS:standard',
51
- ignore: 'pid,hostname'
52
- }
53
- }
54
- });
 
 
55
 
56
- // Componentes
57
- this.apiClient = null;
58
- this.audioProcessor = null;
59
- this.mediaProcessor = null;
60
- this.messageProcessor = null;
61
- this.moderationSystem = null;
62
- this.levelSystem = null;
63
- this.commandHandler = null;
 
 
64
 
65
- // Estado
 
 
 
66
  this.sock = null;
67
- this.BOT_JID = null;
 
68
  this.currentQR = null;
69
  this.isConnected = false;
70
- this.lastProcessedTime = 0;
71
- this.processadas = new Set();
72
  this.reconnectAttempts = 0;
73
- this.qrTimeout = null;
74
- this.connectionStartTime = null;
75
-
76
- // Armazenamento
77
- this.store = null;
78
 
79
- // Event listeners
80
- this.eventListeners = {
81
- onQRGenerated: null,
82
- onConnected: null,
83
- onDisconnected: null
84
- };
 
85
  }
86
 
87
  /**
88
- * Inicializa o bot
89
  */
90
  async initialize() {
91
- try {
92
- this.logger.info('πŸ”§ Inicializando BotCore...');
93
-
94
- // Log da porta do servidor
95
- this.logger.info(`🌐 Porta do servidor: ${this.config.PORT}`);
96
-
97
- // Valida configuraΓ§Γ΅es
98
- if (!this.config.validate()) {
99
- throw new Error('ConfiguraΓ§Γ΅es invΓ‘lidas');
100
- }
101
-
102
- // Mostra configuraΓ§Γ΅es
103
- this.config.logConfig();
104
-
105
- // Cria pastas necessΓ‘rias com tratamento de erro
106
- this.ensureFolders();
107
-
108
- // Inicializa componentes
109
- this.initializeComponents();
110
-
111
- this.logger.info('βœ… BotCore inicializado');
112
- return true;
113
-
114
- } catch (error) {
115
- this.logger.error('❌ Erro ao inicializar:', error.message);
116
- throw error;
117
- }
118
- }
119
-
120
- /**
121
- * Cria pastas necessΓ‘rias com tratamento robusto para Hugging Face
122
- */
123
- ensureFolders() {
124
- const fs = require('fs');
125
- const path = require('path');
126
-
127
- // Primeiro, tenta os caminhos padrΓ£o
128
- const defaultFolders = [
129
- this.config.TEMP_FOLDER,
130
- this.config.AUTH_FOLDER,
131
- this.config.DATABASE_FOLDER,
132
- this.config.LOGS_FOLDER
133
- ];
134
-
135
- // Lista de fallbacks para Hugging Face
136
- const fallbackBase = '/tmp/akira_data';
137
- const fallbackFolders = [
138
- path.join(fallbackBase, 'temp'),
139
- path.join(fallbackBase, 'auth_info_baileys'),
140
- path.join(fallbackBase, 'database'),
141
- path.join(fallbackBase, 'logs')
142
- ];
143
-
144
- this.logger.info('πŸ“ Criando diretΓ³rios necessΓ‘rios...');
145
-
146
- // Tentar criar diretΓ³rios padrΓ£o primeiro
147
- let useFallback = false;
148
- for (let i = 0; i < defaultFolders.length; i++) {
149
- const folder = defaultFolders[i];
150
- try {
151
- if (!fs.existsSync(folder)) {
152
- fs.mkdirSync(folder, { recursive: true });
153
- this.logger.debug(`βœ… Pasta criada (padrΓ£o): ${folder}`);
154
- } else {
155
- this.logger.debug(`βœ… Pasta jΓ‘ existe: ${folder}`);
156
- }
157
- } catch (error) {
158
- this.logger.warn(`⚠️ Erro ao criar pasta padrão ${folder}: ${error.message}`);
159
- useFallback = true;
160
- break;
161
- }
162
- }
163
-
164
- // Se falhou em algum diretΓ³rio padrΓ£o, usar fallback para Hugging Face
165
- if (useFallback) {
166
- this.logger.info('πŸ”„ Usando diretΓ³rios de fallback para Hugging Face Spaces...');
167
-
168
- // Primeiro cria a pasta base de fallback
169
- try {
170
- if (!fs.existsSync(fallbackBase)) {
171
- fs.mkdirSync(fallbackBase, { recursive: true });
172
- this.logger.info(`βœ… Pasta base de fallback criada: ${fallbackBase}`);
173
- }
174
- } catch (error) {
175
- this.logger.error(`❌ Erro crítico ao criar pasta base de fallback: ${error.message}`);
176
- throw error;
177
- }
178
-
179
- // Cria todas as subpastas de fallback
180
- fallbackFolders.forEach(folder => {
181
- try {
182
- if (!fs.existsSync(folder)) {
183
- fs.mkdirSync(folder, { recursive: true });
184
- this.logger.info(`βœ… Pasta de fallback criada: ${folder}`);
185
- }
186
- } catch (error) {
187
- this.logger.error(`❌ Erro ao criar pasta de fallback ${folder}: ${error.message}`);
188
- }
189
- });
190
-
191
- // Atualiza configuraΓ§Γ΅es para usar os caminhos de fallback
192
- this.config.TEMP_FOLDER = fallbackFolders[0];
193
- this.config.AUTH_FOLDER = fallbackFolders[1];
194
- this.config.DATABASE_FOLDER = fallbackFolders[2];
195
- this.config.LOGS_FOLDER = fallbackFolders[3];
196
-
197
- this.logger.info('πŸ”„ ConfiguraΓ§Γ΅es atualizadas para usar caminhos de fallback');
198
- } else {
199
- this.logger.info('βœ… Todos os diretΓ³rios criados com sucesso');
200
- }
201
- }
202
-
203
- /**
204
- * Inicializa todos os componentes
205
- */
206
- initializeComponents() {
207
- try {
208
- this.logger.debug('πŸ”§ Inicializando componentes...');
209
-
210
- this.apiClient = new APIClient(this.logger);
211
- this.audioProcessor = new AudioProcessor(this.logger);
212
- this.mediaProcessor = new MediaProcessor(this.logger);
213
- this.messageProcessor = new MessageProcessor(this.logger);
214
- this.moderationSystem = new ModerationSystem(this.logger);
215
- this.levelSystem = new LevelSystem(this.logger);
216
-
217
- // CommandHandler pode falhar no Hugging Face, tratar separadamente
218
- try {
219
- this.commandHandler = new CommandHandler(this);
220
- this.logger.debug('βœ… CommandHandler inicializado');
221
- } catch (commandError) {
222
- this.logger.warn(`⚠️ CommandHandler falhou: ${commandError.message}`);
223
- this.logger.warn('⚠️ Continuando sem CommandHandler (modo limitado)');
224
- this.commandHandler = null;
225
- }
226
-
227
- this.logger.debug('βœ… Componentes inicializados');
228
- } catch (error) {
229
- this.logger.error('❌ Erro ao inicializar componentes:', error.message);
230
- // NΓ£o lanΓ§ar erro fatal, tentar continuar com componentes disponΓ­veis
231
- }
232
- }
233
-
234
- /**
235
- * Cria agente HTTP personalizado para ambientes restritos (HF Spaces)
236
- */
237
- _createCustomAgent() {
238
- return HFCorrections.createHFAgent();
239
- }
240
-
241
- /**
242
- * Verifica e limpa credenciais antigas se necessΓ‘rio
243
- */
244
- async _checkAndCleanOldAuth() {
245
- const fs = require('fs');
246
- const path = require('path');
247
-
248
- const authPath = this.config.AUTH_FOLDER;
249
- const credsPath = path.join(authPath, 'creds.json');
250
-
251
- try {
252
- if (fs.existsSync(credsPath)) {
253
- const stats = fs.statSync(credsPath);
254
- const ageHours = (Date.now() - stats.mtime.getTime()) / (1000 * 60 * 60);
255
-
256
- this.logger.info(`πŸ“„ Credenciais encontradas (${ageHours.toFixed(1)}h atrΓ‘s)`);
257
-
258
- // REMOVIDO: Limpeza automΓ‘tica apΓ³s 24h - MantΓ©m conexΓ£o estΓ‘vel no HF
259
- // Credenciais sΓ£o mantidas indefinidamente para evitar QR code diΓ‘rio
260
-
261
- // Verifica se o arquivo de credenciais Γ© vΓ‘lido
262
- const credsContent = fs.readFileSync(credsPath, 'utf8');
263
- const creds = JSON.parse(credsContent);
264
-
265
- if (!creds || !creds.me) {
266
- this.logger.warn('πŸ“„ Credenciais invΓ‘lidas detectadas. ForΓ§ando novo login...');
267
- fs.rmSync(authPath, { recursive: true, force: true });
268
- this.isConnected = false;
269
- this.currentQR = null;
270
- this.BOT_JID = null;
271
- return;
272
- }
273
-
274
- this.logger.info('βœ… Credenciais vΓ‘lidas encontradas');
275
- this.logger.info(`πŸ€– Bot registrado como: ${creds.me.id || 'Desconhecido'}`);
276
- } else {
277
- this.logger.info('πŸ“± Nenhuma credencial salva. Aguardando QR code...');
278
- this.isConnected = false;
279
- this.BOT_JID = null;
280
- }
281
- } catch (error) {
282
- this.logger.warn('⚠️ Erro ao verificar credenciais:', error.message);
283
- // Em caso de erro, limpa tudo e forΓ§a novo login
284
- try {
285
- if (fs.existsSync(authPath)) {
286
- fs.rmSync(authPath, { recursive: true, force: true });
287
- }
288
- } catch (e) {
289
- this.logger.warn('Erro ao limpar pasta auth:', e.message);
290
- }
291
- this.isConnected = false;
292
- this.currentQR = null;
293
- this.BOT_JID = null;
294
- }
295
  }
296
 
297
  /**
298
- * Conecta ao WhatsApp
299
  */
300
  async connect() {
301
  try {
302
- // Marca o tempo de inΓ­cio da conexΓ£o
303
- this.connectionStartTime = Date.now();
304
-
305
- // Limpa timeout anterior se existir
306
- if (this.qrTimeout) {
307
- clearTimeout(this.qrTimeout);
308
- this.qrTimeout = null;
309
- }
310
-
311
- // Evita mΓΊltiplas conexΓ΅es simultΓ’neas
312
- if (this.sock && this.sock.ws && this.sock.ws.readyState === 1) {
313
- this.logger.info('πŸ”„ JΓ‘ conectado, ignorando tentativa de reconexΓ£o');
314
- return;
315
- }
316
-
317
- this.logger.info('πŸ”— Conectando ao WhatsApp...');
318
- this.logger.info(`πŸ“ Usando pasta de auth: ${this.config.AUTH_FOLDER}`);
319
-
320
- // ═══ VERIFICAÇÃO DE DNS OTIMIZADA PARA HF SPACES ═══
321
- console.log('πŸ” Verificando resoluΓ§Γ£o DNS (via HFCorrections)...');
322
-
323
- // Verifica conectividade de rede antes de tentar conectar
324
- try {
325
- await HFCorrections.verifyHFNetwork();
326
- } catch (netError) {
327
- console.log('⚠️ Verificação de rede ignorada, continuando...');
328
- }
329
-
330
- // Verifica se devemos limpar credenciais antigas
331
- await this._checkAndCleanOldAuth();
332
 
333
- const { state, saveCreds } = await useMultiFileAuthState(this.config.AUTH_FOLDER);
334
  const { version } = await fetchLatestBaileysVersion();
 
335
 
336
- this.logger.info(`πŸ“¦ VersΓ£o Baileys: ${version}`);
337
-
338
- // ConfiguraΓ§Γ΅es otimizadas para Hugging Face Spaces
339
- const socketConfig = {
340
  version,
341
- auth: state,
342
- logger: pino({ level: 'silent' }), // Silencia logs do Baileys
343
- browser: Browsers.ubuntu('Chrome'), // Alterado para Ubuntu/Chrome (mais estΓ‘vel em container)
344
- markOnlineOnConnect: true,
345
- syncFullHistory: false,
346
- printQRInTerminal: false, // NΓ£o mostra QR no terminal (usamos web)
347
- connectTimeoutMs: 180000, // Aumentado para 3 minutos para ambientes lentos
348
- qrTimeout: 120000, // 120 segundos para QR
349
- defaultQueryTimeoutMs: 60000,
350
- // ConfiguraΓ§Γ΅es otimizadas para ambientes com conectividade limitada
351
- keepAliveIntervalMs: 45000, // Aumentado para manter conexΓ£o
352
- retryRequestDelayMs: 8000, // Aumentado delay entre retentativas
353
- maxRetries: 3, // Reduzido nΓΊmero de retentativas
354
- // ConfiguraΓ§Γ΅es especΓ­ficas para ambientes restritos
355
- agent: this._createCustomAgent(),
356
- // ForΓ§a uso de IPv4
357
- fetchAgent: this._createCustomAgent(),
358
- // ConfiguraΓ§Γ΅es de WebSocket otimizadas
359
- wsOptions: HFCorrections.createWebSocketOptions(),
360
- getMessage: async (key) => {
361
- if (!key) return undefined;
362
- try {
363
- if (this.store && typeof this.store.loadMessage === 'function') {
364
- const msg = await this.store.loadMessage(key.remoteJid, key.id);
365
- return msg ? msg.message : undefined;
366
- }
367
- } catch (e) {
368
- this.logger.debug('Erro ao carregar mensagem:', e.message);
369
- }
370
- return undefined;
371
- }
372
- };
373
-
374
- this.logger.debug('βš™οΈ ConfiguraΓ§Γ£o do socket:', JSON.stringify(socketConfig, null, 2));
375
-
376
- this.sock = makeWASocket(socketConfig);
377
-
378
- // Vincula store se existir
379
- try {
380
- if (this.store && typeof this.store.bind === 'function') {
381
- this.store.bind(this.sock.ev);
382
- this.logger.debug('βœ… Store vinculada ao socket');
383
- }
384
- } catch (e) {
385
- this.logger.warn('⚠️ Erro ao vincular store:', e.message);
386
- }
387
-
388
- // Event listeners
389
- this.sock.ev.on('creds.update', saveCreds);
390
- this.sock.ev.on('connection.update', this.handleConnectionUpdate.bind(this));
391
- this.sock.ev.on('messages.upsert', this.handleMessagesUpsert.bind(this));
392
-
393
- // Timeout para forΓ§ar geraΓ§Γ£o de QR se nΓ£o vier automaticamente
394
- this.qrTimeout = setTimeout(() => {
395
- if (!this.currentQR && !this.isConnected) {
396
- this.logger.warn('⏰ QR não gerado automaticamente. Tentando forçar...');
397
- this._forceQRGeneration();
398
- }
399
- }, 25000); // Aumentado para 25 segundos
400
-
401
- this.logger.info('βœ… ConexΓ£o inicializada - Aguardando QR code ou conexΓ£o...');
402
-
403
- } catch (error) {
404
- this.logger.error('❌ Erro na conexão:', error.message);
405
- this.logger.error(error.stack);
406
-
407
- // Reconecta automaticamente apΓ³s erro
408
- this._scheduleReconnect();
409
- throw error;
410
- }
411
- }
412
-
413
- /**
414
- * Agenda reconexΓ£o automΓ‘tica
415
- */
416
- _scheduleReconnect() {
417
- const reconnectDelay = Math.min(10000 * Math.pow(1.5, this.reconnectAttempts), 120000);
418
- this.reconnectAttempts = (this.reconnectAttempts || 0) + 1;
419
-
420
- this.logger.info(`πŸ”„ Reconectando em ${reconnectDelay/1000}s... (tentativa ${this.reconnectAttempts})`);
421
-
422
- setTimeout(() => {
423
- this.logger.info('πŸ”„ Iniciando reconexΓ£o...');
424
- this.connect().catch(e => this.logger.error('Erro na reconexΓ£o:', e.message));
425
- }, reconnectDelay);
426
- }
427
-
428
- /**
429
- * Handle connection update
430
- */
431
- async handleConnectionUpdate(update) {
432
- try {
433
- const { connection, lastDisconnect, qr } = update;
434
-
435
- // Log detalhado para debugging
436
- this.logger.debug(`πŸ”„ Connection update received:`, {
437
- connection,
438
- hasQR: !!qr,
439
- lastDisconnect: lastDisconnect ? 'yes' : 'no'
440
- });
441
-
442
- if (qr) {
443
- this.currentQR = qr;
444
- this.logger.info('πŸ“± QR Code gerado - pronto para scan!');
445
- this.logger.info(`πŸ”— Acesse: http://localhost:${this.config.PORT}/qr`);
446
-
447
- // Log extra para web
448
- console.log('\n' + '═'.repeat(70));
449
- console.log('πŸ“± QR CODE DISPONÍVEL NA WEB!');
450
- console.log('═'.repeat(70));
451
- console.log(`πŸ”— URL: http://localhost:${this.config.PORT}/qr`);
452
- console.log('⏳ VÑlido por 120 segundos');
453
- console.log('πŸ“± Abra essa URL no seu navegador');
454
- console.log('═'.repeat(70) + '\n');
455
-
456
- // Chama callback se existir
457
- if (this.eventListeners.onQRGenerated) {
458
- this.eventListeners.onQRGenerated(qr);
459
- }
460
-
461
- // Limpa timeout de forΓ§a
462
- if (this.qrTimeout) {
463
- clearTimeout(this.qrTimeout);
464
- this.qrTimeout = null;
465
- }
466
- }
467
-
468
- if (connection === 'open') {
469
- this.BOT_JID = (this.sock.user && this.sock.user.id) || null;
470
- this.isConnected = true;
471
- this.lastProcessedTime = Date.now();
472
- this.currentQR = null; // Limpa QR apΓ³s conexΓ£o
473
- this.reconnectAttempts = 0; // Reseta contador de tentativas
474
-
475
- // Calcula tempo de conexΓ£o
476
- const connectionTime = Date.now() - this.connectionStartTime;
477
 
478
- // Limpa timeout de forΓ§a
479
- if (this.qrTimeout) {
480
- clearTimeout(this.qrTimeout);
481
- this.qrTimeout = null;
482
- }
483
-
484
- this.logger.info('\n' + '═'.repeat(70));
485
- this.logger.info('βœ… AKIRA BOT V21 ONLINE!');
486
- this.logger.info('═'.repeat(70));
487
- this.logger.info(`πŸ€– Nome: ${this.config.BOT_NAME}`);
488
- this.logger.info(`πŸ“± NΓΊmero: ${this.config.BOT_NUMERO_REAL}`);
489
- this.logger.info(`πŸ”— JID: ${this.BOT_JID}`);
490
- this.logger.info(`⏱️ Tempo de conexão: ${connectionTime}ms`);
491
- this.logger.info('═'.repeat(70) + '\n');
492
-
493
- // Chama callback se existir
494
- if (this.eventListeners.onConnected) {
495
- this.eventListeners.onConnected(this.BOT_JID);
496
- }
497
-
498
- // Log extra para web
499
- console.log('\n' + '═'.repeat(70));
500
- console.log('βœ… BOT CONECTADO COM SUCESSO!');
501
- console.log('═'.repeat(70));
502
- console.log(`πŸ€– Bot estΓ‘ online e pronto para uso`);
503
- console.log(`πŸ“± NΓΊmero: ${this.config.BOT_NUMERO_REAL}`);
504
- console.log(`πŸ”— JID: ${this.BOT_JID}`);
505
- console.log('═'.repeat(70) + '\n');
506
-
507
- } else if (connection === 'close') {
508
- this.isConnected = false;
509
- this.currentQR = null;
510
 
511
- const code = (lastDisconnect && lastDisconnect.error && lastDisconnect.error.output && lastDisconnect.error.output.statusCode) || undefined;
512
- const reason = (lastDisconnect && lastDisconnect.error && lastDisconnect.error.message) || 'desconhecido';
513
-
514
- this.logger.warn(`⚠️ Conexão perdida (código: ${code}, motivo: ${reason})`);
515
-
516
- // CΓ³digos de erro especΓ­ficos
517
- if (code === 408) {
518
- this.logger.warn('⏰ Timeout de conexão - possível problema de rede');
519
- } else if (code === 401) {
520
- this.logger.warn('πŸ” Credenciais rejeitadas - serΓ‘ necessΓ‘rio novo login');
521
- // Limpa credenciais em caso de auth error
522
- this._cleanAuthOnError();
523
- } else if (code === 403) {
524
- this.logger.warn('🚫 Conta banida ou bloqueada');
525
- } else if (code === 503) {
526
- this.logger.warn('🌐 Serviço indisponível - servidor WhatsApp offline');
527
- }
528
-
529
- // Chama callback se existir
530
- if (this.eventListeners.onDisconnected) {
531
- this.eventListeners.onDisconnected(code, reason);
532
- }
533
-
534
- // Reconecta com backoff exponencial otimizado
535
- const reconnectDelay = Math.min(8000 * Math.pow(1.8, this.reconnectAttempts || 0), 90000); // MΓ‘ximo 90 segundos
536
- this.reconnectAttempts = (this.reconnectAttempts || 0) + 1;
537
-
538
- this.logger.info(`πŸ”„ Reconectando em ${reconnectDelay/1000}s... (tentativa ${this.reconnectAttempts})`);
539
 
540
- // Limpa socket atual
541
- if (this.sock) {
542
- try {
543
- this.sock.ev.removeAllListeners();
544
- if (this.sock.ws) {
545
- this.sock.ws.close();
546
- }
547
- } catch (e) {
548
- this.logger.warn('Erro ao limpar socket:', e.message);
549
- }
550
- this.sock = null;
551
- }
552
-
553
- setTimeout(() => {
554
- this.logger.info('πŸ”„ Iniciando reconexΓ£o...');
555
- this.connect().catch(e => this.logger.error('Erro na reconexΓ£o:', e.message));
556
- }, reconnectDelay);
557
-
558
- } else if (connection === 'connecting') {
559
- this.logger.info('πŸ”„ Conectando ao WhatsApp...');
560
- // Limpa QR code anterior ao tentar nova conexΓ£o
561
- this.currentQR = null;
562
- }
563
-
564
- } catch (error) {
565
- this.logger.error('❌ Erro em handleConnectionUpdate:', error.message);
566
- this.logger.error(error.stack);
567
- }
568
- }
569
-
570
- /**
571
- * Handle messages upsert
572
- */
573
- async handleMessagesUpsert({ messages }) {
574
- try {
575
- const m = messages[0];
576
- if (!m || !m.message || m.key.fromMe) return;
577
-
578
- // DeduplicaΓ§Γ£o
579
- if (this.processadas.has(m.key.id)) return;
580
- this.processadas.add(m.key.id);
581
- setTimeout(() => this.processadas.delete(m.key.id), this.config.MESSAGE_DEDUP_TIME_MS);
582
-
583
- // Ignorar mensagens antigas (mais de 10 segundos)
584
- const messageTime = m.messageTimestamp * 1000;
585
- const currentTime = Date.now();
586
- if (messageTime && messageTime < currentTime - 10000) {
587
- this.logger.debug(`⏭️ Mensagem antiga ignorada: ${messageTime}`);
588
- return;
589
- }
590
-
591
- // Processa mensagem
592
- await this.processMessage(m);
593
-
594
- } catch (error) {
595
- this.logger.error('❌ Erro em handleMessagesUpsert:', error.message);
596
- }
597
- }
598
-
599
- /**
600
- * Processa mensagem
601
- */
602
- async processMessage(m) {
603
- try {
604
- const ehGrupo = String(m.key.remoteJid || '').endsWith('@g.us');
605
- const numeroReal = this.messageProcessor.extractUserNumber(m);
606
- const nome = m.pushName || numeroReal;
607
- const texto = this.messageProcessor.extractText(m).trim();
608
- const temAudio = this.messageProcessor.hasAudio(m);
609
-
610
- // Log da mensagem recebida
611
- this.logger.info(`πŸ“© [${ehGrupo ? 'GRUPO' : 'PV'}] ${nome}: ${texto.substring(0, 50)}${texto.length > 50 ? '...' : ''}`);
612
-
613
- // Verifica ban
614
- if (this.moderationSystem.isBanned(numeroReal)) {
615
- this.logger.warn(`🚫 Mensagem de usuÑrio banido ignorada: ${nome}`);
616
- return;
617
- }
618
-
619
- // Verifica spam
620
- if (this.moderationSystem.checkSpam(numeroReal)) {
621
- this.logger.warn(`⚠️ Spam detectado de ${nome}`);
622
- return;
623
- }
624
-
625
- // ModeraΓ§Γ£o em grupos
626
- if (ehGrupo && m.key.participant) {
627
- if (this.moderationSystem.isUserMuted(m.key.remoteJid, m.key.participant)) {
628
- await this.handleMutedUserMessage(m, nome);
629
- return;
630
- }
631
-
632
- if (this.moderationSystem.isAntiLinkActive(m.key.remoteJid) && texto && this.moderationSystem.containsLink(texto)) {
633
- await this.handleAntiLinkViolation(m, nome);
634
- return;
635
- }
636
- }
637
-
638
- // Award group XP (auto XP system)
639
- try {
640
- if (ehGrupo && this.config.FEATURE_LEVELING) {
641
- const uid = m.key.participant || m.key.remoteJid;
642
- const xpAmount = Math.floor(Math.random() * (25 - 15 + 1)) + 15;
643
- const { rec, leveled } = this.levelSystem.awardXp(m.key.remoteJid, uid, xpAmount);
644
- if (leveled) {
645
- const patente = typeof this.getPatente === 'function' ? this.getPatente(rec.level) : `NΓ­vel ${rec.level}`;
646
- await this.sock.sendMessage(m.key.remoteJid, {
647
- text: `πŸŽ‰ @${uid.split('@')[0]} subiu para o nΓ­vel ${rec.level}! πŸ… ${patente}`,
648
- contextInfo: { mentionedJid: [uid] }
649
- });
650
- if (rec.level >= this.levelSystem.maxLevel) {
651
- const maxRes = await this.levelSystem.registerMaxLevelUser(m.key.remoteJid, uid, m.pushName || uid, this.sock);
652
- if (maxRes && maxRes.promoted) {
653
- await this.sock.sendMessage(m.key.remoteJid, {
654
- text: `🎊 ${m.pushName || uid} foi promovido automaticamente a ADM!`
655
- });
656
- }
657
- }
658
- }
659
  }
660
- } catch (e) {
661
- this.logger.warn('Erro awarding XP:', e.message);
662
- }
663
-
664
- // ObtΓ©m contexto de reply
665
- const replyInfo = this.messageProcessor.extractReplyInfo(m);
666
-
667
- // Processa Γ‘udio
668
- if (temAudio) {
669
- await this.handleAudioMessage(m, nome, numeroReal, replyInfo, ehGrupo);
670
- return;
671
- }
672
-
673
- // Processa texto
674
- if (texto) {
675
- await this.handleTextMessage(m, nome, numeroReal, texto, replyInfo, ehGrupo);
676
- }
677
-
678
- } catch (error) {
679
- this.logger.error('❌ Erro ao processar mensagem:', error.message);
680
- }
681
- }
682
-
683
- /**
684
- * Handle audio message
685
- */
686
- async handleAudioMessage(m, nome, numeroReal, replyInfo, ehGrupo) {
687
- this.logger.info(`🎀 [ÁUDIO] ${nome}`);
688
-
689
- try {
690
- // Decodifica Γ‘udio
691
- const audioBuffer = await this.mediaProcessor.downloadMedia(
692
- m.message.audioMessage,
693
- 'audio'
694
- );
695
-
696
- if (!audioBuffer) {
697
- this.logger.error('❌ Erro ao baixar Ñudio');
698
- return;
699
- }
700
-
701
- // STT
702
- const transcricao = await this.audioProcessor.speechToText(audioBuffer);
703
 
704
- if (!transcricao.sucesso) {
705
- this.logger.warn('⚠️ Falha na transcrição');
706
- return;
707
  }
708
 
709
- const textoAudio = transcricao.texto;
710
- this.logger.info(`πŸ“ TranscriΓ§Γ£o: ${textoAudio.substring(0, 80)}...`);
711
 
712
- // Processa como texto
713
- await this.handleTextMessage(m, nome, numeroReal, textoAudio, replyInfo, ehGrupo, true);
714
-
715
- } catch (error) {
716
- this.logger.error('❌ Erro ao processar Ñudio:', error.message);
717
- }
718
- }
719
 
720
- /**
721
- * Handle text message
722
- */
723
- async handleTextMessage(m, nome, numeroReal, texto, replyInfo, ehGrupo, foiAudio = false) {
724
- try {
725
- // Check rate limit
726
- if (!this.messageProcessor.checkRateLimit(numeroReal)) {
727
- await this.sock.sendMessage(m.key.remoteJid, {
728
- text: '⏰ VocΓͺ estΓ‘ usando comandos muito rΓ‘pido. Aguarde um pouco.'
729
- });
730
- return;
731
- }
732
 
733
- // Handle commands centrally (short-circuit if handled)
734
- try {
735
- if (this.commandHandler) {
736
- const handled = await this.commandHandler.handle(m, { nome, numeroReal, texto, replyInfo, ehGrupo });
737
- if (handled) {
738
- this.logger.info(`⚑ Comando tratado: ${texto.substring(0, 30)}...`);
739
- return;
740
- }
741
  }
742
- } catch (e) {
743
- this.logger.warn('Erro no CommandHandler:', e.message);
744
- }
745
 
746
- // Verifica se deve responder
747
- let deveResponder = false;
748
-
749
- if (foiAudio) {
750
- // Audio sempre responde em PV
751
- if (!ehGrupo) {
752
- deveResponder = true;
753
- } else {
754
- // Em grupos, responde se for reply ao bot ou menΓ§Γ£o
755
- if (replyInfo && replyInfo.ehRespostaAoBot) {
756
- deveResponder = true;
757
- } else if (this.messageProcessor.isBotMentioned(m)) {
758
- deveResponder = true;
759
- }
760
- }
761
- } else {
762
- // Texto
763
- if (replyInfo && replyInfo.ehRespostaAoBot) {
764
- deveResponder = true;
765
- } else if (!ehGrupo) {
766
- // Em PV sempre responde
767
- deveResponder = true;
768
- } else {
769
- // Em grupo, responde se mencionado
770
- if (this.messageProcessor.isBotMentioned(m)) {
771
- deveResponder = true;
772
  }
773
  }
774
- }
775
 
776
- if (!deveResponder) {
777
- this.logger.debug(`⏭️ Mensagem ignorada (sem ativação): ${texto.substring(0, 50)}...`);
778
- return;
779
- }
780
-
781
- this.logger.info(`\nπŸ”₯ [PROCESSANDO] ${nome}: ${texto.substring(0, 60)}...`);
782
-
783
- // ConstrΓ³i payload
784
- const payload = this.apiClient.buildPayload({
785
- usuario: nome,
786
- numero: numeroReal,
787
- mensagem: texto,
788
- tipo_conversa: ehGrupo ? 'grupo' : 'pv',
789
- tipo_mensagem: foiAudio ? 'audio' : 'texto',
790
- mensagem_citada: (replyInfo && replyInfo.textoMensagemCitada) || '',
791
- reply_metadata: replyInfo ? {
792
- reply_to_bot: replyInfo.ehRespostaAoBot,
793
- quoted_author_name: replyInfo.quemEscreveuCitacao || 'desconhecido'
794
- } : { is_reply: false, reply_to_bot: false }
795
- });
796
-
797
- // Chama API
798
- const resultado = await this.apiClient.processMessage(payload);
799
-
800
- if (!resultado.success) {
801
- this.logger.error('❌ Erro na API:', resultado.error);
802
- await this.sock.sendMessage(m.key.remoteJid, {
803
- text: 'Eita! Tive um problema aqui. Tenta de novo em um segundo?'
804
- });
805
- return;
806
- }
807
-
808
- let resposta = resultado.resposta || 'Sem resposta';
809
-
810
- // TTS se foi Γ‘udio
811
- if (foiAudio) {
812
- const ttsResult = await this.audioProcessor.textToSpeech(resposta);
813
- if (!ttsResult.sucesso) {
814
- await this.sock.sendMessage(m.key.remoteJid, { text: resposta }, { quoted: m });
815
- } else {
816
- await this.sock.sendMessage(m.key.remoteJid, {
817
- audio: ttsResult.buffer,
818
- mimetype: 'audio/mp4',
819
- ptt: true
820
- }, { quoted: m });
821
  }
822
- } else {
823
- // Simula digitaΓ§Γ£o
824
- const tempoDigitacao = Math.min(
825
- Math.max(resposta.length * this.config.TYPING_SPEED_MS, this.config.MIN_TYPING_TIME_MS),
826
- this.config.MAX_TYPING_TIME_MS
827
- );
828
-
829
- await this.simulateTyping(m.key.remoteJid, tempoDigitacao);
830
-
831
- const opcoes = ehGrupo || (replyInfo && replyInfo.ehRespostaAoBot) ? { quoted: m } : {};
832
- await this.sock.sendMessage(m.key.remoteJid, { text: resposta }, opcoes);
833
- }
834
-
835
- this.logger.info(`βœ… [RESPONDIDO] ${resposta.substring(0, 80)}...\n`);
836
-
837
- } catch (error) {
838
- this.logger.error('❌ Erro ao processar texto:', error.message);
839
- }
840
- }
841
-
842
- /**
843
- * Simula digitaΓ§Γ£o
844
- */
845
- async simulateTyping(jid, durationMs) {
846
- try {
847
- await this.sock.sendPresenceUpdate('available', jid);
848
- await delay(300);
849
- await this.sock.sendPresenceUpdate('composing', jid);
850
- await delay(durationMs);
851
- await this.sock.sendPresenceUpdate('paused', jid);
852
- } catch (e) {
853
- // Ignora erros de presenΓ§a silenciosamente
854
- this.logger.debug('Erro na simulaΓ§Γ£o de digitaΓ§Γ£o:', e.message);
855
- }
856
- }
857
-
858
- /**
859
- * Handle muted user
860
- */
861
- async handleMutedUserMessage(m, nome) {
862
- try {
863
- this.logger.warn(`πŸ”‡ UsuΓ‘rio ${nome} tentou falar durante mute`);
864
-
865
- await this.sock.groupParticipantsUpdate(
866
- m.key.remoteJid,
867
- [m.key.participant],
868
- 'remove'
869
- );
870
-
871
- await this.sock.sendMessage(m.key.remoteJid, {
872
- text: `🚫 *${nome} foi removido por enviar mensagem durante período de mute!*`
873
  });
874
 
875
- } catch (error) {
876
- this.logger.error('❌ Erro ao remover usuÑrio mutado:', error.message);
877
- }
878
- }
879
-
880
- /**
881
- * Handle antilink violation
882
- */
883
- async handleAntiLinkViolation(m, nome) {
884
- try {
885
- this.logger.warn(`πŸ”— [ANTI-LINK] ${nome} enviou link`);
886
-
887
- await this.sock.groupParticipantsUpdate(
888
- m.key.remoteJid,
889
- [m.key.participant],
890
- 'remove'
891
- );
892
-
893
- await this.sock.sendMessage(m.key.remoteJid, {
894
- text: `🚫 *${nome} foi removido por enviar link!*\nπŸ”’ Anti-link estΓ‘ ativado neste grupo.`
895
- });
896
-
897
- } catch (error) {
898
- this.logger.error('❌ Erro ao banir por link:', error.message);
899
- }
900
- }
901
-
902
- /**
903
- * Limpa credenciais em caso de erro de autenticaΓ§Γ£o
904
- */
905
- _cleanAuthOnError() {
906
- const fs = require('fs');
907
- try {
908
- if (fs.existsSync(this.config.AUTH_FOLDER)) {
909
- fs.rmSync(this.config.AUTH_FOLDER, { recursive: true, force: true });
910
- this.logger.info('🧹 Credenciais limpas devido a erro de autenticação');
911
- }
912
- this.isConnected = false;
913
- this.currentQR = null;
914
- this.BOT_JID = null;
915
- this.reconnectAttempts = 0;
916
- } catch (error) {
917
- this.logger.error('❌ Erro ao limpar credenciais:', error.message);
918
- }
919
- }
920
-
921
- /**
922
- * ForΓ§a geraΓ§Γ£o de QR code se nΓ£o vier automaticamente
923
- */
924
- async _forceQRGeneration() {
925
- try {
926
- this.logger.info('πŸ”„ ForΓ§ando geraΓ§Γ£o de QR code...');
927
-
928
- // Limpa estado atual
929
- this.currentQR = null;
930
- this.isConnected = false;
931
-
932
- if (this.sock) {
933
  try {
934
- this.sock.ev.removeAllListeners();
935
- if (this.sock.ws) {
936
- this.sock.ws.close();
 
 
 
937
  }
938
- } catch (e) {
939
- this.logger.warn('Erro ao limpar socket:', e.message);
940
  }
941
- this.sock = null;
942
- }
943
-
944
- // Limpa timeout se existir
945
- if (this.qrTimeout) {
946
- clearTimeout(this.qrTimeout);
947
- this.qrTimeout = null;
948
- }
949
-
950
- // Pequeno delay antes de reconectar
951
- await delay(3000);
952
-
953
- // Reconecta
954
- await this.connect();
955
-
956
- } catch (error) {
957
- this.logger.error('❌ Erro ao forçar QR:', error.message);
958
- }
959
- }
960
 
961
- /**
962
- * ObtΓ©m QR Code atual
963
- */
964
- getQRCode() {
965
- try {
966
- // Se jΓ‘ estΓ‘ conectado, nΓ£o precisa de QR
967
- if (this.isConnected) {
968
- this.logger.debug('βœ… Bot jΓ‘ conectado, sem necessidade de QR code');
969
- return null;
970
- }
971
-
972
- // Se tem QR code armazenado, retorna
973
- if (this.currentQR) {
974
- this.logger.debug(`πŸ“± QR code disponΓ­vel (${this.currentQR.length} caracteres)`);
975
- return this.currentQR;
976
- }
977
-
978
- // Se nΓ£o estΓ‘ conectado e nΓ£o tem QR, verifica estado
979
- this.logger.debug('πŸ”„ Bot nΓ£o conectado e sem QR code disponΓ­vel');
980
-
981
- // Verifica se a conexΓ£o estΓ‘ em andamento
982
- if (this.sock && this.sock.ws) {
983
- const state = this.sock.ws.readyState;
984
- if (state === 0) { // CONNECTING
985
- this.logger.debug('πŸ”„ ConexΓ£o em andamento... aguarde QR code');
986
- return null;
987
- } else if (state === 1) { // OPEN
988
- this.logger.debug('βœ… ConexΓ£o jΓ‘ aberta, mas isConnected nΓ£o atualizado');
989
- return null;
990
- }
991
- }
992
-
993
- // Se chegou aqui, precisa reconectar
994
- this.logger.debug('⚠️ Bot desconectado e sem QR. Talvez seja necessÑrio reiniciar a conexão.');
995
- return null;
996
-
997
  } catch (error) {
998
- this.logger.error('❌ Erro ao obter QR code:', error.message);
999
- return null;
1000
  }
1001
  }
1002
 
1003
  /**
1004
- * ObtΓ©m status
1005
  */
1006
  getStatus() {
1007
  return {
1008
  isConnected: this.isConnected,
1009
- botJid: this.BOT_JID,
1010
- botNumero: this.config.BOT_NUMERO_REAL,
1011
- botName: this.config.BOT_NAME,
1012
- version: this.config.BOT_VERSION,
1013
- uptime: Math.floor(process.uptime()),
1014
- hasQR: this.currentQR !== null && this.currentQR !== undefined,
1015
  reconnectAttempts: this.reconnectAttempts,
1016
- connectionStartTime: this.connectionStartTime
1017
  };
1018
  }
1019
 
1020
- /**
1021
- * Retorna estatΓ­sticas
1022
- */
1023
- getStats() {
1024
- const status = this.getStatus();
1025
-
1026
- return {
1027
- ...status,
1028
- api: this.apiClient ? this.apiClient.getStats() : { error: 'API nΓ£o inicializada' },
1029
- audio: this.audioProcessor ? this.audioProcessor.getStats() : { error: 'AudioProcessor nΓ£o inicializado' },
1030
- media: this.mediaProcessor ? this.mediaProcessor.getStats() : { error: 'MediaProcessor nΓ£o inicializado' },
1031
- message: this.messageProcessor ? this.messageProcessor.getStats() : { error: 'MessageProcessor nΓ£o inicializado' },
1032
- moderation: this.moderationSystem ? this.moderationSystem.getStats() : { error: 'ModerationSystem nΓ£o inicializado' },
1033
- leveling: this.levelSystem ? this.levelSystem.getStats() : { error: 'LevelSystem nΓ£o inicializado' },
1034
- componentsInitialized: {
1035
- apiClient: !!this.apiClient,
1036
- audioProcessor: !!this.audioProcessor,
1037
- mediaProcessor: !!this.mediaProcessor,
1038
- messageProcessor: !!this.messageProcessor,
1039
- moderationSystem: !!this.moderationSystem,
1040
- levelSystem: !!this.levelSystem,
1041
- commandHandler: !!this.commandHandler
1042
- }
1043
- };
1044
- }
1045
-
1046
- /**
1047
- * Registra event listeners
1048
- */
1049
- on(event, callback) {
1050
- if (event === 'qr') {
1051
- this.eventListeners.onQRGenerated = callback;
1052
- } else if (event === 'connected') {
1053
- this.eventListeners.onConnected = callback;
1054
- } else if (event === 'disconnected') {
1055
- this.eventListeners.onDisconnected = callback;
1056
- } else {
1057
- this.logger.warn(`⚠️ Evento não suportado: ${event}`);
1058
- }
1059
- }
1060
-
1061
- /**
1062
- * Limpa cache de componentes
1063
- */
1064
- clearCache() {
1065
- try {
1066
- if (this.audioProcessor && typeof this.audioProcessor.clearCache === 'function') {
1067
- this.audioProcessor.clearCache();
1068
- }
1069
- if (this.mediaProcessor && typeof this.mediaProcessor.clearCache === 'function') {
1070
- this.mediaProcessor.clearCache();
1071
- }
1072
- if (this.messageProcessor && typeof this.messageProcessor.clearCache === 'function') {
1073
- this.messageProcessor.clearCache();
1074
- }
1075
- this.logger.info('βœ… Cache limpo');
1076
- } catch (error) {
1077
- this.logger.error('❌ Erro ao limpar cache:', error.message);
1078
- }
1079
- }
1080
-
1081
- /**
1082
- * Verifica se o bot estΓ‘ pronto
1083
- */
1084
- isReady() {
1085
- return this.isConnected && this.sock && this.sock.ws && this.sock.ws.readyState === 1;
1086
  }
1087
 
1088
  /**
1089
- * Desconecta o bot
1090
  */
1091
- async disconnect() {
1092
  try {
1093
- this.logger.info('πŸ”΄ Desconectando bot...');
1094
-
1095
- if (this.sock) {
1096
- try {
1097
- this.sock.ev.removeAllListeners();
1098
- if (this.sock.ws) {
1099
- this.sock.ws.close();
1100
- }
1101
- } catch (e) {
1102
- this.logger.warn('Erro ao fechar socket:', e.message);
1103
- }
1104
- this.sock = null;
1105
- }
1106
-
1107
- this.isConnected = false;
1108
- this.currentQR = null;
1109
- this.BOT_JID = null;
1110
-
1111
- this.logger.info('βœ… Bot desconectado');
1112
- } catch (error) {
1113
- this.logger.error('❌ Erro ao desconectar:', error.message);
1114
  }
1115
  }
1116
  }
1117
 
1118
- module.exports = BotCore;
 
1
  /**
2
  * ═══════════════════════════════════════════════════════════════════════
3
+ * CLASSE: BotCore (HF SPACES EDITION - SMART LOAD)
4
  * ═══════════════════════════════════════════════════════════════════════
5
+ * NΓΊcleo do bot: Baileys wrapper, event handling e orquestraΓ§Γ£o.
 
6
  * ═══════════════════════════════════════════════════════════════════════
7
  */
8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  const {
10
  default: makeWASocket,
11
  useMultiFileAuthState,
12
  fetchLatestBaileysVersion,
13
  Browsers,
14
  delay,
15
+ DisconnectReason
16
  } = require('@whiskeysockets/baileys');
17
  const pino = require('pino');
18
+ const path = require('path');
19
+ const fs = require('fs');
 
 
 
 
 
 
20
 
21
+ /**
22
+ * FunΓ§Γ£o de carregamento inteligente para evitar erros de "Module Not Found"
23
+ * no ambiente do Hugging Face Spaces.
24
+ */
25
+ function smartRequire(moduleName) {
26
+ try {
27
+ return require(`./${moduleName}`);
28
+ } catch (e) {
29
+ try {
30
+ return require(`./modules/${moduleName}`);
31
+ } catch (e2) {
32
+ console.error(`❌ [BotCore] Erro ao carregar componente: ${moduleName}`);
33
+ return null;
34
+ }
35
+ }
36
+ }
37
 
38
+ // Carregamento dos componentes e correΓ§Γ΅es
39
+ const HFCorrections = smartRequire('HFCorrections');
40
+ const ConfigManager = smartRequire('ConfigManager');
41
+ const APIClient = smartRequire('APIClient');
42
+ const AudioProcessor = smartRequire('AudioProcessor');
43
+ const MediaProcessor = smartRequire('MediaProcessor');
44
+ const MessageProcessor = smartRequire('MessageProcessor');
45
+ const ModerationSystem = smartRequire('ModerationSystem');
46
+ const LevelSystem = smartRequire('LevelSystem');
47
+ const CommandHandler = smartRequire('CommandHandler');
48
 
49
+ class BotCore {
50
+ constructor() {
51
+ this.config = ConfigManager ? ConfigManager.getInstance() : {};
52
+ this.logger = pino({ level: 'silent' });
53
  this.sock = null;
54
+ this.state = null;
55
+ this.saveCreds = null;
56
  this.currentQR = null;
57
  this.isConnected = false;
 
 
58
  this.reconnectAttempts = 0;
59
+ this.maxReconnectAttempts = 15; // Aumentado para resiliΓͺncia no HF
 
 
 
 
60
 
61
+ // InicializaΓ§Γ£o de Componentes
62
+ this.apiClient = APIClient ? new APIClient() : null;
63
+ this.moderation = ModerationSystem ? new ModerationSystem() : null;
64
+ this.levels = LevelSystem ? new LevelSystem() : null;
65
+ this.audioProcessor = AudioProcessor ? new AudioProcessor() : null;
66
+ this.mediaProcessor = MediaProcessor ? new MediaProcessor() : null;
67
+ this.commandHandler = null; // Definido apΓ³s o socket
68
  }
69
 
70
  /**
71
+ * Prepara o ambiente de arquivos
72
  */
73
  async initialize() {
74
+ console.log('πŸ“‚ Preparando diretΓ³rios de sistema...');
75
+ const folders = ['auth_info', 'temp', '/tmp/akira_data'];
76
+ folders.forEach(f => {
77
+ if (!fs.existsSync(f)) fs.mkdirSync(f, { recursive: true });
78
+ });
79
+ return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  }
81
 
82
  /**
83
+ * Estabelece conexΓ£o com WhatsApp usando DNS Hard-Patch
84
  */
85
  async connect() {
86
  try {
87
+ console.log('πŸ”§ Configurando credenciais de autenticaΓ§Γ£o...');
88
+ const { state, saveCreds } = await useMultiFileAuthState(path.join(__dirname, 'auth_info'));
89
+ this.state = state;
90
+ this.saveCreds = saveCreds;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
 
92
  const { version } = await fetchLatestBaileysVersion();
93
+ console.log(`πŸ“‘ Baileys Version: ${version.join('.')}`);
94
 
95
+ // ═══════════════════════════════════════════════════════════════════════
96
+ // SOCKET CONFIGURATION (HF SPACES OPTIMIZED)
97
+ // ═══════════════════════════════════════════════════════════════════════
98
+ this.sock = makeWASocket({
99
  version,
100
+ logger: this.logger,
101
+ printQRInTerminal: true,
102
+ auth: this.state,
103
+ browser: Browsers.ubuntu('Chrome'),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ // Aplica Agente e Patch de DNS do HFCorrections
106
+ agent: HFCorrections ? HFCorrections.createHFAgent() : undefined,
107
+ socketConfig: HFCorrections ? HFCorrections.createWebSocketOptions() : {},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
 
109
+ // ConfiguraΓ§Γ΅es de tempo de rede
110
+ connectTimeoutMs: 60000,
111
+ defaultQueryTimeoutMs: 0,
112
+ keepAliveIntervalMs: 30000,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
+ // Prioriza IPv4 para evitar erros de rede do container
115
+ getNextIp: (host) => {
116
+ const ip = HFCorrections ? HFCorrections.getWhatsAppIP() : undefined;
117
+ return ip;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  }
119
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
+ // Vincula o CommandHandler
122
+ if (CommandHandler) {
123
+ this.commandHandler = new CommandHandler(this, this.sock);
124
  }
125
 
126
+ // --- EVENTOS DO WHATSAPP ---
 
127
 
128
+ this.sock.ev.on('creds.update', this.saveCreds);
 
 
 
 
 
 
129
 
130
+ this.sock.ev.on('connection.update', async (update) => {
131
+ const { connection, lastDisconnect, qr } = update;
 
 
 
 
 
 
 
 
 
 
132
 
133
+ if (qr) {
134
+ this.currentQR = qr;
135
+ console.log('✨ [QR CODE] Novo código disponível na Web UI.');
 
 
 
 
 
136
  }
 
 
 
137
 
138
+ if (connection === 'close') {
139
+ this.isConnected = false;
140
+ const statusCode = (lastDisconnect?.error)?.output?.statusCode;
141
+ const shouldReconnect = statusCode !== DisconnectReason.loggedOut;
142
+
143
+ console.log(`⚠️ Conexão encerrada. Motivo: ${lastDisconnect?.error?.message || 'Desconhecido'}`);
144
+
145
+ if (shouldReconnect && this.reconnectAttempts < this.maxReconnectAttempts) {
146
+ this.reconnectAttempts++;
147
+ const backoff = Math.min(this.reconnectAttempts * 5000, 45000);
148
+ console.log(`πŸ”„ Tentando reconexΓ£o em ${backoff/1000}s...`);
149
+ setTimeout(() => this.connect(), backoff);
150
+ } else if (statusCode === DisconnectReason.loggedOut) {
151
+ console.error('❌ Sessão encerrada permanentemente. Apague a pasta auth_info.');
 
 
 
 
 
 
 
 
 
 
 
 
152
  }
153
  }
 
154
 
155
+ if (connection === 'open') {
156
+ this.isConnected = true;
157
+ this.reconnectAttempts = 0;
158
+ this.currentQR = null;
159
+ console.log('πŸš€ AKIRA BOT CONECTADO COM SUCESSO!');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  });
162
 
163
+ // Listener de Mensagens
164
+ this.sock.ev.on('messages.upsert', async (chatUpdate) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  try {
166
+ const m = chatUpdate.messages[0];
167
+ if (!m.message || m.key.fromMe) return;
168
+
169
+ if (MessageProcessor) {
170
+ const processor = new MessageProcessor(this, this.sock);
171
+ await processor.handle(m);
172
  }
173
+ } catch (err) {
174
+ console.error('❌ Erro ao processar mensagem recebida:', err.message);
175
  }
176
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  } catch (error) {
179
+ console.error('❌ Falha crítica no BotCore:', error);
180
+ throw error;
181
  }
182
  }
183
 
184
  /**
185
+ * Status e Acesso para a UI
186
  */
187
  getStatus() {
188
  return {
189
  isConnected: this.isConnected,
 
 
 
 
 
 
190
  reconnectAttempts: this.reconnectAttempts,
191
+ botName: this.config.BOT_NAME || 'Akira Bot'
192
  };
193
  }
194
 
195
+ getQRCode() {
196
+ return this.currentQR;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  }
198
 
199
  /**
200
+ * Envio de mensagem com tratamento global
201
  */
202
+ async sendMessage(jid, content, options = {}) {
203
  try {
204
+ if (!this.isConnected || !this.sock) return false;
205
+ return await this.sock.sendMessage(jid, content, options);
206
+ } catch (err) {
207
+ console.error(`❌ Falha no envio para ${jid}:`, err.message);
208
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
210
  }
211
  }
212
 
213
+ module.exports = BotCore;