const ConfigManager = require('./ConfigManager');
const fs = require('fs');
const path = require('path');
/**
* ═══════════════════════════════════════════════════════════════════════
* COMMAND HANDLER - AKIRA BOT V21
* ═══════════════════════════════════════════════════════════════════════
* ✅ Sistema completo de comandos com permissões por tier
* ✅ Rate limiting inteligente por usuário
* ✅ Menus profissionais e formatados
* ✅ Funcionalidades enterprise-grade
* ═══════════════════════════════════════════════════════════════════════
*/
// Sistema de rate limiting por usuário (premium features)
const premiumFeatureUsage = new Map(); // { userId: { lastUse: timestamp, count: number, resetDate: Date } }
class CommandHandler {
constructor(botCore) {
this.bot = botCore;
this.config = ConfigManager.getInstance();
}
/**
* Verifica se usuário tem acesso a feature premium (1x a cada 3 meses)
*/
canUsePremiumFeature(userId) {
const now = new Date();
const usage = premiumFeatureUsage.get(userId) || { lastUse: 0, count: 0, resetDate: now };
// Reset a cada 3 meses (90 dias)
const threeMonthsAgo = new Date(now.getTime() - (90 * 24 * 60 * 60 * 1000));
if (usage.resetDate < threeMonthsAgo) {
usage.count = 0;
usage.resetDate = now;
}
const canUse = usage.count === 0;
if (canUse) {
usage.count = 1;
usage.lastUse = now.getTime();
}
premiumFeatureUsage.set(userId, usage);
return canUse;
}
/**
* Formata linha divisória para menus
*/
getDivider() {
return '═'.repeat(54);
}
/**
* Formata cabeçalho de menu
*/
getMenuHeader(emoji, title) {
return `╔${'═'.repeat(52)}╗
║ ${emoji} ${title.padEnd(48)} ║
╚${'═'.repeat(52)}╝`;
}
/**
* Formata seção de menu
*/
getMenuSection(emoji, section) {
return `\n${this.getDivider()}
${emoji} ${section}
${this.getDivider()}`;
}
async handle(m, meta) {
// meta: { nome, numeroReal, texto, replyInfo, ehGrupo }
try {
const { nome, numeroReal, texto, replyInfo, ehGrupo } = meta;
const mp = this.bot.messageProcessor;
const parsed = mp.parseCommand(texto);
if (!parsed) return false;
const senderId = numeroReal;
const sock = this.bot.sock;
// common helpers
const isOwner = () => {
try { return this.config.isDono(senderId, nome); } catch { return false; }
};
const cmd = parsed.comando;
const args = parsed.args;
const full = parsed.textoCompleto;
// Rate-limit via messageProcessor
if (!mp.checkRateLimit(senderId)) {
await sock.sendMessage(m.key.remoteJid, { text: '⏰ Você está usando comandos muito rápido. Aguarde.' }, { quoted: m });
return true;
}
// Permission check wrapper for owner-only actions
const ownerOnly = async (fn) => {
if (!isOwner()) {
await sock.sendMessage(m.key.remoteJid, { text: '🚫 Comando restrito ao dono.' }, { quoted: m });
return true;
}
return await fn();
};
switch (cmd) {
case 'ping':
await sock.sendMessage(m.key.remoteJid, { text: `🏓 Pong! Uptime: ${Math.floor(process.uptime())}s` }, { quoted: m });
return true;
case 'perfil':
case 'profile':
case 'info':
try {
const uid = m.key.participant || m.key.remoteJid;
const nomeReg = this.bot.apiClient.getRegisterName ? this.bot.apiClient.getRegisterName(uid) : 'Não registrado';
const level = this.bot.levelSystem.getGroupRecord(m.key.remoteJid, uid, true).level || 0;
const xp = this.bot.levelSystem.getGroupRecord(m.key.remoteJid, uid, true).xp || 0;
const txt = `👤 *Perfil:* ${nomeReg}\n🎮 Nível: ${level}\n⭐ XP: ${xp}`;
await sock.sendMessage(m.key.remoteJid, { text: txt }, { quoted: m });
} catch (e) { }
return true;
case 'registrar':
case 'register':
case 'reg':
try {
// local simple registry using database/datauser/registered.json
const dbFolder = path.join(this.config.DATABASE_FOLDER, 'datauser');
if (!fs.existsSync(dbFolder)) fs.mkdirSync(dbFolder, { recursive: true });
const regPath = path.join(dbFolder, 'registered.json');
if (!fs.existsSync(regPath)) fs.writeFileSync(regPath, JSON.stringify([], null, 2));
if (!full.includes('|')) {
await sock.sendMessage(m.key.remoteJid, { text: 'Uso: #registrar Nome|Idade' }, { quoted: m });
return true;
}
const [nomeUser, idadeStr] = full.split('|').map(s => s.trim());
const idade = parseInt(idadeStr,10);
if (!nomeUser || isNaN(idade)) { await sock.sendMessage(m.key.remoteJid, { text: 'Formato inválido.' }, { quoted: m }); return true; }
const registered = JSON.parse(fs.readFileSync(regPath, 'utf8') || '[]');
const senderJid = m.key.participant || m.key.remoteJid;
if (registered.find(u=>u.id===senderJid)) { await sock.sendMessage(m.key.remoteJid, { text: '✅ Você já está registrado!' }, { quoted: m }); return true; }
const serial = (Date.now().toString(36) + Math.random().toString(36).slice(2,10)).toUpperCase();
const time = new Date().toISOString();
registered.push({ id: senderJid, name: nomeUser, age: idade, time, serial, registeredAt: Date.now() });
fs.writeFileSync(regPath, JSON.stringify(registered, null, 2));
// ensure leveling record
this.bot.levelSystem.getGroupRecord(m.key.remoteJid, senderJid, true);
await sock.sendMessage(m.key.remoteJid, { text: '✅ Registrado com sucesso!' }, { quoted: m });
} catch (e) { this.bot.logger && this.bot.logger.error('registrar error', e); }
return true;
case 'level':
case 'nivel':
case 'rank':
try {
const gid = m.key.remoteJid;
if (!String(gid).endsWith('@g.us')) { await sock.sendMessage(gid, { text: '📵 Level funciona apenas em grupos.' }, { quoted: m }); return true; }
const sub = (args[0]||'').toLowerCase();
if (['on','off','status'].includes(sub)) {
return await ownerOnly(async () => {
const settingsPath = this.config.JSON_PATHS?.leveling || null;
if (settingsPath) {
const toggles = this.bot.apiClient.loadJSON ? this.bot.apiClient.loadJSON(settingsPath) : {};
if (sub === 'on') { toggles[gid]=true; this.bot.apiClient.saveJSON && this.bot.apiClient.saveJSON(settingsPath, toggles); await sock.sendMessage(gid,{text:'✅ Level ativado'},{quoted:m}); }
else if (sub === 'off') { delete toggles[gid]; this.bot.apiClient.saveJSON && this.bot.apiClient.saveJSON(settingsPath, toggles); await sock.sendMessage(gid,{text:'🚫 Level desativado'},{quoted:m}); }
else { await sock.sendMessage(gid,{text:`ℹ️ Status: ${toggles[gid] ? 'Ativo' : 'Inativo'}`},{quoted:m}); }
} else {
await sock.sendMessage(gid,{text:'⚠️ Configuração de leveling não encontrada'},{quoted:m});
}
return true;
});
}
// Mostrar level do usuário
const uid = m.key.participant || m.key.remoteJid;
const rec = this.bot.levelSystem.getGroupRecord(gid, uid, true);
const req = this.bot.levelSystem.requiredXp(rec.level);
const pct = req === Infinity ? 100 : Math.min(100, Math.floor((rec.xp/req)*100));
const msg = `🎉 LEVEL\n👤 @${uid.split('@')[0]}\n📊 Nível: ${rec.level}\n⭐ XP: ${rec.xp}/${req}\nProgresso: ${pct}%`;
await sock.sendMessage(gid, { text: msg, contextInfo: { mentionedJid: [uid] } }, { quoted: m });
} catch (e) { }
return true;
case 'antilink':
try {
return await ownerOnly(async () => {
const sub2 = (args[0]||'').toLowerCase();
const gid = m.key.remoteJid;
if (sub2 === 'on') { this.bot.moderationSystem.toggleAntiLink(gid, true); await sock.sendMessage(gid,{text:'🔒 ANTI-LINK ATIVADO'},{quoted:m}); }
else if (sub2 === 'off') { this.bot.moderationSystem.toggleAntiLink(gid, false); await sock.sendMessage(gid,{text:'🔓 ANTI-LINK DESATIVADO'},{quoted:m}); }
else { await sock.sendMessage(gid,{text:`Status: ${this.bot.moderationSystem.isAntiLinkActive(gid) ? 'Ativo' : 'Inativo'}`},{quoted:m}); }
return true;
});
} catch (e) {}
return true;
case 'mute':
try {
return await ownerOnly(async () => {
const target = (m.message?.extendedTextMessage?.contextInfo?.mentionedJid||[])[0] || replyInfo?.participantJidCitado;
if (!target) { await sock.sendMessage(m.key.remoteJid,{text:'Marque ou responda o usuário'},{quoted:m}); return true; }
const res = this.bot.moderationSystem.muteUser(m.key.remoteJid, target, 5);
await sock.sendMessage(m.key.remoteJid,{text:`🔇 Mutado por ${res.minutes} minutos`},{quoted:m});
return true;
});
} catch (e) {}
return true;
case 'desmute':
try { return await ownerOnly(async ()=>{ const target = (m.message?.extendedTextMessage?.contextInfo?.mentionedJid||[])[0] || replyInfo?.participantJidCitado; if (!target) { await sock.sendMessage(m.key.remoteJid,{text:'Marque ou responda o usuário'},{quoted:m}); return true;} this.bot.moderationSystem.unmuteUser(m.key.remoteJid,target); await sock.sendMessage(m.key.remoteJid,{text:'🔊 Usuário desmutado'},{quoted:m}); return true; }); } catch(e){}
return true;
case 'sticker':
case 's':
case 'fig':
try {
// delegate to mediaProcessor
const quoted = m.message?.extendedTextMessage?.contextInfo?.quotedMessage;
const imageMsg = m.message?.imageMessage || quoted?.imageMessage;
const stickerMsg = quoted?.stickerMessage;
if (stickerMsg) {
const stickerBuf = await this.bot.mediaProcessor.downloadMedia(stickerMsg, 'sticker');
if (stickerBuf) {
await sock.sendMessage(m.key.remoteJid, { sticker: stickerBuf }, { quoted: m });
} else {
await sock.sendMessage(m.key.remoteJid, { text: '❌ Erro ao baixar sticker.' }, { quoted: m });
}
return true;
}
if (imageMsg) {
const buf = await this.bot.mediaProcessor.downloadMedia(imageMsg, 'image');
const res = await this.bot.mediaProcessor.createStickerFromImage(buf, { packName: this.config.STICKER_PACK || 'Akira Pack', author: nome });
if (res && res.sucesso && res.buffer) {
await sock.sendMessage(m.key.remoteJid, { sticker: res.buffer }, { quoted: m });
} else {
await sock.sendMessage(m.key.remoteJid, { text: '❌ Erro ao criar sticker' }, { quoted: m });
}
return true;
}
await sock.sendMessage(m.key.remoteJid, { text: 'Envie/Responda uma imagem ou sticker' }, { quoted: m });
} catch (e) { }
return true;
case 'play':
try {
if (!full) { await sock.sendMessage(m.key.remoteJid, { text: 'Uso: #play ' }, { quoted: m }); return true; }
await sock.sendMessage(m.key.remoteJid, { text: '⏳ Processando música...' }, { quoted: m });
const res = await this.bot.mediaProcessor.downloadYouTubeAudio(full);
if (res.error) { await sock.sendMessage(m.key.remoteJid, { text: `❌ ${res.error}` }, { quoted: m }); return true; }
await sock.sendMessage(m.key.remoteJid, { audio: res.buffer, mimetype: 'audio/mpeg', ptt: false, fileName: `${res.title || 'music'}.mp3` }, { quoted: m });
} catch (e) {}
return true;
// ═══════════════════════════════════════════════════════════════
// COMANDO: MENU / HELP / COMANDOS
// ═══════════════════════════════════════════════════════════════
case 'help':
case 'menu':
case 'comandos':
case 'ajuda':
try {
const menuText = `╔════════════════════════════════════════════════════╗
║ 🤖 AKIRA BOT V21 - MENU COMPLETO 🤖 ║
╚════════════════════════════════════════════════════╝
📱 *PREFIXO:* \`${this.config.PREFIXO}\`
═══════════════════════════════════════════════════════
🎨 MÍDIA & CRIATIVIDADE (Todos)
═══════════════════════════════════════════════════════
\`#sticker\` - Criar sticker de imagem
\`#s\` ou \`#fig\` - Aliases para #sticker
\`#play \` - Baixar música do YouTube
\`#ping\` - Testar latência do bot
═══════════════════════════════════════════════════════
🎤 ÁUDIO INTELIGENTE (Novo)
═══════════════════════════════════════════════════════
• Respondo áudio automaticamente em PV
• Em grupos: mencione "Akira" ou responda ao áudio
• Transcrição interna (NUNCA mostra no chat)
• Resposto em áudio automático
═══════════════════════════════════════════════════════
👑 MODERAÇÃO (Apenas Isaac Quarenta)
═══════════════════════════════════════════════════════
\`#antilink on\` - Ativar anti-link
\`#antilink off\` - Desativar anti-link
\`#antilink status\` - Ver status
\`#mute @usuário\` - Mutar por 5 min (ou reply)
\`#desmute @usuário\` - Desmutar (ou reply)
\`#level on\` - Ativar sistema de níveis
\`#level off\` - Desativar sistema de níveis
\`#level status\` - Ver status do level
═══════════════════════════════════════════════════════
🎮 UTILIDADES (Todos)
═══════════════════════════════════════════════════════
\`#perfil\` ou \`#info\` - Ver seu perfil
\`#registrar Nome|Idade\` - Registrar no bot
\`#level\` - Ver seu nível e XP
═══════════════════════════════════════════════════════
💬 CONVERSA NORMAL
═══════════════════════════════════════════════════════
✅ Mencione "Akira" em grupos
✅ Ou responda minhas mensagens para conversar
✅ IA sempre disponível em PV
═══════════════════════════════════════════════════════
⚠️ INFORMAÇÕES IMPORTANTES
═══════════════════════════════════════════════════════
🔐 Comandos de grupo: Apenas Isaac Quarenta
📊 Sistema de XP automático ao enviar mensagens
🏆 Suba de nível conversando (automaticamente)
🛡️ Anti-spam e proteção contra abuso
🎤 STT: Deepgram (200h/mês gratuito)
🔊 TTS: Google Text-to-Speech
═══════════════════════════════════════════════════════
💰 *Quer apoiar o projeto?*
Digite: \`#donate\` ou \`#doar\`
═══════════════════════════════════════════════════════
*Desenvolvido com ❤️ por Isaac Quarenta*`;
await sock.sendMessage(m.key.remoteJid, { text: menuText }, { quoted: m });
} catch (e) {
this.bot.logger?.error('Erro no comando menu:', e);
}
return true;
// ═══════════════════════════════════════════════════════════════
// COMANDO: DONATE / APOIO
// ═══════════════════════════════════════════════════════════════
case 'donate':
case 'doar':
case 'apoia':
case 'doacao':
try {
const donateText = `╔════════════════════════════════════════════════════╗
║ ❤️ APOIE O PROJETO AKIRA BOT ❤️ ║
╚════════════════════════════════════════════════════╝
🙏 *Você gosta do Akira?*
Seu apoio nos ajuda a manter:
✅ Bot online 24/7
✅ Novas funcionalidades
✅ Sem publicidades
✅ Gratuito para todos
═══════════════════════════════════════════════════════
💰 FORMAS DE APOIAR
═══════════════════════════════════════════════════════
🔑 *PIX (IMEDIATO):*
E-mail: akira.bot.dev@gmail.com
Chave: akira.bot.dev@gmail.com
☕ *COMPRE UM CAFÉ (Ko-fi):*
https://ko-fi.com/isaacquarenta
💳 *PAYPAL:*
https://paypal.me/isaacquarenta
🎁 *QUALQUER VALOR AJUDA!*
Desde R$ 5 até quanto você quiser contribuir
═══════════════════════════════════════════════════════
🙏 AGRADECIMENTOS ESPECIAIS
═══════════════════════════════════════════════════════
Todos que contribuem receberão:
✨ Meu sincero agradecimento
✨ Suporte prioritário
✨ Novas features primeiro
✨ Reconhecimento especial
✨ Status VIP no bot
═══════════════════════════════════════════════════════
📊 IMPACTO DA SUA DOAÇÃO
═══════════════════════════════════════════════════════
R$ 5 = Mantém o bot 1 dia online
R$ 20 = Semana completa
R$ 50 = Mês inteiro
R$ 100+ = Mês + desenvolvimento de features
═══════════════════════════════════════════════════════
*Desenvolvido com ❤️ por Isaac Quarenta*
_Obrigado por apoiar um projeto feito com paixão!_ 🚀`;
await sock.sendMessage(m.key.remoteJid, { text: donateText }, { quoted: m });
} catch (e) {
this.bot.logger?.error('Erro no comando donate:', e);
}
return true;
default:
return false;
}
} catch (err) {
try { await this.bot.sock.sendMessage(m.key.remoteJid, { text: '❌ Erro no comando.' }, { quoted: m }); } catch {}
return true;
}
}
}
module.exports = CommandHandler;