// code from goatstore // api: https://store.agi.bd /** * security.js — GoatBot V2 group security command * Author: Zihad * * Features (no API key needed): * - antilink : link পাঠালে warn, limit পার হলে kick * - antispam : অল্প সময়ে অনেক মেসেজ দিলে warn, limit পার হলে kick * - antijoin : নতুন কেউ add হলে auto-remove * - lockname : গ্রুপের নাম বদলালে আগের নামে ফিরিয়ে দেয় * * Usage: * security -> status দেখাবে * security on|off * security warnlimit -> কতবার warn এর পর kick (default 3) * security resetwarn @tag -> কারো warn মুছবে * security allon | alloff -> সব feature একসাথে * * Note: kick / name-lock কাজ করতে বটকে গ্রুপ admin হতে হবে। */ const FEATURES = ["antilink", "antispam", "antijoin", "lockname"]; const LINK_REGEX = /(https?:\/\/|www\.)\S+|\b[a-z0-9-]+\.(com|net|org|io|me|xyz|info|link|gg|co|app|site|online|shop)(\/\S*)?\b/i; const SPAM_WINDOW_MS = 6000; // সময়সীমা const SPAM_MAX_MSGS = 6; // ওই সময়ে সর্বোচ্চ মেসেজ // in-memory spam tracker: threadID_userID -> [timestamps] const spamMap = new Map(); function defaultConfig() { return { antilink: false, antispam: false, antijoin: false, lockname: false, lockedName: null, warnLimit: 3, warns: {} // userID -> count }; } async function getConfig(threadsData, threadID) { const saved = await threadsData.get(threadID, "data.security"); return Object.assign(defaultConfig(), saved || {}); } async function saveConfig(threadsData, threadID, cfg) { await threadsData.set(threadID, cfg, "data.security"); } function isBotAdmin(uid) { const admins = (global.GoatBot && global.GoatBot.config && global.GoatBot.config.adminBot) || []; return admins.map(String).includes(String(uid)); } async function isGroupAdmin(threadsData, threadID, uid) { const adminIDs = (await threadsData.get(threadID, "adminIDs")) || []; return adminIDs.map(String).includes(String(uid)); } async function isExempt(threadsData, threadID, uid, api) { const botID = String(api.getCurrentUserID()); if (String(uid) === botID) return true; if (isBotAdmin(uid)) return true; return isGroupAdmin(threadsData, threadID, uid); } async function addWarn({ api, message, threadsData, threadID, uid, reason, usersData }) { const cfg = await getConfig(threadsData, threadID); cfg.warns[uid] = (cfg.warns[uid] || 0) + 1; const count = cfg.warns[uid]; const limit = cfg.warnLimit || 3; let name = uid; try { name = await usersData.getName(uid); } catch (e) { /* ignore */ } if (count >= limit) { cfg.warns[uid] = 0; await saveConfig(threadsData, threadID, cfg); try { await api.removeUserFromGroup(uid, threadID); return message.reply(`🚫 ${name} কে kick করা হয়েছে।\nকারণ: ${reason} (${limit}/${limit} warn)`); } catch (e) { return message.reply(`⚠️ ${name} এর warn limit শেষ, কিন্তু kick করতে পারিনি।\nবটকে গ্রুপ admin বানাও।`); } } await saveConfig(threadsData, threadID, cfg); return message.reply(`⚠️ ${name}, সতর্কতা!\nকারণ: ${reason}\nWarn: ${count}/${limit}`); } module.exports = { config: { name: "security", aliases: ["secure", "guard"], version: "1.0.0", author: "Zihad", countDown: 3, role: 1, // গ্রুপ admin description: { en: "Group security: anti-link, anti-spam, anti-join, name lock", bn: "গ্রুপ সিকিউরিটি: অ্যান্টি-লিংক, অ্যান্টি-স্প্যাম, অ্যান্টি-জয়েন, নাম লক" }, category: "box chat", guide: { en: "{pn} → status\n" + "{pn} on|off \n" + "{pn} allon | alloff\n" + "{pn} warnlimit \n" + "{pn} resetwarn @tag" } }, onStart: async function ({ api, event, args, message, threadsData }) { const { threadID, mentions } = event; const cfg = await getConfig(threadsData, threadID); const sub = (args[0] || "").toLowerCase(); const statusText = () => { const s = v => (v ? "🟢 ON" : "🔴 OFF"); return ( "🛡️ GROUP SECURITY\n" + "━━━━━━━━━━━━━━\n" + `🔗 Anti-Link : ${s(cfg.antilink)}\n` + `💬 Anti-Spam : ${s(cfg.antispam)}\n` + `🚪 Anti-Join : ${s(cfg.antijoin)}\n` + `🔒 Lock-Name : ${s(cfg.lockname)}${cfg.lockname && cfg.lockedName ? ` (${cfg.lockedName})` : ""}\n` + `⚠️ Warn Limit: ${cfg.warnLimit}\n` + "━━━━━━━━━━━━━━\n" + "Use: security on|off " ); }; if (!sub) return message.reply(statusText()); if (sub === "on" || sub === "off") { const feature = (args[1] || "").toLowerCase(); if (!FEATURES.includes(feature)) return message.reply(`❌ Feature দাও: ${FEATURES.join(", ")}`); cfg[feature] = sub === "on"; if (feature === "lockname") { if (cfg.lockname) { const info = await threadsData.get(threadID); cfg.lockedName = info.threadName || null; } else { cfg.lockedName = null; } } await saveConfig(threadsData, threadID, cfg); return message.reply(`${cfg[feature] ? "✅" : "❎"} ${feature} এখন ${cfg[feature] ? "ON" : "OFF"}`); } if (sub === "allon" || sub === "alloff") { const on = sub === "allon"; for (const f of FEATURES) cfg[f] = on; if (on) { const info = await threadsData.get(threadID); cfg.lockedName = info.threadName || null; } else { cfg.lockedName = null; } await saveConfig(threadsData, threadID, cfg); return message.reply(on ? "✅ সব security ON করা হলো।" : "❎ সব security OFF করা হলো।"); } if (sub === "warnlimit") { const n = parseInt(args[1]); if (isNaN(n) || n < 1 || n > 20) return message.reply("❌ 1 থেকে 20 এর মধ্যে সংখ্যা দাও।"); cfg.warnLimit = n; await saveConfig(threadsData, threadID, cfg); return message.reply(`✅ Warn limit এখন ${n}`); } if (sub === "resetwarn") { const target = Object.keys(mentions || {})[0] || (event.messageReply && event.messageReply.senderID); if (!target) return message.reply("❌ কাউকে tag করো বা তার মেসেজে reply দাও।"); cfg.warns[target] = 0; await saveConfig(threadsData, threadID, cfg); return message.reply("✅ Warn মুছে ফেলা হয়েছে।"); } return message.reply(statusText()); }, // প্রতিটি মেসেজে চলবে: anti-link ও anti-spam onChat: async function ({ api, event, message, threadsData, usersData }) { const { threadID, senderID, body, isGroup } = event; if (!isGroup || !senderID) return; const cfg = await getConfig(threadsData, threadID); if (!cfg.antilink && !cfg.antispam) return; if (await isExempt(threadsData, threadID, senderID, api)) return; // ---- Anti-Link ---- if (cfg.antilink && body && LINK_REGEX.test(body)) { try { await api.unsendMessage(event.messageID); } catch (e) { /* admin না হলে fail হতে পারে */ } return addWarn({ api, message, threadsData, usersData, threadID, uid: senderID, reason: "লিংক পাঠানো নিষেধ" }); } // ---- Anti-Spam ---- if (cfg.antispam) { const key = `${threadID}_${senderID}`; const now = Date.now(); const list = (spamMap.get(key) || []).filter(t => now - t < SPAM_WINDOW_MS); list.push(now); spamMap.set(key, list); if (list.length > SPAM_MAX_MSGS) { spamMap.set(key, []); return addWarn({ api, message, threadsData, usersData, threadID, uid: senderID, reason: "স্প্যাম করা নিষেধ" }); } } }, // গ্রুপ ইভেন্ট: anti-join ও lock-name onEvent: async function ({ api, event, message, threadsData }) { const { threadID, logMessageType, logMessageData, author } = event; if (!threadID) return; const cfg = await getConfig(threadsData, threadID); // ---- Anti-Join ---- if (cfg.antijoin && logMessageType === "log:subscribe") { const botID = String(api.getCurrentUserID()); const added = (logMessageData.addedParticipants || []).map(p => String(p.userFbId)); // বট নিজে add হলে কিছু করবে না if (added.includes(botID)) return; // admin add করলে অনুমতি if (await isExempt(threadsData, threadID, author, api)) return; for (const uid of added) { try { await api.removeUserFromGroup(uid, threadID); } catch (e) { /* ignore */ } } return message.send("🚪 Anti-Join ON আছে, নতুন সদস্য remove করা হয়েছে।"); } // ---- Lock-Name ---- if (cfg.lockname && logMessageType === "log:thread-name" && cfg.lockedName) { if (String(author) === String(api.getCurrentUserID())) return; if (await isExempt(threadsData, threadID, author, api)) { // admin বদলালে নতুন নামটাই lock হবে cfg.lockedName = logMessageData.name; await saveConfig(threadsData, threadID, cfg); return; } try { await api.setTitle(cfg.lockedName, threadID); return message.send("🔒 গ্রুপের নাম locked, আগের নামে ফিরিয়ে দেওয়া হয়েছে।"); } catch (e) { /* ignore */ } } } };