const fs = require("fs"); const path = require("path"); function getPath(obj, keys) { return keys.reduce((o, k) => (o == null ? o : o[k]), obj); } function setPath(obj, keys, value) { let cur = obj; for (let i = 0; i < keys.length - 1; i++) { cur[keys[i]] = cur[keys[i]] || {}; cur = cur[keys[i]]; } cur[keys[keys.length - 1]] = value; } function status(val) { return val ? "ON ✦" : "OFF ◌"; } function wrapItem(text, maxWidth) { const words = text.split(" "); const lines = []; let cur = ""; for (const w of words) { const test = cur ? `${cur} ${w}` : w; if (test.length > maxWidth && cur) { lines.push(cur); cur = w; } else { cur = test; } } if (cur) lines.push(cur); return lines; } function renderBox(headerIcon, headerTitle, subtitleLines, boxLabel, itemLines, footer) { const lines = [`╭─ ${headerIcon} ${headerTitle.toUpperCase()}`]; subtitleLines.forEach(l => lines.push(`├ ${l}`)); lines.push(""); lines.push(`┌─ ${boxLabel.toUpperCase()} ─┐`); itemLines.forEach(item => { wrapItem(item, 22).forEach(w => lines.push(`│ ${w}`)); }); lines.push("└─────────────┘"); lines.push(""); lines.push(`╰─ ${footer}`); return lines.join("\n"); } function masked(val) { if (val === undefined || val === null || val === "" || val === " ") return "Not set ✕"; return "Set ✦ (" + String(val).length + " chars)"; } const BOOL_GROUPS = { hideNoti: { title: "🔕 Hide Notification Messages", path: ["hideNotiMessage"], items: [ ["commandNotFound", "Command Not Found"], ["adminOnly", "Admin Only"], ["threadBanned", "Thread Banned"], ["userBanned", "User Banned"], ["needRoleToUseCmd", "Need Role To Use Cmd"], ["needRoleToUseCmdOnReply", "…On Reply"], ["needRoleToUseCmdOnReaction", "…On Reaction"] ] }, logEvents: { title: "📝 Log Events", path: ["logEvents"], items: [ ["disableAll", "Disable All"], ["message", "Message"], ["message_reaction", "Message Reaction"], ["message_unsend", "Message Unsend"], ["message_reply", "Message Reply"], ["event", "Event"], ["read_receipt", "Read Receipt"], ["typ", "Typing"], ["presence", "Presence"] ] }, fcaOptions: { title: "🧩 FCA Options", path: ["optionsFca"], items: [ ["forceLogin", "Force Login"], ["listenEvents", "Listen Events"], ["updatePresence", "Update Presence"], ["listenTyping", "Listen Typing"], ["selfListen", "Self Listen"], ["selfListenEvent", "Self Listen Event"], ["autoMarkDelivery", "Auto Mark Delivery"], ["autoReconnect", "Auto Reconnect"] ], afterToggle: (api, key, val) => { try { api.setOptions({ [key]: val }); } catch (e) { } } }, notiMqttGmail: { title: "📧 MQTT Error Notify — Gmail", path: ["notiWhenListenMqttError", "gmail"], items: [["enable", "Enable"]] }, notiMqttTelegram: { title: "📨 MQTT Error Notify — Telegram", path: ["notiWhenListenMqttError", "telegram"], items: [["enable", "Enable"]] }, notiMqttDiscord: { title: "📢 MQTT Error Notify — Discord", path: ["notiWhenListenMqttError", "discordHook"], items: [["enable", "Enable"]] } }; function renderBoolGroupMenu(config, groupKey) { const group = BOOL_GROUPS[groupKey]; const sub = getPath(config, group.path) || {}; const itemLines = group.items.map(([key, label], i) => `${i + 1}. ${label} — ${status(sub[key])}`); return renderBox("⚙️", group.title.replace(/^[^\w]*/, "").trim(), [`Items : ${group.items.length}`], "toggles", itemLines, "Reply a number to toggle, or 0 to go back"); } const CATEGORIES = [ { title: "🔐 Account & Security", items: [ { num: 1, label: "Facebook Account" }, { num: 17, label: "Credentials (Gmail / reCAPTCHA)" } ] }, { title: "🤖 Bot Behavior", items: [ { num: 2, label: "Bot Behavior" }, { num: 5, label: "No Prefix" }, { num: 7, label: "Nickname" }, { num: 9, label: "Typing Indicator" } ] }, { title: "👥 Access Control", items: [ { num: 3, label: "Admin Manage" }, { num: 4, label: "Whitelist Manage" }, { num: 6, label: "React Unsend" } ] }, { title: "🧩 Connection (FCA / MQTT)", items: [ { num: 8, label: "FCA Options" }, { num: 13, label: "Restart Listen MQTT" }, { num: 14, label: "MQTT Error Notify" }, { num: 18, label: "E2EE" } ] }, { title: "🗄️ Database & Storage", items: [ { num: 10, label: "Database" } ] }, { title: "📊 Dashboard & Monitoring", items: [ { num: 11, label: "Dashboard & Uptime" }, { num: 12, label: "Auto Load Scripts" } ] }, { title: "📝 Logging", items: [ { num: 15, label: "Hide Notification Messages" }, { num: 16, label: "Log Events" } ] } ]; function renderCategoryList() { const itemLines = CATEGORIES.map((cat, i) => `${i + 1}. ${cat.title.replace(/^[^\w]*/, "").trim()}`); return renderBox("⚙️", "Bot Settings", [`Categories : ${CATEGORIES.length}`, "Author : EryXenX"], "categories", itemLines, `Reply 1-${CATEGORIES.length} to enter a category`); } function renderCategoryItems(catIndex) { const cat = CATEGORIES[catIndex]; const itemLines = cat.items.map((item, i) => `${i + 1}. ${item.label}`); return renderBox("🧩", cat.title.replace(/^[^\w]*/, "").trim(), [`Items : ${cat.items.length}`], "items", itemLines, "Reply a number, or 0 to go back to categories"); } module.exports = { config: { name: "إعدادات", aliases: ["setting", "settings"], version: "8.0.0", author: "EryXenX", countDown: 5, role: 2, shortDescription: "إعدادات البوت العامة", longDescription: "إعدادات البوت العامة", category: "admin", guide: "{pn}" }, onStart: async function ({ event, message }) { const sent = await message.reply(renderCategoryList()); global.GoatBot.onReply.set(sent.messageID, { commandName: "إعدادات", messageID: sent.messageID, author: event.senderID, state: "categories" }); }, onReply: async function ({ api, event, Reply, message }) { const { author, state } = Reply; if (event.senderID !== author) return; const configPath = path.join(process.cwd(), "config.json"); const config = JSON.parse(fs.readFileSync(configPath, "utf8")); const input = event.body.trim(); let num = parseInt(input); function saveConfig() { fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf8"); } async function sendAndListen(text, newState, extra = {}) { const sent = await message.reply(text); global.GoatBot.onReply.set(sent.messageID, { commandName: "إعدادات", messageID: sent.messageID, author, state: newState, ...extra }); } async function backToCategories() { return await sendAndListen(renderCategoryList(), "categories"); } if (state === "categories") { const cat = CATEGORIES[num - 1]; if (!cat) return message.reply(`𝗫 Invalid selection. Reply 1-${CATEGORIES.length}.`); return await sendAndListen(renderCategoryItems(num - 1), "categoryItems", { catIndex: num - 1 }); } if (state === "categoryItems") { if (num === 0) return backToCategories(); const cat = CATEGORIES[Reply.catIndex]; const item = cat && cat.items[num - 1]; if (!item) return message.reply("𝗫 Invalid selection."); num = item.num; // fall through to the existing item logic below } if (BOOL_GROUPS[state]) { const group = BOOL_GROUPS[state]; if (num === 0) return backToCategories(); const item = group.items[num - 1]; if (!item) return message.reply("𝗫 Invalid selection. Reply a number from the list, or 0 to go back."); const [key] = item; const sub = getPath(config, group.path) || {}; sub[key] = !sub[key]; setPath(config, group.path, sub); saveConfig(); if (group.afterToggle) group.afterToggle(api, key, sub[key]); return await sendAndListen(renderBoolGroupMenu(config, state), state); } if (state === "categoryItems" || state === "itemMenu") { if (num === 1) { const fb = config.facebookAccount || {}; const menu = renderBox("👤", "Facebook Account", [], "fields", [ `1. ${"Email"} — ${fb.email || "Not set ✕"}`, `2. ${"Password"} — ${masked(fb.password)}`, `3. ${"2FA Secret"} — ${masked(fb["2FASecret"])}`, `4. ${"i_user"} — ${masked(fb.i_user)}`, `5. ${"Proxy"} — ${fb.proxy || "Not set ✕"}`, `6. ${"User Agent"} — ${fb.userAgent ? "Set ✦" : "Not set ✕"}`, `7. ${"Cookie Refresh Interval"} — ${fb.intervalGetNewCookie ?? "Not set"} min` ], "Reply 1-7 · ⚠️ needs a bot restart to take effect"); return await sendAndListen(menu, "fbAccount"); } if (num === 2) { const menu = renderBox("🤖", "Bot Behavior", [], "fields", [ `1. ${"Prefix"} — ${config.prefix || "/"}`, `2. ${"Language"} — ${config.language || "en"}`, `3. ${"Time Zone"} — ${config.timeZone || "Not set"}`, `4. ${"Anti Inbox"} — ${status(config.antiInbox)}`, `5. ${"Admin Only"} — ${status(config.adminOnly?.enable)}`, `6. ${"Only Admin Box"} — ${status(config.onlyAdminBox)}`, `7. ${"Auto Refresh Fbstate"} — ${status(config.autoRefreshFbstate)}`, `8. ${"Auto Relogin On Account Change"} — ${status(config.autoReloginWhenChangeAccount)}`, `9. ${"Auto Restart On MQTT Error"} — ${status(config.autoRestartWhenListenMqttError)}`, `10. ${"Auto Restart Time (cron/ms)"} — ${config.autoRestart?.time ?? "Not set"}` ], "Reply 1-10"); return await sendAndListen(menu, "botBehavior"); } if (num === 3) { const menu = renderBox("⚙️", "Admin Manage", [], "actions", [ `1. ${"Add Admin"}`, `2. ${"Remove Admin"}`, `3. ${"List Admins"}` ], "Reply 1-3"); return await sendAndListen(menu, "adminManage"); } if (num === 4) { const menu = renderBox("⚙️", "Whitelist Manage", [], "actions", [ `1. ${"Thread Whitelist"} — ${status(config.whiteListModeThread?.enable)}`, `2. ${"Add Thread"}`, `3. ${"Remove Thread"}`, `4. ${"User Whitelist"} — ${status(config.whiteListMode?.enable)}`, `5. ${"Add User"}`, `6. ${"Remove User"}` ], "Reply 1-6"); return await sendAndListen(menu, "whitelist"); } if (num === 5) { config.noPrefix = config.noPrefix || {}; config.noPrefix.enable = !config.noPrefix.enable; saveConfig(); return message.reply(`✦ No Prefix is now ${status(config.noPrefix.enable)}`); } if (num === 6) { const menu = renderBox("⚙️", "React Unsend", [], "fields", [ `1. ${"Toggle"} — ${status(config.reactUnsend?.enable)}`, `2. ${"Only Admin"} — ${status(config.reactUnsend?.onlyAdmin)}`, `3. ${"Add Emoji"}`, `4. ${"Remove Emoji"}`, `5. ${"List Emojis"}` ], "Reply 1-5"); return await sendAndListen(menu, "reactUnsend"); } if (num === 7) { const current = config.nickNameBot || "Not set"; const menu = renderBox("⚙️", "Nickname", [`Current : ${current}`], "actions", [ `1. ${"Set Nickname (this group)"}`, `2. ${"Set Nickname (all groups)"}`, `3. ${"Reset Nickname"}` ], "Reply 1-3"); return await sendAndListen(menu, "nickname"); } if (num === 8) return await sendAndListen(renderBoolGroupMenu(config, "fcaOptions"), "fcaOptions"); if (num === 9) { const menu = renderBox("⚙️", "Typing Indicator", [], "fields", [ `1. ${"Toggle"} — ${status(config.enableTypingIndicator)}`, `2. ${"Duration"} — ${config.typingDuration || 2000}ms` ], "Reply 1-2"); return await sendAndListen(menu, "typingIndicator"); } if (num === 10) { const db = config.database || {}; const menu = renderBox("🗄️", "Database", [], "fields", [ `1. ${"Type"} — ${db.type || "Not set"} (needs restart to change)`, `2. ${"MongoDB URI"} — ${masked(db.uriMongodb)}`, `3. ${"Auto Sync When Start"} — ${status(db.autoSyncWhenStart)}`, `4. ${"Auto Refresh Thread Info (first time)"} — ${status(db.autoRefreshThreadInfoFirstTime)}` ], "Reply 1-4"); return await sendAndListen(menu, "database"); } if (num === 11) { const dash = config.dashBoard || {}; const up = config.serverUptime || {}; const auto = config.autoUptime || {}; const menu = renderBox("📊", "Dashboard & Uptime", [], "fields", [ `1. ${"Dashboard Enable"} — ${status(dash.enable)}`, `2. ${"Dashboard Port"} — ${dash.port ?? "Not set"}`, `3. ${"Dashboard Verify Code Expiry (ms)"} — ${dash.expireVerifyCode ?? "Not set"}`, `4. ${"Server Uptime Enable"} — ${status(up.enable)}`, `5. ${"Server Uptime Port"} — ${up.port ?? "Not set"}`, `6. ${"Server Uptime Socket Enable"} — ${status(up.socket?.enable)}`, `7. ${"Server Uptime Channel Name"} — ${up.socket?.channelName || "Not set"}`, `8. ${"Auto Uptime Enable"} — ${status(auto.enable)}`, `9. ${"Auto Uptime Interval (s)"} — ${auto.timeInterval ?? "Not set"}`, `10. ${"Auto Uptime URL"} — ${auto.url || "Not set"}` ], "Reply 1-10"); return await sendAndListen(menu, "dashboardUptime"); } if (num === 12) { const a = config.autoLoadScripts || {}; const menu = renderBox("📜", "Auto Load Scripts", [], "fields", [ `1. ${"Enable"} — ${status(a.enable)}`, `2. ${"Ignore Commands"} — ${a.ignoreCmds || "(none)"}`, `3. ${"Ignore Events"} — ${a.ignoreEvents || "(none)"}` ], "Reply 1-3"); return await sendAndListen(menu, "autoLoadScripts"); } if (num === 13) { const r = config.restartListenMqtt || {}; const menu = renderBox("🔁", "Restart Listen MQTT", [], "fields", [ `1. ${"Enable"} — ${status(r.enable)}`, `2. ${"Restart Interval (ms)"} — ${r.timeRestart ?? "Not set"}`, `3. ${"Delay After Stop (ms)"} — ${r.delayAfterStopListening ?? "Not set"}`, `4. ${"Log Notification"} — ${status(r.logNoti)}` ], "Reply 1-4"); return await sendAndListen(menu, "restartListenMqtt"); } if (num === 14) { const menu = renderBox("🔔", "MQTT Error Notify", [], "channels", [ `1. ${"Gmail"}`, `2. ${"Telegram"}`, `3. ${"Discord"}` ], "Reply 1-3"); return await sendAndListen(menu, "notiMqttPick"); } if (num === 15) return await sendAndListen(renderBoolGroupMenu(config, "hideNoti"), "hideNoti"); if (num === 16) return await sendAndListen(renderBoolGroupMenu(config, "logEvents"), "logEvents"); if (num === 17) { const g = config.credentials?.gmailAccount || {}; const r = config.credentials?.gRecaptcha || {}; const menu = renderBox("🔑", "Credentials", [], "fields", [ `1. ${"Gmail Email"} — ${g.email || "Not set ✕"}`, `2. ${"Gmail Client ID"} — ${masked(g.clientId)}`, `3. ${"Gmail Client Secret"} — ${masked(g.clientSecret)}`, `4. ${"Gmail Refresh Token"} — ${masked(g.refreshToken)}`, `5. ${"Gmail API Key"} — ${masked(g.apiKey)}`, `6. ${"reCAPTCHA Site Key"} — ${masked(r.siteKey)}`, `7. ${"reCAPTCHA Secret Key"} — ${masked(r.secretKey)}` ], "Reply 1-7"); return await sendAndListen(menu, "credentials"); } if (num === 18) { config.e2ee = config.e2ee || {}; const was = config.e2ee.enable; config.e2ee.enable = !was; saveConfig(); const sent = await message.reply(`✦ E2EE is now ${status(config.e2ee.enable)}\n⚠️ Needs a bot restart to take effect.\n› React to this message to restart now, or select E2EE again later to switch it back without restarting.`); global.GoatBot.onReaction.set(sent.messageID, { commandName: "إعدادات", messageID: sent.messageID, type: "e2eeRestartConfirm", author }); return; } } if (state === "fbAccount") { const fields = [ ["email", "Email", "text"], ["password", "Password", "text"], ["2FASecret", "2FA Secret", "text"], ["i_user", "i_user", "text"], ["proxy", "Proxy", "text"], ["userAgent", "User Agent", "text"], ["intervalGetNewCookie", "Cookie Refresh Interval (minutes)", "number"] ]; const field = fields[num - 1]; if (!field) return message.reply("𝗫 Invalid selection."); const [key, label, type] = field; return await sendAndListen(`› Reply with new value for ${label}:`, "fbAccountSet", { fieldKey: key, fieldType: type }); } if (state === "fbAccountSet") { const { fieldKey, fieldType } = Reply; let value = input; if (fieldType === "number") { if (isNaN(num)) return message.reply("𝗫 Invalid number."); value = num; } config.facebookAccount = config.facebookAccount || {}; config.facebookAccount[fieldKey] = value; saveConfig(); return message.reply(`✦ Updated. (Restart the bot for this to take effect.)`); } if (state === "botBehavior") { if (num === 1) return await sendAndListen("› Reply with new prefix:", "prefixSet"); if (num === 2) return await sendAndListen("› Reply with new language code (e.g. en, bn):", "languageSet"); if (num === 3) return await sendAndListen("› Reply with new time zone (e.g. Asia/Dhaka):", "timeZoneSet"); if (num === 4) { config.antiInbox = !config.antiInbox; saveConfig(); return message.reply(`✦ Anti Inbox — ${status(config.antiInbox)}`); } if (num === 5) { config.adminOnly = config.adminOnly || {}; config.adminOnly.enable = !config.adminOnly.enable; saveConfig(); return message.reply(`✦ Admin Only — ${status(config.adminOnly.enable)}`); } if (num === 6) { config.onlyAdminBox = !config.onlyAdminBox; saveConfig(); return message.reply(`✦ Only Admin Box — ${status(config.onlyAdminBox)}`); } if (num === 7) { config.autoRefreshFbstate = !config.autoRefreshFbstate; saveConfig(); return message.reply(`✦ Auto Refresh Fbstate — ${status(config.autoRefreshFbstate)}`); } if (num === 8) { config.autoReloginWhenChangeAccount = !config.autoReloginWhenChangeAccount; saveConfig(); return message.reply(`✦ Auto Relogin On Account Change — ${status(config.autoReloginWhenChangeAccount)}`); } if (num === 9) { config.autoRestartWhenListenMqttError = !config.autoRestartWhenListenMqttError; saveConfig(); return message.reply(`✦ Auto Restart On MQTT Error — ${status(config.autoRestartWhenListenMqttError)}`); } if (num === 10) return await sendAndListen("› Reply with new Auto Restart time (ms, or cron string, or 'null' to disable):", "autoRestartTimeSet"); } if (state === "prefixSet") { if (!input) return message.reply("𝗫 Invalid prefix."); config.prefix = input; saveConfig(); return message.reply(`✦ Prefix set to: ${input}`); } if (state === "languageSet") { if (!input) return message.reply("𝗫 Invalid language code."); config.language = input; saveConfig(); return message.reply(`✦ Language set to: ${input}`); } if (state === "timeZoneSet") { if (!input) return message.reply("𝗫 Invalid time zone."); config.timeZone = input; saveConfig(); return message.reply(`✦ Time Zone set to: ${input}`); } if (state === "autoRestartTimeSet") { config.autoRestart = config.autoRestart || {}; config.autoRestart.time = input === "null" ? null : (isNaN(num) ? input : num); saveConfig(); return message.reply(`✦ Auto Restart time set to: ${config.autoRestart.time}`); } if (state === "adminManage") { if (num === 1) return await sendAndListen("› Reply with UID or tag user to add as admin:", "adminAdd"); if (num === 2) { const admins = config.adminBot || []; if (!admins.length) return message.reply("𝗫 No admins found."); const list = admins.map((id, i) => `${i + 1}. ${id}`); await message.reply(renderBox("⚙️", "Remove Admin", [], "admins", list, "Reply number")); return await sendAndListen("› Waiting...", "adminRemoveSelect"); } if (num === 3) { const admins = config.adminBot || []; if (!admins.length) return message.reply("𝗫 No admins found."); return message.reply(renderBox("⚙️", "Admins", [], "list", admins.map((id, i) => `${i + 1}. ${id}`), "Reply 1 to go back to categories")); } } if (state === "adminAdd") { let uid = input; if (event.mentions && Object.keys(event.mentions).length > 0) uid = Object.keys(event.mentions)[0]; if (!uid || isNaN(uid)) return message.reply("𝗫 Invalid UID."); config.adminBot = config.adminBot || []; if (config.adminBot.includes(uid)) return message.reply("𝗫 Already an admin."); config.adminBot.push(uid); saveConfig(); return message.reply(`✦ Added ${uid} as admin.`); } if (state === "adminRemoveSelect") { const admins = config.adminBot || []; const idx = num - 1; if (isNaN(num) || !admins[idx]) return message.reply("𝗫 Invalid selection."); const removed = admins.splice(idx, 1)[0]; config.adminBot = admins; saveConfig(); return message.reply(`✦ Removed ${removed} from admins.`); } if (state === "whitelist") { if (num === 1) { config.whiteListModeThread = config.whiteListModeThread || {}; config.whiteListModeThread.enable = !config.whiteListModeThread.enable; saveConfig(); return message.reply(`✦ Thread Whitelist is now ${status(config.whiteListModeThread.enable)}`); } if (num === 2) return await sendAndListen("› Reply with Thread ID to add:", "threadAdd"); if (num === 3) { const threads = config.whiteListModeThread?.whiteListThreadIds || []; if (!threads.length) return message.reply("𝗫 No threads found."); const list = threads.map((id, i) => `${i + 1}. ${id}`); await message.reply(renderBox("⚙️", "Remove Thread", [], "threads", list, "Reply number")); return await sendAndListen("› Waiting...", "threadRemoveSelect"); } if (num === 4) { config.whiteListMode = config.whiteListMode || {}; config.whiteListMode.enable = !config.whiteListMode.enable; saveConfig(); return message.reply(`✦ User Whitelist is now ${status(config.whiteListMode.enable)}`); } if (num === 5) return await sendAndListen("› Reply with UID or tag user to add:", "userAdd"); if (num === 6) { const users = config.whiteListMode?.whiteListIds || []; if (!users.length) return message.reply("𝗫 No users found."); const list = users.map((id, i) => `${i + 1}. ${id}`); await message.reply(renderBox("⚙️", "Remove User", [], "users", list, "Reply number")); return await sendAndListen("› Waiting...", "userRemoveSelect"); } } if (state === "threadAdd") { const tid = input; if (!tid || isNaN(tid)) return message.reply("𝗫 Invalid Thread ID."); config.whiteListModeThread = config.whiteListModeThread || {}; config.whiteListModeThread.whiteListThreadIds = config.whiteListModeThread.whiteListThreadIds || []; if (config.whiteListModeThread.whiteListThreadIds.includes(tid)) return message.reply("𝗫 Already in whitelist."); config.whiteListModeThread.whiteListThreadIds.push(tid); saveConfig(); return message.reply(`✦ Thread ${tid} added.`); } if (state === "threadRemoveSelect") { const threads = config.whiteListModeThread?.whiteListThreadIds || []; const idx = num - 1; if (isNaN(num) || !threads[idx]) return message.reply("𝗫 Invalid selection."); const removed = threads.splice(idx, 1)[0]; config.whiteListModeThread.whiteListThreadIds = threads; saveConfig(); return message.reply(`✦ Thread ${removed} removed.`); } if (state === "userAdd") { let uid = input; if (event.mentions && Object.keys(event.mentions).length > 0) uid = Object.keys(event.mentions)[0]; if (!uid || isNaN(uid)) return message.reply("𝗫 Invalid UID."); config.whiteListMode = config.whiteListMode || {}; config.whiteListMode.whiteListIds = config.whiteListMode.whiteListIds || []; if (config.whiteListMode.whiteListIds.includes(uid)) return message.reply("𝗫 Already in whitelist."); config.whiteListMode.whiteListIds.push(uid); saveConfig(); return message.reply(`✦ User ${uid} added.`); } if (state === "userRemoveSelect") { const users = config.whiteListMode?.whiteListIds || []; const idx = num - 1; if (isNaN(num) || !users[idx]) return message.reply("𝗫 Invalid selection."); const removed = users.splice(idx, 1)[0]; config.whiteListMode.whiteListIds = users; saveConfig(); return message.reply(`✦ User ${removed} removed.`); } if (state === "reactUnsend") { if (num === 1) { config.reactUnsend = config.reactUnsend || {}; config.reactUnsend.enable = !config.reactUnsend.enable; saveConfig(); return message.reply(`✦ React Unsend — ${status(config.reactUnsend.enable)}`); } if (num === 2) { config.reactUnsend = config.reactUnsend || {}; config.reactUnsend.onlyAdmin = !config.reactUnsend.onlyAdmin; saveConfig(); return message.reply(`✦ Only Admin — ${status(config.reactUnsend.onlyAdmin)}`); } if (num === 3) return await sendAndListen("› Reply with emoji to add:", "emojiAdd"); if (num === 4) { const emojis = config.reactUnsend?.emojis || []; if (!emojis.length) return message.reply("𝗫 No emojis found."); const list = emojis.map((e, i) => `${i + 1}. ${e}`); await message.reply(renderBox("⚙️", "Remove Emoji", [], "emojis", list, "Reply number")); return await sendAndListen("› Waiting...", "emojiRemoveSelect"); } if (num === 5) { const emojis = config.reactUnsend?.emojis || []; if (!emojis.length) return message.reply("𝗫 No emojis."); return message.reply(renderBox("⚙️", "Emojis", [], "list", [emojis.join(" ")], "Reply 1 to go back to categories")); } } if (state === "emojiAdd") { const emoji = input.trim(); if (!emoji) return message.reply("𝗫 Invalid emoji."); config.reactUnsend = config.reactUnsend || {}; config.reactUnsend.emojis = config.reactUnsend.emojis || []; if (config.reactUnsend.emojis.includes(emoji)) return message.reply("𝗫 Already added."); config.reactUnsend.emojis.push(emoji); saveConfig(); return message.reply(`✦ Emoji ${emoji} added.`); } if (state === "emojiRemoveSelect") { const emojis = config.reactUnsend?.emojis || []; const idx = num - 1; if (isNaN(num) || !emojis[idx]) return message.reply("𝗫 Invalid selection."); const removed = emojis.splice(idx, 1)[0]; config.reactUnsend.emojis = emojis; saveConfig(); return message.reply(`✦ Emoji ${removed} removed.`); } if (state === "nickname") { if (num === 1) return await sendAndListen("› Reply with new nickname:", "nicknameSet"); if (num === 2) return await sendAndListen("› Reply with new nickname (will set in all groups):", "nicknameSetAll"); if (num === 3) { config.nickNameBot = ""; saveConfig(); try { await api.changeNickname("", event.threadID, api.getCurrentUserID()); } catch (e) { } return message.reply("✦ Nickname reset."); } } if (state === "nicknameSet") { const nickname = input; if (!nickname) return message.reply("𝗫 Invalid nickname."); config.nickNameBot = nickname; saveConfig(); try { await api.changeNickname(nickname, event.threadID, api.getCurrentUserID()); } catch (e) { } return message.reply(`✦ Nickname set to: ${nickname}`); } if (state === "nicknameSetAll") { const nickname = input; if (!nickname) return message.reply("𝗫 Invalid nickname."); config.nickNameBot = nickname; saveConfig(); const threads = await api.getThreadList(100, null, ["INBOX"]); let success = 0; for (const thread of threads) { if (!thread.isGroup) continue; try { await api.changeNickname(nickname, thread.threadID, api.getCurrentUserID()); success++; } catch (e) { } } return message.reply(`✦ Nickname set to: ${nickname}\n› Updated in ${success} groups.`); } if (state === "typingIndicator") { if (num === 1) { config.enableTypingIndicator = !config.enableTypingIndicator; saveConfig(); return message.reply(`✦ Typing Indicator — ${status(config.enableTypingIndicator)}`); } if (num === 2) return await sendAndListen("› Reply with duration in milliseconds (e.g. 2000 for 2s):", "typingDurationSet"); } if (state === "typingDurationSet") { if (isNaN(num) || num < 500 || num > 15000) return message.reply("𝗫 Invalid duration. Use a value between 500 and 15000 ms."); config.typingDuration = num; saveConfig(); return message.reply(`✦ Typing Duration set to: ${num}ms`); } if (state === "database") { if (num === 1) return message.reply("𝗫 Database type can't be changed live — edit config.json manually and restart the bot."); if (num === 2) return await sendAndListen("› Reply with new MongoDB URI:", "mongoUriSet"); if (num === 3) { config.database = config.database || {}; config.database.autoSyncWhenStart = !config.database.autoSyncWhenStart; saveConfig(); return message.reply(`✦ Auto Sync When Start — ${status(config.database.autoSyncWhenStart)}`); } if (num === 4) { config.database = config.database || {}; config.database.autoRefreshThreadInfoFirstTime = !config.database.autoRefreshThreadInfoFirstTime; saveConfig(); return message.reply(`✦ Auto Refresh Thread Info — ${status(config.database.autoRefreshThreadInfoFirstTime)}`); } } if (state === "mongoUriSet") { if (!input) return message.reply("𝗫 Invalid URI."); config.database = config.database || {}; config.database.uriMongodb = input; saveConfig(); return message.reply("✦ MongoDB URI updated. (Restart the bot for this to take effect.)"); } if (state === "dashboardUptime") { config.dashBoard = config.dashBoard || {}; config.serverUptime = config.serverUptime || {}; config.serverUptime.socket = config.serverUptime.socket || {}; config.autoUptime = config.autoUptime || {}; if (num === 1) { config.dashBoard.enable = !config.dashBoard.enable; saveConfig(); return message.reply(`✦ Dashboard Enable — ${status(config.dashBoard.enable)}`); } if (num === 2) return await sendAndListen("› Reply with new Dashboard port:", "dashPortSet"); if (num === 3) return await sendAndListen("› Reply with new Verify Code expiry (ms):", "dashExpirySet"); if (num === 4) { config.serverUptime.enable = !config.serverUptime.enable; saveConfig(); return message.reply(`✦ Server Uptime Enable — ${status(config.serverUptime.enable)}`); } if (num === 5) return await sendAndListen("› Reply with new Server Uptime port:", "uptimePortSet"); if (num === 6) { config.serverUptime.socket.enable = !config.serverUptime.socket.enable; saveConfig(); return message.reply(`✦ Server Uptime Socket Enable — ${status(config.serverUptime.socket.enable)}`); } if (num === 7) return await sendAndListen("› Reply with new channel name:", "uptimeChannelSet"); if (num === 8) { config.autoUptime.enable = !config.autoUptime.enable; saveConfig(); return message.reply(`✦ Auto Uptime Enable — ${status(config.autoUptime.enable)}`); } if (num === 9) return await sendAndListen("› Reply with new interval in seconds:", "autoUptimeIntervalSet"); if (num === 10) return await sendAndListen("› Reply with new Auto Uptime URL:", "autoUptimeUrlSet"); } if (state === "dashPortSet") { if (isNaN(num)) return message.reply("𝗫 Invalid port."); config.dashBoard.port = num; saveConfig(); return message.reply(`✦ Dashboard port set to: ${num}`); } if (state === "dashExpirySet") { if (isNaN(num)) return message.reply("𝗫 Invalid value."); config.dashBoard.expireVerifyCode = num; saveConfig(); return message.reply(`✦ Verify Code expiry set to: ${num}ms`); } if (state === "uptimePortSet") { if (isNaN(num)) return message.reply("𝗫 Invalid port."); config.serverUptime.port = num; saveConfig(); return message.reply(`✦ Server Uptime port set to: ${num}`); } if (state === "uptimeChannelSet") { if (!input) return message.reply("𝗫 Invalid channel name."); config.serverUptime.socket.channelName = input; saveConfig(); return message.reply(`✦ Channel name set to: ${input}`); } if (state === "autoUptimeIntervalSet") { if (isNaN(num)) return message.reply("𝗫 Invalid value."); config.autoUptime.timeInterval = num; saveConfig(); return message.reply(`✦ Interval set to: ${num}s`); } if (state === "autoUptimeUrlSet") { if (!input) return message.reply("𝗫 Invalid URL."); config.autoUptime.url = input; saveConfig(); return message.reply(`✦ URL set to: ${input}`); } if (state === "autoLoadScripts") { config.autoLoadScripts = config.autoLoadScripts || {}; if (num === 1) { config.autoLoadScripts.enable = !config.autoLoadScripts.enable; saveConfig(); return message.reply(`✦ Auto Load Scripts — ${status(config.autoLoadScripts.enable)}`); } if (num === 2) return await sendAndListen("› Reply with comma-separated command names to ignore:", "ignoreCmdsSet"); if (num === 3) return await sendAndListen("› Reply with comma-separated event names to ignore:", "ignoreEventsSet"); } if (state === "ignoreCmdsSet") { config.autoLoadScripts.ignoreCmds = input; saveConfig(); return message.reply(`✦ Ignore Commands set to: ${input || "(none)"}`); } if (state === "ignoreEventsSet") { config.autoLoadScripts.ignoreEvents = input; saveConfig(); return message.reply(`✦ Ignore Events set to: ${input || "(none)"}`); } if (state === "restartListenMqtt") { config.restartListenMqtt = config.restartListenMqtt || {}; if (num === 1) { config.restartListenMqtt.enable = !config.restartListenMqtt.enable; saveConfig(); return message.reply(`✦ Restart Listen MQTT Enable — ${status(config.restartListenMqtt.enable)}`); } if (num === 2) return await sendAndListen("› Reply with new restart interval (ms):", "mqttTimeRestartSet"); if (num === 3) return await sendAndListen("› Reply with new delay after stop (ms):", "mqttDelaySet"); if (num === 4) { config.restartListenMqtt.logNoti = !config.restartListenMqtt.logNoti; saveConfig(); return message.reply(`✦ Log Notification — ${status(config.restartListenMqtt.logNoti)}`); } } if (state === "mqttTimeRestartSet") { if (isNaN(num)) return message.reply("𝗫 Invalid value."); config.restartListenMqtt.timeRestart = num; saveConfig(); return message.reply(`✦ Restart interval set to: ${num}ms`); } if (state === "mqttDelaySet") { if (isNaN(num)) return message.reply("𝗫 Invalid value."); config.restartListenMqtt.delayAfterStopListening = num; saveConfig(); return message.reply(`✦ Delay set to: ${num}ms`); } if (state === "notiMqttPick") { if (num === 1) return await sendAndListen(renderBoolGroupMenu(config, "notiMqttGmail") + "\n\n2. Set Notify Email\n0. Back", "notiMqttGmailMenu"); if (num === 2) return await sendAndListen(renderBoolGroupMenu(config, "notiMqttTelegram") + "\n\n2. Set Bot Token\n3. Set Chat ID\n0. Back", "notiMqttTelegramMenu"); if (num === 3) return await sendAndListen(renderBoolGroupMenu(config, "notiMqttDiscord") + "\n\n2. Set Webhook URL\n0. Back", "notiMqttDiscordMenu"); } if (state === "notiMqttGmailMenu") { config.notiWhenListenMqttError = config.notiWhenListenMqttError || {}; config.notiWhenListenMqttError.gmail = config.notiWhenListenMqttError.gmail || {}; if (num === 0) { const menu = renderBox("🔔", "MQTT Error Notify", [], "channels", [ `1. ${"Gmail"}`, `2. ${"Telegram"}`, `3. ${"Discord"}` ], "Reply 1-3"); return await sendAndListen(menu, "notiMqttPick"); } if (num === 1) { config.notiWhenListenMqttError.gmail.enable = !config.notiWhenListenMqttError.gmail.enable; saveConfig(); return message.reply(`✦ Gmail Notify — ${status(config.notiWhenListenMqttError.gmail.enable)}`); } if (num === 2) return await sendAndListen("› Reply with notify email:", "notiGmailEmailSet"); } if (state === "notiGmailEmailSet") { if (!input) return message.reply("𝗫 Invalid email."); config.notiWhenListenMqttError.gmail.emailGetNoti = input; saveConfig(); return message.reply(`✦ Notify email set to: ${input}`); } if (state === "notiMqttTelegramMenu") { config.notiWhenListenMqttError = config.notiWhenListenMqttError || {}; config.notiWhenListenMqttError.telegram = config.notiWhenListenMqttError.telegram || {}; if (num === 1) { config.notiWhenListenMqttError.telegram.enable = !config.notiWhenListenMqttError.telegram.enable; saveConfig(); return message.reply(`✦ Telegram Notify — ${status(config.notiWhenListenMqttError.telegram.enable)}`); } if (num === 2) return await sendAndListen("› Reply with bot token:", "notiTgTokenSet"); if (num === 3) return await sendAndListen("› Reply with chat ID:", "notiTgChatSet"); } if (state === "notiTgTokenSet") { config.notiWhenListenMqttError.telegram.botToken = input; saveConfig(); return message.reply("✦ Bot token updated."); } if (state === "notiTgChatSet") { config.notiWhenListenMqttError.telegram.chatId = input; saveConfig(); return message.reply(`✦ Chat ID set to: ${input}`); } if (state === "notiMqttDiscordMenu") { config.notiWhenListenMqttError = config.notiWhenListenMqttError || {}; config.notiWhenListenMqttError.discordHook = config.notiWhenListenMqttError.discordHook || {}; if (num === 1) { config.notiWhenListenMqttError.discordHook.enable = !config.notiWhenListenMqttError.discordHook.enable; saveConfig(); return message.reply(`✦ Discord Notify — ${status(config.notiWhenListenMqttError.discordHook.enable)}`); } if (num === 2) return await sendAndListen("› Reply with webhook URL:", "notiDiscordUrlSet"); } if (state === "notiDiscordUrlSet") { config.notiWhenListenMqttError.discordHook.webhookUrl = input; saveConfig(); return message.reply("✦ Webhook URL updated."); } if (state === "credentials") { config.credentials = config.credentials || {}; config.credentials.gmailAccount = config.credentials.gmailAccount || {}; config.credentials.gRecaptcha = config.credentials.gRecaptcha || {}; const fields = [ ["gmailAccount", "email", "Gmail Email"], ["gmailAccount", "clientId", "Gmail Client ID"], ["gmailAccount", "clientSecret", "Gmail Client Secret"], ["gmailAccount", "refreshToken", "Gmail Refresh Token"], ["gmailAccount", "apiKey", "Gmail API Key"], ["gRecaptcha", "siteKey", "reCAPTCHA Site Key"], ["gRecaptcha", "secretKey", "reCAPTCHA Secret Key"] ]; const field = fields[num - 1]; if (!field) return message.reply("𝗫 Invalid selection."); const [section, key, label] = field; return await sendAndListen(`› Reply with new value for ${label}:`, "credentialSet", { credSection: section, credKey: key }); } if (state === "credentialSet") { const { credSection, credKey } = Reply; config.credentials[credSection][credKey] = input; saveConfig(); return message.reply("✦ Updated."); } }, onReaction: async function ({ api, event, Reaction, message }) { if (Reaction.type !== "e2eeRestartConfirm") return; if (event.userID !== Reaction.author) return; const fs = require("fs"); const pathFile = `${__dirname}/tmp/restart.txt`; fs.writeFileSync(pathFile, `${event.threadID} ${Date.now()}`); await message.reply("🔄 | Restarting bot to apply E2EE change..."); process.exit(2); } };