Spaces:
Build error
Build error
| // =============================================================== | |
| // AKIRA BOT — JID/LID unify + Session fix + Reply PV/Group logic | |
| // =============================================================== | |
| const { | |
| default: makeWASocket, | |
| useMultiFileAuthState, | |
| fetchLatestBaileysVersion, | |
| Browsers, | |
| delay | |
| } = require('@whiskeysockets/baileys'); | |
| const pino = require('pino'); | |
| const axios = require('axios'); | |
| const express = require('express'); | |
| const qrcode = require('qrcode-terminal'); | |
| const logger = pino({ level: 'info' }); | |
| const AKIRA_API_URL = 'https://akra35567-akira.hf.space/api/akira'; | |
| const PORT = process.env.PORT || 3000; | |
| let sock; | |
| let BOT_JID = null; | |
| let lastProcessedTime = 0; | |
| // =============================================================== | |
| // 🔧 UTILITÁRIOS | |
| // =============================================================== | |
| function extractNumber(input = '') { | |
| if (!input) return 'desconhecido'; | |
| const clean = input.toString(); | |
| const match = clean.match(/2449\d{8}/); | |
| if (match) return match[0]; | |
| const local = clean.match(/9\d{8}/); | |
| if (local) return `244${local[0]}`; | |
| return clean.replace(/\D/g, '').slice(-12); | |
| } | |
| function normalizeJid(jid = '') { | |
| if (!jid) return null; | |
| jid = jid.toString().trim(); | |
| // remove lixo e sufixos | |
| jid = jid.replace(/[:@].*/g, ''); | |
| if (jid.startsWith('37') || jid.startsWith('202') || jid.length < 9) | |
| return BOT_JID || '244952786417@s.whatsapp.net'; | |
| if (!jid.startsWith('244') && /^9\d{8}$/.test(jid)) | |
| jid = '244' + jid; | |
| return `${jid}@s.whatsapp.net`; | |
| } | |
| function isBotJid(jid) { | |
| const norm = normalizeJid(jid); | |
| return norm === normalizeJid(BOT_JID); | |
| } | |
| // =============================================================== | |
| // ⚙️ CONEXÃO | |
| // =============================================================== | |
| async function connect() { | |
| const { state, saveCreds } = await useMultiFileAuthState('auth_info_baileys'); | |
| const { version } = await fetchLatestBaileysVersion(); | |
| sock = makeWASocket({ | |
| version, | |
| auth: state, | |
| logger, | |
| browser: Browsers.macOS('Desktop'), | |
| markOnlineOnConnect: true, | |
| syncFullHistory: false, | |
| connectTimeoutMs: 60000 | |
| }); | |
| sock.ev.on('creds.update', saveCreds); | |
| sock.ev.on('connection.update', async (update) => { | |
| const { connection, qr } = update; | |
| if (qr) { | |
| qrcode.generate(qr, { small: true }); | |
| console.log('\n📱 ESCANEIE O QR PARA CONECTAR\n'); | |
| } | |
| if (connection === 'open') { | |
| BOT_JID = normalizeJid(sock.user.id); | |
| console.log('✅ AKIRA BOT ONLINE!'); | |
| console.log('botJid detectado:', BOT_JID); | |
| lastProcessedTime = Date.now(); | |
| } | |
| if (connection === 'close') { | |
| console.log('⚠️ Conexão perdida. Tentando reconectar...'); | |
| setTimeout(connect, 5000); | |
| } | |
| }); | |
| // =============================================================== | |
| // 💬 MENSAGENS | |
| // =============================================================== | |
| sock.ev.on('messages.upsert', async (m) => { | |
| const msg = m.messages[0]; | |
| if (!msg.message || msg.key.fromMe) return; | |
| const from = msg.key.remoteJid; | |
| const isGroup = from.endsWith('@g.us'); | |
| if (msg.messageTimestamp && msg.messageTimestamp * 1000 < lastProcessedTime - 10000) return; | |
| // Extração inteligente do número real | |
| let sender = msg.key.participant || msg.participant || msg.key.remoteJid; | |
| const pn = msg.message?.participantPn || msg.message?.senderPn; | |
| const lid = msg.key.senderLid || msg.participantLid || msg.message?.senderLid; | |
| const possible = pn || lid || sender; | |
| const senderNumber = extractNumber(possible); | |
| const nome = msg.pushName || senderNumber; | |
| const text = | |
| msg.message.conversation || | |
| msg.message.extendedTextMessage?.text || | |
| msg.message?.imageMessage?.caption || | |
| msg.message?.videoMessage?.caption || | |
| ''; | |
| if (!text.trim()) return; | |
| console.log(`\n[MENSAGEM] ${isGroup ? 'GRUPO' : 'PV'} | ${nome} (${senderNumber}): ${text}`); | |
| const ativar = await shouldActivate(msg, isGroup, text); | |
| if (!ativar) { | |
| console.log('[IGNORADO] Não ativado para responder (não reply ou não menção).'); | |
| return; | |
| } | |
| await sock.sendPresenceUpdate('composing', from); | |
| try { | |
| const res = await axios.post(AKIRA_API_URL, { | |
| usuario: nome, | |
| mensagem: text, | |
| numero: senderNumber | |
| }); | |
| const resposta = res.data.resposta || '...'; | |
| console.log(`[RESPOSTA] ${resposta}`); | |
| await delay(Math.min(resposta.length * 50, 4000)); | |
| await sock.sendPresenceUpdate('paused', from); | |
| await sock.sendMessage(from, { text: resposta }, { quoted: msg }); | |
| } catch (err) { | |
| console.error('⚠️ Erro na API:', err.message); | |
| await sock.sendMessage(from, { text: 'Erro interno. 😴' }, { quoted: msg }); | |
| } | |
| }); | |
| // =============================================================== | |
| // 🧰 REGENERAÇÃO DE SESSÃO | |
| // =============================================================== | |
| sock.ev.on('message-decrypt-failed', async (msgKey) => { | |
| console.log('⚠️ Tentando regenerar sessão perdida...'); | |
| try { | |
| await sock.sendRetryRequest(msgKey.key); | |
| } catch (e) { | |
| console.log('❌ Falha ao regenerar sessão:', e.message); | |
| } | |
| }); | |
| } | |
| // =============================================================== | |
| // 🎯 ATIVAÇÃO (reply / menção / PV) | |
| // =============================================================== | |
| async function shouldActivate(msg, isGroup, text) { | |
| const context = msg.message?.extendedTextMessage?.contextInfo; | |
| const lowered = text.toLowerCase(); | |
| // Reply ao bot | |
| if (context?.participant) { | |
| const quoted = normalizeJid(context.participant); | |
| if (isBotJid(quoted)) { | |
| console.log(`[ATIVAÇÃO] Reply ao bot detectado (${BOT_JID})`); | |
| return true; | |
| } | |
| } | |
| // Menção direta no grupo | |
| if (isGroup) { | |
| const mentions = context?.mentionedJid || []; | |
| const mentionMatch = mentions.some( | |
| j => isBotJid(j) || j.includes(BOT_JID.split('@')[0]) | |
| ); | |
| if (lowered.includes('akira') || mentionMatch) { | |
| console.log('[ATIVAÇÃO] Menção direta a Akira detectada.'); | |
| return true; | |
| } | |
| } | |
| // PV → sempre responde | |
| if (!isGroup) return true; | |
| return false; | |
| } | |
| // =============================================================== | |
| // 🌐 HEALTH CHECK | |
| // =============================================================== | |
| const app = express(); | |
| const server = app.listen(PORT, '0.0.0.0', () => { | |
| console.log(`Health check na porta ${server.address().port}`); | |
| }); | |
| app.get('/', (req, res) => res.send('AKIRA BOT ONLINE ✅')); | |
| connect(); | |