akra35567 commited on
Commit
6598bed
·
verified ·
1 Parent(s): 4744f01

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +172 -173
index.js CHANGED
@@ -1,208 +1,207 @@
1
- // index.js - Akira Bot HF Spaces
 
 
 
2
  const {
3
  default: makeWASocket,
4
  useMultiFileAuthState,
5
  fetchLatestBaileysVersion,
6
  Browsers,
7
- delay,
8
- DisconnectReason
9
  } = require('@whiskeysockets/baileys');
10
-
11
  const pino = require('pino');
12
  const axios = require('axios');
13
  const express = require('express');
14
- const qrcode = require('qrcode');
15
 
 
 
16
  const PORT = process.env.PORT || 3000;
17
- const AKIRA_API_URL = process.env.AKIRA_API_URL || 'https://akra35567-akira.hf.space/api/akira';
18
- const BOT_REAL_JID = process.env.BOT_REAL_JID || '37839265886398@lid';
19
 
20
  let sock;
21
- let isConnecting = false;
22
  let lastProcessedTime = 0;
23
- let healthInterval;
24
- let currentQR = null;
25
-
26
- // --- Express Server para página QR ---
27
- const app = express();
28
- const server = app.listen(PORT, '0.0.0.0', () => {
29
- console.log(`Servidor ativo na porta ${server.address().port}`);
30
- });
31
-
32
- app.get('/', (req, res) => {
33
- res.send(`AKIRA BOT ONLINE | ${new Date().toLocaleString()} | <a href="/qr">Ver QR Code</a>`);
34
- });
35
 
36
- app.get('/qr', (req, res) => {
37
- if (currentQR) {
38
- qrcode.toDataURL(currentQR, { scale: 10, margin: 2 }, (err, url) => {
39
- if (err) return res.send('<h1>Erro ao gerar QR</h1>');
40
- res.send(`
41
- <!DOCTYPE html>
42
- <html>
43
- <head><title>Akira Bot - QR</title></head>
44
- <body style="font-family:sans-serif; text-align:center; background:#000; color:#0f0; padding:30px;">
45
- <h1>AKIRA BOT - ESCANEIE QR</h1>
46
- <img src="${url}" style="width:300px; margin:20px 0;">
47
- <p>Atualiza a página se QR expirar.</p>
48
- <script>setTimeout(()=>location.reload(),5000);</script>
49
- </body>
50
- </html>
51
- `);
52
- });
53
- } else {
54
- res.send('<h1>Bot já conectado!</h1><p>Atualize para ver QR.</p>');
55
- }
56
- });
57
 
58
- // --- Normaliza JID ---
59
- function normalizeJid(jid) {
60
  if (!jid) return null;
61
- return jid.replace(/@lid|@s\.whatsapp\.net|@c\.us/g, '').trim();
 
 
 
 
 
 
 
 
62
  }
63
 
64
- // --- Health Check ---
65
- function startHealthCheck() {
66
- if (healthInterval) clearInterval(healthInterval);
67
- healthInterval = setInterval(() => {
68
- console.log(`[HEALTH] ${new Date().toLocaleString()} - Bot ativo`);
69
- if (sock?.sendPresenceUpdate) {
70
- sock.sendPresenceUpdate('available').catch(() => {});
71
- }
72
- }, 20000); // ping a cada 20s
73
  }
74
 
