// command-handler.js import fs from "fs"; import path from "path"; import { pathToFileURL } from "url"; /** * Load semua command dari folder (rekursif) * Struktur file command: * export default { * command: ["ping", "p"], * description: "...", * run: async (m, { conn }) => {} * } */ export async function loadCommands(dir) { const commands = new Map(); async function loadDir(folder) { const files = fs.readdirSync(folder); for (const file of files) { const full = path.join(folder, file); const stat = fs.statSync(full); if (stat.isDirectory()) { // masuk folder lagi (rekursif) await loadDir(full); continue; } if (!file.endsWith(".js")) continue; try { const fileUrl = pathToFileURL(full).href; const cmdModule = (await import(fileUrl)).default; if (!cmdModule || !cmdModule.command) continue; const aliases = Array.isArray(cmdModule.command) ? cmdModule.command : [cmdModule.command]; for (const alias of aliases) { const key = String(alias).toLowerCase(); commands.set(key, cmdModule); } } catch (err) { console.error(`❌ Gagal load command: ${full}`, err); } } } await loadDir(dir); return commands; } /** * Helper untuk ambil teks utama dari message */ function getMessageText(msg) { return ( msg.message?.conversation || msg.message?.extendedTextMessage?.text || msg.message?.imageMessage?.caption || msg.message?.videoMessage?.caption || msg.message?.buttonsResponseMessage?.selectedButtonId || msg.message?.listResponseMessage?.singleSelectReply?.selectedRowId || "" ); } /** * Handler utama command * - parsing prefix, command, args * - bikin wrapper "m" dengan m.reply, m.send, dll * - jalanin plugin.run(...) */ export async function handleCommand(sock, msg, commands) { try { const from = msg.key.remoteJid; const isGroup = from.endsWith("@g.us"); const sender = msg.key.participant || msg.key.remoteJid; const body = getMessageText(msg); if (!body) return; // global.prefix bisa string / regex const prefixMatch = typeof global.prefix === "string" ? body.startsWith(global.prefix) ? [global.prefix] : null : body.match(global.prefix); if (!prefixMatch) return; // bukan command const prefix = prefixMatch[0]; const [rawCmd, ...args] = body .slice(prefix.length) .trim() .split(/\s+/); if (!rawCmd) return; const command = rawCmd.toLowerCase(); const plugin = commands.get(command); if (!plugin) return; // Data tambahan const quoted = msg.message?.extendedTextMessage?.contextInfo?.quotedMessage || null; const mentionedJid = msg.message?.extendedTextMessage?.contextInfo?.mentionedJid || []; // ========================================= // Wrapper "m" untuk dikirim ke plugin // ========================================= const m = { ...msg, from, sender, isGroup, body, text: body, args, prefix, command, quoted, mentionedJid, /** * Reply text sederhana * @param {string} text * @param {object} options - opsi tambahan sendMessage */ reply: async (text, options = {}) => { return sock.sendMessage( from, { text: String(text) }, { quoted: msg, ...options } ); }, /** * Kirim pesan custom / media * contoh: * m.send({ image: buffer, caption: "hai" }) */ send: async (content, options = {}) => { return sock.sendMessage(from, content, { quoted: msg, ...options }); }, /** * React ke pesan * @param {string} emoji */ react: async (emoji) => { try { return sock.sendMessage(from, { react: { text: emoji, key: msg.key }, }); } catch (err) { console.error("❌ Gagal react:", err); } }, /** * Reply error dengan format rapi */ error: async (err) => { console.error("❌ Plugin error:", err); const message = typeof err === "string" ? err : err?.message || "Terjadi kesalahan"; return m.reply("⚠️ Error: " + message); }, }; // ========================================= // Jalankan plugin await plugin.run(m, { conn: sock, sock }); } catch (e) { console.error("❌ Error handleCommand:", e); } }