75
- // --- Conecta ao WhatsApp ---
 
 
76
  async function connect() {
77
- if (isConnecting) return;
78
- isConnecting = true;
79
-
80
- try {
81
- const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');
82
- const { version } = await fetchLatestBaileysVersion();
83
-
84
- sock = makeWASocket({
85
- version,
86
- auth: state,
87
- logger: pino({ level: 'silent' }),
88
- browser: Browsers.macOS('Desktop'),
89
- markOnlineOnConnect: true,
90
- keepAliveIntervalMs: 30000,
91
- connectTimeoutMs: 60000,
92
- defaultQueryTimeoutMs: 60000,
93
- syncFullHistory: false,
94
- generateHighQualityLinkPreview: false,
95
- printQRInTerminal: false,
96
- getMessage: async () => ({ conversation: '' }),
97
- shouldSyncHistoryMessage: () => false,
98
- patchMessageBeforeSending: msg => msg.text ? { text: msg.text } : msg
99
- });
100
-
101
- sock.ev.on('creds.update', saveCreds);
102
-
103
- sock.ev.on('connection.update', async update => {
104
- const { connection, lastDisconnect, qr } = update;
105
-
106
- if (qr) {
107
- currentQR = qr;
108
- console.log('[QR CODE] Acesse /qr para escanear');
109
- }
110
-
111
- if (connection === 'open') {
112
- currentQR = null;
113
- console.log('AKIRA BOT ONLINE! (Multi-device ativo)');
114
- lastProcessedTime = Date.now();
115
- isConnecting = false;
116
- startHealthCheck();
117
- }
118
-
119
- if (connection === 'close') {
120
- isConnecting = false;
121
- const reason = lastDisconnect?.error?.output?.statusCode || 'desconhecido';
122
- if (reason === DisconnectReason.loggedOut) {
123
- console.log('Sessão encerrada. Escaneie novo QR em /qr');
124
- return;
125
- }
126
- const delay = [408, 428, 440, 515].includes(reason) ? 45000 : 15000;
127
- console.log(`Reconectando em ${delay/1000}s... (código: ${reason})`);
128
- setTimeout(connect, delay);
129
- }
130
- });
131
-
132
- sock.ev.on('messages.upsert', async m => {
133
- try {
134
- const msg = m.messages[0];
135
- if (!msg.message || msg.key.fromMe) return;
136
- if (msg.messageStubType || msg.message.protocolMessage) return;
137
- if (msg.messageTimestamp * 1000 < lastProcessedTime - 10000) return;
138
-
139
- const from = msg.key.remoteJid;
140
- const isGroup = from.endsWith('@g.us');
141
- const participant = msg.key.participant || from;
142
- const numero = participant.replace(/@s\.whatsapp\.net|@lid/g, '');
143
- const nome = msg.pushName?.trim() || numero;
144
- const text = msg.message.conversation || msg.message.extendedTextMessage?.text || '';
145
- if (!text.trim()) return;
146
-
147
- console.log(`\n[MENSAGEM] ${isGroup ? 'GRUPO' : 'PV'} | ${nome} (${numero}): ${text}`);
148
-
149
- if (!(await shouldActivate(msg, isGroup))) return;
150
-
151
- await sock.sendPresenceUpdate('composing', from);
152
- const start = Date.now();
153
-
154
- try {
155
- const res = await axios.post(AKIRA_API_URL, { usuario: nome, mensagem: text, numero }, { timeout: 30000 });
156
- let resposta = res.data.resposta || "Não entendi.";
157
- if (resposta.length > 4000) resposta = resposta.slice(0, 3990) + '...';
158
- const typing = Math.min(Math.max(resposta.length * 50, 1000), 5000);
159
- if (Date.now() - start < typing) await delay(typing - (Date.now() - start));
160
-
161
- await sock.sendPresenceUpdate('paused', from);
162
- try { await sock.presenceSubscribe(from); await sock.sendPresenceUpdate('available', from); await delay(1000); } catch {}
163
-
164
- let attempts = 0;
165
- while (attempts < 3) {
166
- try { await sock.sendMessage(from, { text: resposta }, { quoted: msg }); break; }
167
- catch (err) { attempts++; console.error(`Tentativa ${attempts} falhou: ${err.message}`); await delay(1500 * attempts); }
168
- }
169
-
170
- if (attempts >= 3) await sock.sendMessage(from, { text: "Erro ao responder. Tente novamente." }, { quoted: msg });
171
-
172
- } catch (err) {
173
- console.error('Erro na API:', err.message);
174
- try { await sock.sendMessage(from, { text: "Erro interno. Tente novamente." }, { quoted: msg }); } catch {}
175
- }
176
-
177
- } catch (err) {
178
- console.error('Erro geral:', err.message);
179
- }
180
- });
181
-
182
- } catch (err) {
183
- isConnecting = false;
184
- console.error('Erro crítico:', err.message);
185
- setTimeout(connect, 20000);
186
- }
187
  }
188
 
189
- // --- Define quando o bot deve ativar ---
190
- async function shouldActivate(msg, isGroup) {
 
 
191
  const context = msg.message?.extendedTextMessage?.contextInfo;
192
- const text = (msg.message?.conversation || msg.message?.extendedTextMessage?.text || '').toLowerCase();
193
-
194
- if (context?.quotedMessage && context.stanzaId) {
195
- const quotedJid = context.participant || context.quotedMessage?.key?.participant;
196
- if (quotedJid && normalizeJid(quotedJid) === normalizeJid(BOT_REAL_JID)) return true;
 
 
 
 
197
  }
198
 
 
199
  if (isGroup) {
200
  const mentions = context?.mentionedJid || [];
201
- if (text.includes('akira') || mentions.some(j => normalizeJid(j) === normalizeJid(BOT_REAL_JID))) return true;
 
 
 
 
 
 
202
  }
203
 
 
 
204
  return false;
205
  }
206
 
207
- // --- Inicia conexão ---
 
 
 
 
 
 
 
208
  connect();
 
1
+ // ===============================================================
2
+ // AKIRA BOT — JID/LID unify + Session fix + Reply PV/Group logic
3
+ // ===============================================================
4
+
5
  const {
6
  default: makeWASocket,
7
  useMultiFileAuthState,
8
  fetchLatestBaileysVersion,
9
  Browsers,
10
+ delay
 
11
  } = require('@whiskeysockets/baileys');
 
12
  const pino = require('pino');
13
  const axios = require('axios');
14
  const express = require('express');
15
+ const qrcode = require('qrcode-terminal');
16
 
17
+ const logger = pino({ level: 'info' });
18
+ const AKIRA_API_URL = 'https://akra35567-akira.hf.space/api/akira';
19
  const PORT = process.env.PORT || 3000;
 
 
20
 
21
  let sock;
22
+ let BOT_JID = null;
23
  let lastProcessedTime = 0;
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ // ===============================================================
26
+ // 🔧 UTILITÁRIOS
27
+ // ===============================================================
28
+
29
+ function extractNumber(input = '') {
30
+ if (!input) return 'desconhecido';
31
+ const clean = input.toString();
32
+ const match = clean.match(/2449\d{8}/);
33
+ if (match) return match[0];
34
+ const local = clean.match(/9\d{8}/);
35
+ if (local) return `244${local[0]}`;
36
+ return clean.replace(/\D/g, '').slice(-12);
37
+ }
 
 
 
 
 
 
 
 
38
 
39
+ function normalizeJid(jid = '') {
 
40
  if (!jid) return null;
41
+ jid = jid.toString().trim();
42
+
43
+ // remove lixo e sufixos
44
+ jid = jid.replace(/[:@].*/g, '');
45
+ if (jid.startsWith('37') || jid.startsWith('202') || jid.length < 9)
46
+ return BOT_JID || '244952786417@s.whatsapp.net';
47
+ if (!jid.startsWith('244') && /^9\d{8}$/.test(jid))
48
+ jid = '244' + jid;
49
+ return `${jid}@s.whatsapp.net`;
50
  }
51
 
52
+ function isBotJid(jid) {
53
+ const norm = normalizeJid(jid);
54
+ return norm === normalizeJid(BOT_JID);
 
 
 
 
 
 
55
  }
56
 
57
+ // ===============================================================
58
+ // ⚙️ CONEXÃO
59
+ // ===============================================================
60
  async function connect() {
61
+ const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys');
62
+ const { version } = await fetchLatestBaileysVersion();
63
+
64
+ sock = makeWASocket({
65
+ version,
66
+ auth: state,
67
+ logger,
68
+ browser: Browsers.macOS('Desktop'),
69
+ markOnlineOnConnect: true,
70
+ syncFullHistory: false,
71
+ connectTimeoutMs: 60000
72
+ });
73
+
74
+ sock.ev.on('creds.update', saveCreds);
75
+
76
+ sock.ev.on('connection.update', async (update) => {
77
+ const { connection, qr } = update;
78
+
79
+ if (qr) {
80
+ qrcode.generate(qr, { small: true });
81
+ console.log('\n📱 ESCANEIE O QR PARA CONECTAR\n');
82
+ }
83
+
84
+ if (connection === 'open') {
85
+ BOT_JID = normalizeJid(sock.user.id);
86
+ console.log('✅ AKIRA BOT ONLINE!');
87
+ console.log('botJid detectado:', BOT_JID);
88
+ lastProcessedTime = Date.now();
89
+ }
90
+
91
+ if (connection === 'close') {
92
+ console.log('⚠️ Conexão perdida. Tentando reconectar...');
93
+ setTimeout(connect, 5000);
94
+ }
95
+ });
96
+
97
+ // ===============================================================
98
+ // 💬 MENSAGENS
99
+ // ===============================================================
100
+ sock.ev.on('messages.upsert', async (m) => {
101
+ const msg = m.messages[0];
102
+ if (!msg.message || msg.key.fromMe) return;
103
+
104
+ const from = msg.key.remoteJid;
105
+ const isGroup = from.endsWith('@g.us');
106
+ if (msg.messageTimestamp && msg.messageTimestamp * 1000 < lastProcessedTime - 10000) return;
107
+
108
+ // Extração inteligente do número real
109
+ let sender = msg.key.participant || msg.participant || msg.key.remoteJid;
110
+ const pn = msg.message?.participantPn || msg.message?.senderPn;
111
+ const lid = msg.key.senderLid || msg.participantLid || msg.message?.senderLid;
112
+ const possible = pn || lid || sender;
113
+ const senderNumber = extractNumber(possible);
114
+ const nome = msg.pushName || senderNumber;
115
+
116
+ const text =
117
+ msg.message.conversation ||
118
+ msg.message.extendedTextMessage?.text ||
119
+ msg.message?.imageMessage?.caption ||
120
+ msg.message?.videoMessage?.caption ||
121
+ '';
122
+
123
+ if (!text.trim()) return;
124
+ console.log(`\n[MENSAGEM] ${isGroup ? 'GRUPO' : 'PV'} | ${nome} (${senderNumber}): ${text}`);
125
+
126
+ const ativar = await shouldActivate(msg, isGroup, text);
127
+ if (!ativar) {
128
+ console.log('[IGNORADO] Não ativado para responder (não reply ou não menção).');
129
+ return;
130
+ }
131
+
132
+ await sock.sendPresenceUpdate('composing', from);
133
+
134
+ try {
135
+ const res = await axios.post(AKIRA_API_URL, {
136
+ usuario: nome,
137
+ mensagem: text,
138
+ numero: senderNumber
139
+ });
140
+
141
+ const resposta = res.data.resposta || '...';
142
+ console.log(`[RESPOSTA] ${resposta}`);
143
+
144
+ await delay(Math.min(resposta.length * 50, 4000));
145
+ await sock.sendPresenceUpdate('paused', from);
146
+ await sock.sendMessage(from, { text: resposta }, { quoted: msg });
147
+ } catch (err) {
148
+ console.error('⚠️ Erro na API:', err.message);
149
+ await sock.sendMessage(from, { text: 'Erro interno. 😴' }, { quoted: msg });
150
+ }
151
+ });
152
+
153
+ // ===============================================================
154
+ // 🧰 REGENERAÇÃO DE SESSÃO
155
+ // ===============================================================
156
+ sock.ev.on('message-decrypt-failed', async (msgKey) => {
157
+ console.log('⚠️ Tentando regenerar sessão perdida...');
158
+ try {
159
+ await sock.sendRetryRequest(msgKey.key);
160
+ } catch (e) {
161
+ console.log('❌ Falha ao regenerar sessão:', e.message);
162
+ }
163
+ });
 
 
 
 
 
 
 
164
  }
165
 
166
+ // ===============================================================
167
+ // 🎯 ATIVAÇÃO (reply / menção / PV)
168
+ // ===============================================================
169
+ async function shouldActivate(msg, isGroup, text) {
170
  const context = msg.message?.extendedTextMessage?.contextInfo;
171
+ const lowered = text.toLowerCase();
172
+
173
+ // Reply ao bot
174
+ if (context?.participant) {
175
+ const quoted = normalizeJid(context.participant);
176
+ if (isBotJid(quoted)) {
177
+ console.log(`[ATIVAÇÃO] Reply ao bot detectado (${BOT_JID})`);
178
+ return true;
179
+ }
180
  }
181
 
182
+ // Menção direta no grupo
183
  if (isGroup) {
184
  const mentions = context?.mentionedJid || [];
185
+ const mentionMatch = mentions.some(
186
+ j => isBotJid(j) || j.includes(BOT_JID.split('@')[0])
187
+ );
188
+ if (lowered.includes('akira') || mentionMatch) {
189
+ console.log('[ATIVAÇÃO] Menção direta a Akira detectada.');
190
+ return true;
191
+ }
192
  }
193
 
194
+ // PV → sempre responde
195
+ if (!isGroup) return true;
196
  return false;
197
  }
198
 
199
+ // ===============================================================
200
+ // 🌐 HEALTH CHECK
201
+ // ===============================================================
202
+ const app = express();
203
+ const server = app.listen(PORT, '0.0.0.0', () => {
204
+ console.log(`Health check na porta ${server.address().port}`);
205
+ });
206
+ app.get('/', (req, res) => res.send('AKIRA BOT ONLINE ✅'));
207
  connect();