├── config └── tokens.json ├── Types ├── config.d.ts ├── token.d.ts └── Client.ts ├── Scripts ├── Start.ts ├── Setup.ts └── Token.ts ├── Handlers ├── ConfigListener.ts └── TokenChecker.ts ├── Commands ├── Fun │ ├── CoinFlip.ts │ ├── Hack.ts │ └── Ascii.disabled ├── Command.ts ├── Basic │ ├── Avatar.ts │ ├── Calculate.ts │ ├── AFK.ts │ ├── help.ts │ ├── Speedtest.ts │ └── Ascii.ts ├── Utility │ ├── Friendcount.ts │ ├── AutoReact.ts │ ├── Presence.ts │ ├── Information.ts │ └── Library.ts ├── Moderation │ ├── Ban.ts │ ├── Kick.ts │ ├── Unban.ts │ ├── TotalBans.ts │ ├── Slowmode.ts │ ├── Clear.ts │ ├── Nick.ts │ ├── CloseDM.ts │ └── DeleteDM.ts ├── Raiding │ └── spam.ts └── Protection │ └── DeleteInvite.ts ├── Utils ├── Logger.ts ├── GitHubNews.ts ├── ErrorUtils.ts ├── MiscUtils.ts ├── MessageUtils.ts └── DiscordUtils.ts ├── package.json ├── tsconfig.json ├── Main.ts ├── LICENSE └── README.md /config/tokens.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /Types/config.d.ts: -------------------------------------------------------------------------------- 1 | declare module "../config/config.json" { 2 | interface Config { 3 | prefix: string; 4 | allowUsers: string[]; 5 | } 6 | const config: Config; 7 | export default config; 8 | } -------------------------------------------------------------------------------- /Types/token.d.ts: -------------------------------------------------------------------------------- 1 | declare module "../config/tokens.json" { 2 | interface TokenConfig { 3 | prefix: string; 4 | token: string; 5 | allowUsers: string[] | "all"; 6 | // allowEveryone: boolean; // Removed 7 | } 8 | 9 | const tokensConfig: Record; 10 | export default tokensConfig; 11 | } -------------------------------------------------------------------------------- /Scripts/Start.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | 3 | function Start() { 4 | console.log("Starting Kukuri Client..."); 5 | try { 6 | execSync("bun run start", { stdio: "inherit" }); 7 | console.log("Kukuri Client started successfully."); 8 | } catch { 9 | console.log("Failed to start Kukuri Client."); 10 | process.exit(1); 11 | } 12 | } 13 | 14 | Start(); 15 | -------------------------------------------------------------------------------- /Handlers/ConfigListener.ts: -------------------------------------------------------------------------------- 1 | import chokidar from "chokidar"; 2 | import path from "path"; 3 | import { Logger } from "../Utils/Logger"; 4 | 5 | export const ConfigListener = (config: any) => { 6 | const configPath = path.join(__dirname, "../config/tokens.json"); 7 | chokidar.watch(configPath).on("change", () => { 8 | delete require.cache[require.resolve(configPath)]; 9 | Object.assign(config, require(configPath)); 10 | Logger.log("Config reloaded!"); 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /Commands/Fun/CoinFlip.ts: -------------------------------------------------------------------------------- 1 | import { Client, Message } from "discord.js-selfbot-v13"; 2 | 3 | export default { 4 | name: "coinflip", 5 | description: "Flips a coin.", 6 | usage: "coinflip", 7 | execute: (client: Client, message: Message, args: string[]) => { 8 | function getRandomInt(max: number) { 9 | return Math.floor(Math.random() * max); 10 | } 11 | const coin = ["Heads", "Tails"]; 12 | const result = coin[getRandomInt(coin.length)]; 13 | message.reply(`You flipped a coin and got **${result}**!`); 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /Utils/Logger.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | 3 | export class Logger { 4 | static log(message: string) { 5 | console.log(chalk.green(`[INFO]: ${message}`)); 6 | } 7 | 8 | static error(message: string) { 9 | console.error(chalk.red(`[ERROR]: ${message}`)); 10 | } 11 | 12 | static warn(message: string) { 13 | console.warn(chalk.yellow(`[WARN]: ${message}`)); 14 | } 15 | 16 | static news(type: string, message: string) { 17 | if (type === "header") { 18 | console.log(chalk.cyan.bold(`[KUKURI CLIENT]: ${message}`)); 19 | } else { 20 | console.log(chalk.cyan(message)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Commands/Command.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Message } from "discord.js-selfbot-v13"; 3 | import { ClientInit } from "../Types/Client"; 4 | import { Logger } from "../Utils/Logger"; 5 | 6 | export default { 7 | name: "example", 8 | description: "This is a example command.", 9 | usage: "example", 10 | 11 | execute: async (client: ClientInit, message: Message, args: string[]) => { 12 | try { 13 | 14 | // Logic here 15 | 16 | } catch (error) { 17 | Logger.error(`Error in command '${exports.default.name}': ${error}`); 18 | await message.reply("An error occurred while executing this command.").catch(Logger.error); 19 | } 20 | }, 21 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kukuri-client", 3 | "module": "Main.ts", 4 | "type": "module", 5 | "scripts": { 6 | "setup": "bun Scripts/Setup.ts", 7 | "token": "bun Scripts/Token.ts", 8 | "start": "bun Main.ts", 9 | "cli": "bun Scripts/Start.ts" 10 | }, 11 | "devDependencies": { 12 | "@types/bun": "latest" 13 | }, 14 | "peerDependencies": { 15 | "typescript": "^5.0.0" 16 | }, 17 | "dependencies": { 18 | "axios": "^1.7.9", 19 | "chalk": "^5.4.1", 20 | "chokidar": "^4.0.3", 21 | "discord.js-selfbot-v13": "^3.6.0", 22 | "dotenv": "^16.4.7", 23 | "figlet": "^1.8.0", 24 | "speedtest-net": "^2.2.0", 25 | "systeminformation": "^5.25.11" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // Enable latest features 4 | "lib": ["ESNext", "DOM"], 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleDetection": "force", 8 | "jsx": "react-jsx", 9 | "allowJs": true, 10 | 11 | // Bundler mode 12 | "moduleResolution": "bundler", 13 | "allowImportingTsExtensions": true, 14 | "verbatimModuleSyntax": true, 15 | "noEmit": true, 16 | 17 | // Best practices 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "noFallthroughCasesInSwitch": true, 21 | 22 | // Some stricter flags (disabled by default) 23 | "noUnusedLocals": false, 24 | "noUnusedParameters": false, 25 | "noPropertyAccessFromIndexSignature": false 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Scripts/Setup.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | import { existsSync, writeFileSync } from "fs"; 3 | 4 | function Install() { 5 | console.log("Installing project dependencies..."); 6 | try { 7 | execSync("bun install", { stdio: "inherit" }); 8 | console.log("Dependencies installed successfully."); 9 | } catch { 10 | console.log("Failed to install dependencies."); 11 | process.exit(1); 12 | } 13 | } 14 | 15 | function Start() { 16 | console.log("Starting Kukuri Client..."); 17 | try { 18 | execSync("bun run start", { stdio: "inherit" }); 19 | console.log("Kukuri Client started successfully."); 20 | } catch { 21 | console.log("Failed to start Kukuri Client."); 22 | process.exit(1); 23 | } 24 | } 25 | 26 | function Setup() { 27 | console.log("Starting setup process..."); 28 | 29 | if (!existsSync("./config/tokens.json")) { 30 | writeFileSync("./config/tokens.json", JSON.stringify({})); 31 | console.log("Created empty tokens.json"); 32 | } 33 | Install(); 34 | Start(); 35 | } 36 | 37 | Setup(); 38 | -------------------------------------------------------------------------------- /Commands/Basic/Avatar.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { HandleError } from "../../Utils/ErrorUtils"; 5 | import { ParseUsr } from "../../Utils/DiscordUtils"; 6 | import { SendTempRep } from "../../Utils/MessageUtils"; 7 | 8 | export default { 9 | name: "avatar", 10 | description: "Displays the avatar of a user.", 11 | usage: "avatar [@user/userid]", 12 | execute: async (client: ClientInit, message: Message, args: string[]) => { 13 | try { 14 | const targetUser = await ParseUsr(client, args[0], message); 15 | 16 | if (!targetUser) { 17 | await SendTempRep(message, "Could not find the specified user."); 18 | return; 19 | } 20 | 21 | const avatarURL = targetUser.displayAvatarURL({ dynamic: true, size: 4096 }); 22 | await message.reply(avatarURL); 23 | 24 | } catch (error) { 25 | await HandleError(error, exports.default.name, message); 26 | } 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /Main.ts: -------------------------------------------------------------------------------- 1 | import { ClientInit } from "./Types/Client"; 2 | import { CheckToken } from "./Handlers/TokenChecker"; 3 | import { ConfigListener } from "./Handlers/ConfigListener"; 4 | import TokenConfig from "./config/tokens.json"; 5 | import { Logger } from "./Utils/Logger"; 6 | import { GetUpdates } from "./Utils/GitHubNews"; 7 | 8 | type TokenData = { 9 | prefix: string; 10 | token: string; 11 | allowUsers: string[] | "all"; 12 | // allowEveryone: boolean; 13 | }; 14 | 15 | const tokens = Object.values(TokenConfig).map( 16 | (tokenData: TokenData) => tokenData.token, 17 | ); 18 | 19 | if (tokens.length === 0) { 20 | Logger.error("No tokens found. Please add them in the tokens.json file."); 21 | process.exit(1); 22 | } 23 | 24 | CheckToken(tokens); 25 | 26 | const bots: ClientInit[] = []; 27 | 28 | for (const tokenData of Object.values(TokenConfig)) { 29 | const bot = new ClientInit( 30 | tokenData.prefix, 31 | tokenData.allowUsers 32 | ); 33 | bot.start(tokenData.token); 34 | bots.push(bot); 35 | } 36 | 37 | ConfigListener(TokenConfig); 38 | GetUpdates(); 39 | -------------------------------------------------------------------------------- /Commands/Utility/Friendcount.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { CodeBlock } from "../../Utils/MessageUtils"; 5 | import { HandleError } from "../../Utils/ErrorUtils"; 6 | 7 | export default { 8 | name: "friendcount", 9 | description: "Get the total number of friends on Discord.", 10 | usage: "friendcount", 11 | execute: async (client: ClientInit, message: Message, args: string[]) => { 12 | try { 13 | if (!client.user) { 14 | await message.reply(CodeBlock("Client is not fully initialized yet.")); 15 | return; 16 | } 17 | 18 | const friends = client.relationships.friendCache; 19 | const friendCount = friends.size; 20 | 21 | const msgContent = `Friend Information:\n- Total Friends: ${friendCount}`; 22 | 23 | await message.reply(CodeBlock(msgContent)); 24 | 25 | } catch (error) { 26 | await HandleError(error, exports.default.name, message); 27 | } 28 | }, 29 | }; 30 | -------------------------------------------------------------------------------- /Handlers/TokenChecker.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "discord.js-selfbot-v13"; 2 | import { Logger } from "../Utils/Logger"; 3 | 4 | export const CheckToken = async (tokens: string[]) => { 5 | Logger.log(`Checking ${tokens.length} token(s)...`); 6 | const validTokens: string[] = []; 7 | const invalidTokens: string[] = []; 8 | 9 | for (const token of tokens) { 10 | const partialToken = `${token.substring(0, 6)}...${token.substring(token.length - 4)}`; 11 | try { 12 | const client = new Client(); 13 | await Promise.race([ 14 | client.login(token), 15 | new Promise((_, reject) => setTimeout(() => reject(new Error('Login timed out')), 15000)) 16 | ]); 17 | validTokens.push(partialToken); 18 | client.destroy(); 19 | } catch (error: any) { 20 | invalidTokens.push(partialToken); 21 | } 22 | } 23 | Logger.log(`Token check complete. Valid: ${validTokens.length}.`); 24 | if (validTokens.length === 0 && tokens.length > 0) { 25 | Logger.error("CRITICAL: No valid tokens found. Exiting."); 26 | process.exit(1); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /Commands/Basic/Calculate.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { SendTempRep, CodeBlock } from "../../Utils/MessageUtils"; 5 | 6 | export default { 7 | name: "calculate", 8 | description: "Calculates a simple math expression (use with caution).", 9 | usage: "calculate ", 10 | execute: async (client: ClientInit, message: Message, args: string[]) => { 11 | 12 | if (args.length === 0) { 13 | await SendTempRep(message, `Please provide a math expression. Usage: ${client.prefix}${exports.default.usage}`); 14 | return; 15 | } 16 | 17 | const expression = args.join(" "); 18 | 19 | try { 20 | const result = eval(expression); 21 | 22 | if (typeof result === "number" && !isNaN(result)) { 23 | await message.reply(`Result: ${CodeBlock(result.toString())}`); 24 | } else { 25 | await SendTempRep(message, "Invalid math expression or result is not a number."); 26 | } 27 | } catch (evalError) { 28 | Logger.error(`Eval error in calculate command: ${evalError}`); 29 | await SendTempRep(message, "Error calculating expression. Please check your input."); 30 | } 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /Commands/Moderation/Ban.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { EnsureGC, ParseMbr, CheckBanable } from "../../Utils/DiscordUtils"; 4 | import { HandleError } from "../../Utils/ErrorUtils"; 5 | import { SafeDelCmd, SendTempRep } from "../../Utils/MessageUtils"; 6 | 7 | export default { 8 | name: "ban", 9 | description: "Ban a member from the server", 10 | usage: "ban <@user/userid> [reason]", 11 | execute: async (client: ClientInit, message: Message, args: string[]) => { 12 | try { 13 | if (!await EnsureGC(message)) return; 14 | 15 | const memberToBan = await ParseMbr(message, args[0]); 16 | if (!memberToBan) { 17 | await SendTempRep(message, `Invalid user mention or ID. Usage: ${client.prefix}${exports.default.usage}`); 18 | return; 19 | } 20 | 21 | if (!await CheckBanable(message, memberToBan)) return; 22 | 23 | const reason = args.slice(1).join(" ") || "No reason provided"; 24 | await memberToBan.ban({ reason }); 25 | 26 | await message.channel.send( 27 | `Banned ${memberToBan.user.tag} | Reason: ${reason}` 28 | ); 29 | SafeDelCmd(message); 30 | 31 | } catch (error) { 32 | await HandleError(error, exports.default.name, message, "Failed to ban member."); 33 | } 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /Commands/Moderation/Kick.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | 5 | import { EnsureGC, ParseMbr, CheckKickable } from "../../Utils/DiscordUtils"; 6 | import { HandleError } from "../../Utils/ErrorUtils"; 7 | import { SafeDelCmd } from "../../Utils/MessageUtils"; 8 | 9 | export default { 10 | name: "kick", 11 | description: "Kick a member from the server (using utilities)", 12 | usage: "kick <@user/userid> [reason]", 13 | execute: async (client: ClientInit, message: Message, args: string[]) => { 14 | try { 15 | if (!await EnsureGC(message)) { 16 | return; 17 | } 18 | 19 | const memberToKick = await ParseMbr(message, args[0]); 20 | if (!memberToKick) { 21 | await message.reply(`Invalid user mention or ID. Usage: ${client.prefix}${exports.default.usage}`); 22 | return; 23 | } 24 | 25 | if (!await CheckKickable(message, memberToKick)) { 26 | return; 27 | } 28 | 29 | const reason = args.slice(1).join(" ") || "No reason provided"; 30 | await memberToKick.kick(reason); 31 | await message.channel.send( 32 | `Kicked ${memberToKick.user.tag} | Reason: ${reason}`, 33 | ); 34 | 35 | SafeDelCmd(message); 36 | 37 | } catch (error) { 38 | await HandleError(error, exports.default.name, message); 39 | } 40 | }, 41 | }; -------------------------------------------------------------------------------- /Commands/Basic/AFK.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { HandleError } from "../../Utils/ErrorUtils"; 5 | import { SendTempRep } from "../../Utils/MessageUtils"; 6 | 7 | export default { 8 | name: "afk", 9 | description: "Sets your status to AFK or removes it.", 10 | usage: "afk [reason]", 11 | execute: async (client: ClientInit, message: Message, args: string[]) => { 12 | try { 13 | const userId = message.author.id; 14 | 15 | if (client.afkStatus.has(userId)) { 16 | client.afkStatus.delete(userId); 17 | try { 18 | await client.user?.setAFK(false); 19 | } catch (discordError) { 20 | Logger.warn(`Could not automatically unset Discord AFK status: ${discordError}`); 21 | } 22 | await SendTempRep(message, "You are no longer AFK."); 23 | 24 | } else { 25 | const reason = args.join(" ") || "No reason provided."; 26 | client.afkStatus.set(userId, reason); 27 | try { 28 | await client.user?.setAFK(true); 29 | } catch (discordError) { 30 | Logger.warn(`Could not automatically set Discord AFK status: ${discordError}`); 31 | } 32 | await SendTempRep(message, `You are now AFK. Reason: ${reason}`); 33 | } 34 | } catch (error) { 35 | await HandleError(error, exports.default.name, message); 36 | } 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /Commands/Moderation/Unban.ts: -------------------------------------------------------------------------------- 1 | import { Client, Message } from "discord.js-selfbot-v13"; 2 | import { Logger } from "../../Utils/Logger"; 3 | 4 | export default { 5 | name: "unban", 6 | description: "Unban a user from the server", 7 | usage: "unban [userID]", 8 | execute: async (client: Client, message: Message, args: string[]) => { 9 | try { 10 | if (!message.guild) { 11 | await message.reply("This command can only be used in a server"); 12 | return; 13 | } 14 | 15 | if (!args[0]) { 16 | await message.reply( 17 | "Usage: !unban [userID]\nExample: !unban 123456789", 18 | ); 19 | return; 20 | } 21 | 22 | try { 23 | await message.guild.bans.fetch(args[0]); 24 | } catch { 25 | await message.reply("Ts user is not banned or the ID is invalid"); 26 | return; 27 | } 28 | 29 | try { 30 | await message.guild.members.unban(args[0]); 31 | await message.channel.send( 32 | `✅ Successfully unbanned user with ID: ${args[0]}`, 33 | ); 34 | } catch (error) { 35 | Logger.error(`Failed to unban user: ${error}`); 36 | await message.reply( 37 | "Failed to unban user. They may not be banned or I may not have permission", 38 | ); 39 | } 40 | } catch (error) { 41 | Logger.error(`Error in unban command: ${error}`); 42 | await message.reply("An error occurred while trying to unban the user"); 43 | } 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /Utils/GitHubNews.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import chalk from "chalk"; 3 | import { Logger } from "./Logger"; 4 | 5 | async function GetUpdates() { 6 | const URL = `https://raw.githubusercontent.com/Mikasuru/KukuriClient/refs/heads/News/news`; 7 | try { 8 | const Res = await axios.get(URL); 9 | const Content = Res.data as string; 10 | Parse(Content); 11 | } catch (error) { 12 | Logger.error(`Failed to fetch bot updates`); 13 | } 14 | } 15 | 16 | function Parse(content: string) { 17 | const Lines = content.split("\n").filter((line) => line.trim() !== ""); 18 | let CurrDate = ""; 19 | let News: string[] = []; 20 | 21 | for (const line of Lines) { 22 | if (line.startsWith("[ Date ]")) { 23 | if (CurrDate && News.length > 0) { 24 | Display(CurrDate, News); 25 | News = []; 26 | } 27 | CurrDate = Lines[Lines.indexOf(line) + 1]?.trim() || ""; 28 | } else if (line.startsWith("[ News ]")) { 29 | continue; 30 | } else if (CurrDate) { 31 | News.push(line.trim()); 32 | } 33 | } 34 | 35 | if (CurrDate && News.length > 0) { 36 | Display(CurrDate, News); 37 | } 38 | } 39 | 40 | function Display(date: string, news: string[]) { 41 | console.log(chalk.magenta.bold("---------- [ Getting News ] ----------")); 42 | Logger.news("header", `Update on ${date}:`); 43 | news.forEach((item) => Logger.news("", `- ${item}`)); 44 | console.log(chalk.magenta.bold("------------ [ Starting ] ------------")); 45 | } 46 | 47 | export { GetUpdates }; 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Kukuri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | --- 24 | 25 | Additional Terms 26 | - You must provide appropriate credit to the original author (Kukuri). 27 | - You are NOT ALLOWED TO SELL this software or any modified version of it. 28 | - You are **STRICTLY PROHIBITED FROM EMBEDDDING ANY MALWARE, BACKDOORS, OR ANY MALICIOUS CODE. 29 | - Violating these terms will result in a permanent ban from the community. 30 | -------------------------------------------------------------------------------- /Commands/Moderation/TotalBans.ts: -------------------------------------------------------------------------------- 1 | import { Client, Message } from "discord.js-selfbot-v13"; 2 | import { Logger } from "../../Utils/Logger"; 3 | 4 | export default { 5 | name: "totalbans", 6 | description: "Check total bans in server", 7 | usage: "totalbans", 8 | execute: async (client: Client, message: Message, args: string[]) => { 9 | try { 10 | if (!message.guild) { 11 | await message.reply("This command can only be used in a server"); 12 | return; 13 | } 14 | 15 | const Msg = await message.channel.send("🔄 Fetching ban list..."); 16 | try { 17 | const bans = await message.guild.bans.fetch(); 18 | 19 | if (bans.size === 0) { 20 | await Msg.edit("No banned users found in this server"); 21 | return; 22 | } 23 | 24 | const Recent = Array.from(bans.values()).slice(0, 10); 25 | const BanList = Recent.map((ban, index) => { 26 | const Reason = ban.reason?.length ? ban.reason : "No reason provided"; 27 | return `${index + 1}. ${ban.user.tag} (${ban.user.id})\n Reason: ${Reason}`; 28 | }).join("\n\n"); 29 | 30 | await Msg.edit( 31 | `Total Bans: ${bans.size}\n\nRecent Bans:\n${BanList}` + 32 | (bans.size > 10 ? "\n\nShowing 10 most recent bans" : ""), 33 | ); 34 | } catch (error) { 35 | Logger.error(`Error fetching bans: ${error}`); 36 | await Msg.edit("Failed to fetch ban list"); 37 | } 38 | } catch (error) { 39 | Logger.error(`Error in totalbans command: ${error}`); 40 | await message.reply("An error occurred while fetching server bans"); 41 | } 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /Commands/Moderation/Slowmode.ts: -------------------------------------------------------------------------------- 1 | import { Client, Message, TextChannel } from "discord.js-selfbot-v13"; 2 | import { Logger } from "../../Utils/Logger"; 3 | 4 | export default { 5 | name: "slowmode", 6 | description: "Set channel slowmode duration", 7 | usage: "slowmode [duration] [#channel]", 8 | execute: async (client: Client, message: Message, args: string[]) => { 9 | try { 10 | if (!message.guild) { 11 | await message.reply("This command can only be used in a server"); 12 | return; 13 | } 14 | 15 | if (!args[0]) { 16 | await message.reply( 17 | "Usage: !slowmode [duration] [#channel]\nDuration in seconds, or 0 to disable", 18 | ); 19 | return; 20 | } 21 | 22 | const duration = parseInt(args[0]); 23 | if (isNaN(duration) || duration < 0 || duration > 21600) { 24 | await message.reply( 25 | "Please provide a valid duration between 0 and 21600 seconds (6 hours)", 26 | ); 27 | return; 28 | } 29 | 30 | const channel = (message.mentions.channels.first() || 31 | message.channel) as TextChannel; 32 | 33 | if (!channel.isText()) { 34 | await message.reply("Slowmode can only be set in text channels"); 35 | return; 36 | } 37 | 38 | await channel.setRateLimitPerUser(duration); 39 | await message.channel.send( 40 | duration === 0 41 | ? `Slowmode has been disabled in ${channel}` 42 | : `Slowmode has been set to ${duration} seconds in ${channel}`, 43 | ); 44 | } catch (error) { 45 | Logger.error(`Error in slowmode command: ${error}`); 46 | await message.reply("An error occurred while setting slowmode"); 47 | } 48 | }, 49 | }; 50 | -------------------------------------------------------------------------------- /Commands/Utility/AutoReact.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { SendTempRep, CodeBlock } from "../../Utils/MessageUtils"; 5 | import { HandleError } from "../../Utils/ErrorUtils"; 6 | 7 | export default { 8 | name: "autoreact", 9 | description: "Automatically reacts to messages with a specified emoji.", 10 | usage: "autoreact [emoji]", 11 | execute: async (client: ClientInit, message: Message, args: string[]) => { 12 | try { 13 | const subCmd = args[0]?.toLowerCase(); 14 | 15 | if (subCmd === "on") { 16 | const emoji = args[1]; 17 | if (!emoji) { 18 | await SendTempRep(message, CodeBlock(`Please provide an emoji. Usage: ${client.prefix}autoreact on `)); 19 | return; 20 | } 21 | if (emoji.length > 5 && !emoji.match(//)) { 22 | await SendTempRep(message, CodeBlock(`"${emoji}" doesn't look like a valid emoji.`)); 23 | return; 24 | } 25 | 26 | client.autoReactConfig.enabled = true; 27 | client.autoReactConfig.emoji = emoji; 28 | await SendTempRep(message, `Auto react enabled with emoji: ${emoji}`); 29 | 30 | } else if (subCmd === "off") { 31 | client.autoReactConfig.enabled = false; 32 | client.autoReactConfig.emoji = ""; 33 | await SendTempRep(message, "Auto react disabled."); 34 | 35 | } else { 36 | await SendTempRep(message, CodeBlock(`Invalid subcommand. Usage: ${client.prefix}${exports.default.usage}`)); 37 | } 38 | } catch (error) { 39 | await HandleError(error, exports.default.name, message); 40 | } 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /Utils/ErrorUtils.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { Logger } from "./Logger"; 3 | 4 | /** 5 | * Handles errors occurring within command execution by logging and sending a generic reply. 6 | * @param error The error object caught (expected to be 'unknown'). 7 | * @param commandName The name of the command where the error occurred. 8 | * @param message The message object that triggered the command. 9 | * @param replyMessage Optional custom error message for the user. 10 | */ 11 | export async function HandleError( 12 | error: unknown, 13 | commandName: string, 14 | message: Message, 15 | replyMessage: string = `An error occurred while executing the '${commandName}' command.` 16 | ): Promise { 17 | let errorMessage = 'Unknown error'; 18 | let errorStack = ''; 19 | 20 | if (error instanceof Error) { 21 | errorMessage = error.message; 22 | errorStack = error.stack || ''; 23 | } else if (typeof error === 'string') { 24 | errorMessage = error; 25 | } else if (typeof error === 'object' && error !== null) { 26 | errorMessage = String((error as any).message || JSON.stringify(error)); 27 | } 28 | 29 | Logger.error(`Error executing command '${commandName}': ${errorMessage}\n${errorStack}`); 30 | 31 | try { 32 | await message.reply(replyMessage).catch(err => { 33 | const replyErr = err instanceof Error ? err.message : String(err); 34 | Logger.error(`Failed to send error reply: ${replyErr}`); 35 | }); 36 | } catch (replyError) { 37 | const replyFailErr = replyError instanceof Error ? replyError.message : String(replyError); 38 | Logger.error(`Failed to send error reply after command error: ${replyFailErr}`); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Commands/Raiding/spam.ts: -------------------------------------------------------------------------------- 1 | import { Client, Message } from "discord.js-selfbot-v13"; 2 | import { Logger } from "../../Utils/Logger"; 3 | 4 | export default { 5 | name: "spam", 6 | description: 7 | "Spam a message a specified number of times with a delay between each message.", 8 | usage: 9 | "spam \nExample: !spam \"Hello!\" 5 1000 (spams 'Hello!' 5 times, 1 second apart)", 10 | execute: async (client: Client, message: Message, args: string[]) => { 11 | try { 12 | if (args.length < 3) { 13 | await message.reply( 14 | '```\nUsage: !spam \nExample: !spam "Hello!" 5 1000\n```', 15 | ); 16 | return; 17 | } 18 | 19 | const SpamMsg = args[0]; 20 | const Amt = parseInt(args[1]); 21 | const Dur = parseInt(args[2]); 22 | 23 | if (isNaN(Amt) || isNaN(Dur) || Amt <= 0 || Dur < 0) { 24 | await message.reply( 25 | "```\nAmount must be a positive number and Duration must be a non-negative number.\n```", 26 | ); 27 | return; 28 | } 29 | 30 | if (Amt > 50) { 31 | await message.reply( 32 | "```\nAmount cannot exceed 50 for safety reasons.\n```", 33 | ); 34 | return; 35 | } 36 | 37 | await message.reply("```\nStarting spam...\n```"); 38 | 39 | const spam = async (count: number) => { 40 | if (count >= Amt) return; 41 | 42 | await message.channel.send(SpamMsg); 43 | setTimeout(() => spam(count + 1), Dur); 44 | }; 45 | 46 | await spam(0); 47 | } catch (error) { 48 | Logger.error(`Error in spam command: ${(error as Error).message}`); 49 | await message.reply("```\n❌ An error occurred while spamming\n```"); 50 | } 51 | }, 52 | }; 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Kukuri Client

2 |

3 | A free and open-source Discord selfbot with powerful features 4 |

5 |

6 | GitHub stars 7 |

8 | 9 | --- 10 | 11 |

✨Community

12 |

13 | Hey! I've create a Community server, Feel free to join!
14 | If you have some issue, You can join my discord server.
15 | Kukuri Client | Community (New) 16 |

17 | 18 | --- 19 | 20 |

🚀 What is Kukuri Client?

21 |

22 | Kukuri Client is a selfbot I created after noticing that many users purchase selfbots with a lot of useless commands. 23 | Kukuri Client is different. It's free, open-source, and focuses on providing only the most useful features you need. 24 | Don't worry, Kukuri Client won't betray you! 25 |

26 | 27 |

28 | I've seen that many people want to try selfbots, but unfortunately, many of them have fallen victim to hacking by using free programs. 29 | That's why Kukuri Client is here! 30 |

31 | 32 | --- 33 | 34 |

📦 Getting Started

35 |

1️⃣ Install Dependencies

36 |
37 | bun install
38 | 
39 | 40 |

2️⃣ Setting up

41 |
42 | bun run token
43 | 
44 | 45 |

3️⃣ Run Kukuri Client

46 |
47 | bun run cli
48 | 
49 | 50 |

🤝 Contributing

51 |

Contributions are always welcome! If you have improvements, bug fixes, or new features in mind, feel free to submit a pull request. All contributions will go through a short review before being merged.

52 | 53 |

💖 Support the Project

You can help Kukuri Client grow by:

54 | -------------------------------------------------------------------------------- /Commands/Basic/help.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { HandleError } from "../../Utils/ErrorUtils"; 4 | import { CodeBlock, SendTempRep } from "../../Utils/MessageUtils"; 5 | 6 | export default { 7 | name: "help", 8 | description: "Lists all available commands or details of a specific command.", 9 | usage: "help [command]", 10 | execute: async (client: ClientInit, message: Message, args: string[]) => { 11 | try { 12 | const prefix = client.prefix; 13 | const commands = client.commands; 14 | 15 | if (args.length === 0) { 16 | // List all commands 17 | if (commands.size === 0) { 18 | await SendTempRep(message, CodeBlock("No commands available.")); 19 | return; 20 | } 21 | 22 | let helpMessage = "Available Commands:\n\n"; 23 | commands.forEach(cmd => { 24 | helpMessage += `${prefix}${cmd.name}: ${cmd.description}\n`; 25 | }); 26 | helpMessage += `\nUse ${prefix}help for more details.`; 27 | 28 | await message.reply(CodeBlock(helpMessage)); // Use CodeBlock for output 29 | 30 | } else { 31 | const commandName = args[0].toLowerCase(); 32 | const command = commands.get(commandName); 33 | 34 | if (!command) { 35 | await SendTempRep(message, CodeBlock(`Command "${commandName}" not found.\nUse ${prefix}help to see all available commands.`)); 36 | return; 37 | } 38 | 39 | const detailMessage = 40 | `Command: ${command.name}\n` + 41 | `Description: ${command.description}\n` + 42 | `Usage: ${prefix}${command.usage}`; 43 | 44 | await message.reply(CodeBlock(detailMessage)); 45 | } 46 | 47 | } catch (error) { 48 | await HandleError(error, exports.default.name, message, "An error occurred while showing help information."); 49 | } 50 | }, 51 | }; 52 | -------------------------------------------------------------------------------- /Commands/Moderation/Clear.ts: -------------------------------------------------------------------------------- 1 | import { Message, TextChannel, DMChannel } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { FetchChMsg, SafeDelMsg, SendTempRep, SafeDelCmd } from "../../Utils/MessageUtils"; 4 | import { HandleError } from "../../Utils/ErrorUtils"; 5 | import { ParseIntArg } from "../../Utils/MiscUtils"; 6 | 7 | export default { 8 | name: "clear", 9 | description: "Deletes a specified number of your own messages in the chat.", 10 | usage: "clear ", 11 | execute: async (client: ClientInit, message: Message, args: string[]) => { 12 | try { 13 | const amountToDelete = ParseIntArg(args[0], 1, 100); 14 | 15 | if (amountToDelete === null) { 16 | await SendTempRep(message, `Invalid amount. Usage: ${client.prefix}${exports.default.usage}`); 17 | return; 18 | } 19 | 20 | if (!(message.channel instanceof TextChannel || message.channel instanceof DMChannel)) { 21 | await SendTempRep(message, "Cannot clear messages in this type of channel."); 22 | return; 23 | } 24 | const fetchedMessages = await FetchChMsg(message.channel, 100); 25 | 26 | if (!fetchedMessages) { 27 | await SendTempRep(message, "Could not fetch messages."); 28 | return; 29 | } 30 | 31 | const ownMessages = Array.from(fetchedMessages.values()) 32 | .filter(msg => msg.author.id === client.user?.id && msg.id !== message.id) 33 | .slice(0, amountToDelete); 34 | 35 | if (ownMessages.length === 0) { 36 | await SendTempRep(message, "No recent messages found to delete."); 37 | return; 38 | } 39 | 40 | let deletedCount = 0; 41 | for (const msgToDelete of ownMessages) { 42 | await SafeDelMsg(msgToDelete); 43 | deletedCount++; 44 | } 45 | 46 | await SendTempRep(message, `Successfully deleted ${deletedCount} message(s).`); 47 | SafeDelCmd(message, 3000); 48 | 49 | } catch (error) { 50 | await HandleError(error, exports.default.name, message); 51 | } 52 | }, 53 | }; -------------------------------------------------------------------------------- /Utils/MiscUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates a Promise-based delay. 3 | * @param ms Duration in milliseconds. 4 | */ 5 | export function delay(ms: number): Promise { 6 | return new Promise(resolve => setTimeout(resolve, ms)); 7 | } 8 | 9 | /** 10 | * Generates a random integer within the range [min, max] (inclusive). 11 | * @param min Minimum value. 12 | * @param max Maximum value. 13 | * @returns A random integer. 14 | */ 15 | export function getRandomInt(min: number, max: number): number { 16 | min = Math.ceil(min); 17 | max = Math.floor(max); 18 | return Math.floor(Math.random() * (max - min + 1)) + min; 19 | } 20 | 21 | /** 22 | * Picks a random element from an array. 23 | * @param array The array to pick from. 24 | * @returns A random element from the array, or undefined if the array is empty. 25 | */ 26 | export function getRandomElement(array: T[]): T | undefined { 27 | if (array.length === 0) return undefined; 28 | return array[Math.floor(Math.random() * array.length)]; 29 | } 30 | 31 | /** 32 | * Truncates text to a maximum length, adding '...' if it exceeds the limit. 33 | * @param text The input text. 34 | * @param maxLength Maximum length allowed. 35 | * @returns The truncated text. 36 | */ 37 | export function truncateText(text: string, maxLength: number): string { 38 | if (text.length <= maxLength) { 39 | return text; 40 | } 41 | return text.slice(0, maxLength - 3) + "..."; 42 | } 43 | 44 | /** 45 | * Parses an integer from a string argument, optionally validating range. 46 | * @param arg The string argument to parse. 47 | * @param min Optional minimum allowed value (inclusive). 48 | * @param max Optional maximum allowed value (inclusive). 49 | * @returns The parsed integer, or null if parsing fails or value is out of range. 50 | */ 51 | export function ParseIntArg(arg: string | undefined, min?: number, max?: number): number | null { 52 | if (arg === undefined) return null; 53 | const num = parseInt(arg); 54 | if (isNaN(num)) return null; 55 | if (min !== undefined && num < min) return null; 56 | if (max !== undefined && num > max) return null; 57 | return num; 58 | } 59 | -------------------------------------------------------------------------------- /Commands/Fun/Hack.ts: -------------------------------------------------------------------------------- 1 | import { Client, Message } from "discord.js-selfbot-v13"; 2 | 3 | export default { 4 | name: "hack", 5 | description: "Hacks a user.", 6 | usage: "hack [user]", 7 | execute: async (client: Client, message: Message, args: string[]) => { 8 | try { 9 | function delay(ms: number): Promise { 10 | return new Promise(resolve => setTimeout(resolve, ms)); 11 | } 12 | 13 | const user = message.mentions.users.first() || message.author; 14 | const msg = await message.channel.send('```🔍 Start hacking...```'); 15 | 16 | await msg.edit('```Connecting to mainframe... [▓░░░░░░░░░] 10%```'); 17 | await delay(1000); 18 | await msg.edit('```Accessing user data... [▓▓▓░░░░░░░] 30%```'); 19 | await delay(1000); 20 | await msg.edit('```Downloading files... [▓▓▓▓▓░░░░░] 50%```'); 21 | await delay(1000); 22 | await msg.edit('```Breaking encryption... [▓▓▓▓▓▓▓░░░] 70%```'); 23 | await delay(1000); 24 | await msg.edit('```Completing process... [▓▓▓▓▓▓▓▓▓░] 90%```'); 25 | await delay(1000); 26 | 27 | const passwd = [ 28 | 'iLovePizza123', 29 | 'password123', 30 | 'qwerty12345', 31 | 'letmein2024', 32 | 'iAmHacker99' 33 | ]; 34 | 35 | const email = `${user.username.toLowerCase()}${Math.floor(Math.random() * 999)}@email.com`; 36 | const uIp = `${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`; 37 | const upwd = passwd[Math.floor(Math.random() * passwd.length)]; 38 | 39 | const hkmsg = [ 40 | '```ml', 41 | 'HACK COMPLETE!', 42 | '===================================', 43 | `👤 "Hacked" User: ${user.tag}`, 44 | `📧 "Found" Email: ${email}`, 45 | `🔑 "Cracked" Password: ${upwd}`, 46 | `🌐 "IP Address": ${uIp}`, 47 | `📍 Location: Probably Earth 🌍`, 48 | '```' 49 | ].join('\n'); 50 | 51 | await msg.edit(hkmsg); 52 | 53 | } catch (error) { 54 | console.error(`Error in hack command: ${(error as Error).message}`); 55 | await message.reply('```❌ Error running hack command```'); 56 | } 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /Commands/Basic/Speedtest.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import axios from "axios"; 4 | import { Logger } from "../../Utils/Logger"; 5 | import { HandleError } from "../../Utils/ErrorUtils"; 6 | import { SendEditRep, CodeBlock } from "../../Utils/MessageUtils"; 7 | 8 | export default { 9 | name: "speedtest", 10 | description: 11 | "Tests internet download speed using Cloudflare.", 12 | usage: "speedtest", 13 | execute: async (client: ClientInit, message: Message, args: string[]) => { 14 | let editMessage: Message | null = null; 15 | try { 16 | editMessage = await SendEditRep(message, "Running internet speed test... Please wait."); 17 | if (!editMessage) { 18 | Logger.error("Failed to send initial speedtest message."); 19 | return; 20 | } 21 | 22 | const startTime = Date.now(); 23 | const response = await axios.get( 24 | "https://speed.cloudflare.com/__down?bytes=50000000", // 50MB 25 | { 26 | responseType: "arraybuffer", 27 | timeout: 20000, 28 | }, 29 | ); 30 | const endTime = Date.now(); 31 | 32 | const durationSeconds = (endTime - startTime) / 1000; 33 | const sizeMegabits = (response.data.byteLength / (1024 * 1024)) * 8; 34 | 35 | if (durationSeconds === 0) { 36 | await editMessage.edit(CodeBlock("Speed test duration too short, unable to calculate speed.")); 37 | return; 38 | } 39 | 40 | const downloadSpeedMbps = (sizeMegabits / durationSeconds).toFixed(2); // Mbps 41 | 42 | const resultText = 43 | `📊 Internet Speed Test Results (Cloudflare):\n\n` + 44 | `- Download: ${downloadSpeedMbps} Mbps\n` + 45 | `- Time Taken: ${durationSeconds.toFixed(2)} s\n` + 46 | `- Data Size: ${(sizeMegabits / 8).toFixed(2)} MB\n\n` + 47 | `Note: Upload/Ping tests not available.`; 48 | 49 | await editMessage.edit(CodeBlock(resultText)); 50 | 51 | } catch (error) { 52 | const errorMsg = `Failed to run speed test. Check connection or Cloudflare status.`; 53 | if (editMessage) { 54 | try { 55 | await editMessage.edit(CodeBlock(errorMsg)); 56 | } catch (editError) { 57 | await HandleError(error, exports.default.name, message, errorMsg); 58 | } 59 | } else { 60 | await HandleError(error, exports.default.name, message, errorMsg); 61 | } 62 | Logger.error(`Speedtest error: ${error}`); 63 | } 64 | }, 65 | }; 66 | -------------------------------------------------------------------------------- /Commands/Moderation/Nick.ts: -------------------------------------------------------------------------------- 1 | import { Message, GuildMember } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { EnsureGC, ParseMbr, CheckManageable } from "../../Utils/DiscordUtils"; 5 | import { getRandomElement } from "../../Utils/MiscUtils"; 6 | import { SendTempRep, CodeBlock } from "../../Utils/MessageUtils"; 7 | import { HandleError } from "../../Utils/ErrorUtils"; 8 | 9 | const adjectives = ["Sigma", "Maxxing", "Alpha", "Skibidi", "Ohio", "Rizzler"]; 10 | const nouns = ["Toilet", "Skibidi", "Wolf", "Kukuri"]; // Fun names 11 | 12 | export default { 13 | name: "nick", 14 | description: "Manage user nicknames", 15 | usage: "nick <@user/userid> [nickname]", 16 | execute: async (client: ClientInit, message: Message, args: string[]) => { 17 | try { 18 | if (!await EnsureGC(message)) return; 19 | 20 | const action = args[0]?.toLowerCase(); 21 | const targetArg = args[1]; 22 | const potentialNick = args.slice(2).join(" "); 23 | 24 | if (!action || !targetArg || (action === "set" && !potentialNick)) { 25 | await SendTempRep(message, CodeBlock( 26 | `Invalid arguments. Usage:\n`+ 27 | `${client.prefix}nick set <@user/userid> \n` + 28 | `${client.prefix}nick reset <@user/userid>\n` + 29 | `${client.prefix}nick random <@user/userid>` 30 | ) 31 | ); 32 | return; 33 | } 34 | 35 | const member = await ParseMbr(message, targetArg); 36 | if (!member) { 37 | await SendTempRep(message, `Could not find the specified member.`); 38 | return; 39 | } 40 | 41 | if (!await CheckManageable(message, member, 'change nickname for')) return; 42 | 43 | switch (action) { 44 | case "set": { 45 | if (potentialNick.length > 32) { 46 | await SendTempRep(message, "Nickname must be 32 characters or less."); 47 | return; 48 | } 49 | await member.setNickname(potentialNick); 50 | await SendTempRep(message, `Changed ${member.user.username}'s nickname to "${potentialNick}".`); 51 | break; 52 | } 53 | case "reset": { 54 | const oldNick = member.nickname; 55 | await member.setNickname(null); 56 | await SendTempRep(message, `Reset ${member.user.username}'s nickname${oldNick ? ` from "${oldNick}"` : ""}.`); 57 | break; 58 | } 59 | case "random": { 60 | const randomAdj = getRandomElement(adjectives) || "Cool"; 61 | const randomNoun = getRandomElement(nouns) || "User"; 62 | const randomNick = `${randomAdj} ${randomNoun}`; 63 | await member.setNickname(randomNick); 64 | await SendTempRep(message, `Changed ${member.user.username}'s nickname to "${randomNick}".`); 65 | break; 66 | } 67 | default: { 68 | await SendTempRep(message, `Invalid action "${action}". Use 'set', 'reset', or 'random'.`); 69 | } 70 | } 71 | 72 | } catch (error) { 73 | await HandleError(error, exports.default.name, message, "An error occurred while managing nickname."); 74 | } 75 | }, 76 | }; 77 | -------------------------------------------------------------------------------- /Commands/Basic/Ascii.ts: -------------------------------------------------------------------------------- 1 | // Im headache with this command. 2 | import { Client, Message } from "discord.js-selfbot-v13"; 3 | import axios from "axios"; 4 | import sharp from "sharp"; 5 | 6 | const Chars = " .,:;i1tfLCG08@"; 7 | 8 | export default { 9 | name: "ascii", 10 | description: "Convert anime character image to special ASCII art", 11 | usage: 'ascii anime \nExample: !ascii anime "Yue"', 12 | execute: async (client: Client, message: Message, args: string[]) => { 13 | try { 14 | if (args.length < 2 || args[0].toLowerCase() !== "anime") { 15 | await message.reply( 16 | '```\nUsage: !ascii anime \nExample: !ascii anime "Yue"\n```', 17 | ); 18 | return; 19 | } 20 | 21 | const CharName = args.slice(1).join(" "); 22 | const StatusMsg = await message.reply( 23 | "```\n🔍 Searching for character...\n```", 24 | ); 25 | 26 | const query = ` 27 | query ($search: String) { 28 | Character(search: $search) { 29 | image { 30 | large 31 | } 32 | name { 33 | full 34 | } 35 | } 36 | } 37 | `; 38 | 39 | const FuckingRes = await axios.post("https://graphql.anilist.co", { 40 | query, 41 | variables: { search: CharName }, 42 | }); 43 | 44 | const ImgURL = FuckingRes.data?.data?.Character?.image?.large; 45 | const FullName = FuckingRes.data?.data?.Character?.name?.full; 46 | 47 | if (!ImgURL) { 48 | await StatusMsg.edit("```\nCharacter not found\n```"); 49 | return; 50 | } 51 | 52 | await StatusMsg.edit("```\n🎨 Converting image to ASCII art...\n```"); 53 | 54 | const ImgRes = await axios.get(ImgURL, { 55 | responseType: "arraybuffer", 56 | }); 57 | const ImageBuff = Buffer.from(ImgRes.data); 58 | 59 | const ihatethis = 75; 60 | const godhelpme = Math.floor(ihatethis * 0.5); 61 | 62 | const PleaseProcess = await sharp(ImageBuff) 63 | .resize(ihatethis, godhelpme, { fit: "fill", position: "center" }) 64 | .grayscale() 65 | .normalize() 66 | .raw() 67 | .toBuffer(); 68 | 69 | let FuckingArt = ""; 70 | for (let y = 0; y < godhelpme; y++) { 71 | let row = ""; 72 | for (let x = 0; x < ihatethis; x++) { 73 | const pixel = PleaseProcess[y * ihatethis + x]; 74 | const CharIndex = Math.floor((pixel / 255) * (Chars.length - 1)); 75 | row += Chars[CharIndex]; 76 | } 77 | FuckingArt += row + "\n"; 78 | } 79 | 80 | const FinallyAResult = [ 81 | "```", 82 | `Character: ${FullName}`, 83 | "═".repeat(40), 84 | FuckingArt, 85 | "```", 86 | ].join("\n"); 87 | 88 | if (FinallyAResult.length > 4000) { 89 | await StatusMsg.edit( 90 | "```\nGenerated ASCII art is too large for Discord\n```", 91 | ); 92 | return; 93 | } 94 | 95 | await StatusMsg.edit(FinallyAResult); 96 | } catch (error) { 97 | console.error(`Error in ascii command: ${(error as Error).message}`); 98 | await message.reply("```\nAn error occurred\n```"); 99 | } 100 | }, 101 | }; 102 | -------------------------------------------------------------------------------- /Commands/Utility/Presence.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { SendTempRep, CodeBlock } from "../../Utils/MessageUtils"; 5 | import { HandleError } from "../../Utils/ErrorUtils"; 6 | 7 | const VaildActTypes = [ 8 | "PLAYING", "STREAMING", "LISTENING", "WATCHING", "CUSTOM", "COMPETING" 9 | ] as const; 10 | type ActivityType = typeof VaildActTypes[number]; 11 | 12 | const VaildPlatforms = [ 13 | "desktop", "web", "mobile" 14 | ] as const; 15 | type PlatformType = typeof VaildPlatforms[number]; 16 | 17 | const VaildStatus = ["online", "idle", "dnd"] as const; 18 | type StatusType = typeof VaildStatus[number]; 19 | 20 | const commandDefinition = { 21 | name: "presence", 22 | description: "Change your Discord presence (status, activity, platform).", 23 | usage: "presence | presence stop", 24 | execute: async (client: ClientInit, message: Message, args: string[]) => { 25 | const commandName = commandDefinition.name; 26 | try { 27 | if (args.length === 1 && args[0].toLowerCase() === "stop") { 28 | try { 29 | client.user?.setPresence({ 30 | activities: [], 31 | status: "online", 32 | }); 33 | await SendTempRep(message, "✅ Custom presence stopped (reset to default/online)."); 34 | } catch (presenceError) { 35 | Logger.error(`Failed to stop presence: ${presenceError}`); 36 | await SendTempRep(message, "⚠️ Could not fully reset presence, but cleared settings."); 37 | } 38 | return; 39 | } 40 | 41 | if (args.length < 4) { 42 | await SendTempRep(message, CodeBlock(`Invalid arguments. Usage: ${client.prefix}${commandDefinition.usage}`)); 43 | return; 44 | } 45 | 46 | const typeArg = args[0].toUpperCase(); 47 | const platformArg = args[1].toLowerCase(); 48 | const statusArg = args[2].toLowerCase(); 49 | const name = args.slice(3).join(" "); 50 | 51 | if (!(VaildActTypes as ReadonlyArray).includes(typeArg)) { 52 | await SendTempRep(message, CodeBlock(`Invalid type "${typeArg}".\nAvailable: ${VaildActTypes.join(", ")}`)); 53 | return; 54 | } 55 | const type = typeArg as ActivityType; 56 | 57 | if (!(VaildPlatforms as ReadonlyArray).includes(platformArg)) { 58 | await SendTempRep(message, CodeBlock(`Invalid platform "${platformArg}".\nAvailable: ${VaildPlatforms.join(", ")}`)); 59 | return; 60 | } 61 | const platform = platformArg as PlatformType; 62 | 63 | if (!(VaildStatus as ReadonlyArray).includes(statusArg)) { 64 | await SendTempRep(message, CodeBlock(`Invalid status "${statusArg}".\nAvailable: ${VaildStatus.join(", ")}`)); 65 | return; 66 | } 67 | const status = statusArg as StatusType; 68 | 69 | client.user?.setPresence({ 70 | activities: [{ 71 | name: name, 72 | type: type as any, 73 | }], 74 | status: status, 75 | }); 76 | 77 | await SendTempRep(message, `Presence updated: ${type} "${name}" on ${platform} (${status})`); 78 | 79 | } catch (error) { 80 | await HandleError(error, commandName, message, "An error occurred while updating presence."); 81 | } 82 | }, 83 | }; 84 | 85 | export default commandDefinition; -------------------------------------------------------------------------------- /Commands/Moderation/CloseDM.ts: -------------------------------------------------------------------------------- 1 | import { Message, DMChannel } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { ParseUsr } from "../../Utils/DiscordUtils"; 5 | import { SendEditRep, CodeBlock } from "../../Utils/MessageUtils"; 6 | import { HandleError } from "../../Utils/ErrorUtils"; 7 | import { delay } from "../../Utils/MiscUtils"; 8 | 9 | export default { 10 | name: "closedm", 11 | description: "Close DM channels with a user or all users", 12 | usage: "closedm <@user/userid | all>", 13 | execute: async (client: ClientInit, message: Message, args: string[]) => { 14 | let editMessage: Message | null = null; 15 | try { 16 | if (!args[0]) { 17 | await message.reply(CodeBlock(`Usage: ${client.prefix}${exports.default.usage}\nExample: ${client.prefix}closedm @user or ${client.prefix}closedm all`)); 18 | return; 19 | } 20 | 21 | const targetArg = args[0].toLowerCase(); 22 | 23 | if (targetArg === "all") { 24 | editMessage = await SendEditRep(message, "Closing all DM channels..."); 25 | if (!editMessage) return; 26 | 27 | const dmChannels = client.channels.cache.filter( 28 | (channel): channel is DMChannel => channel instanceof DMChannel 29 | ); 30 | let closedCount = 0; 31 | 32 | for (const [, channel] of dmChannels) { 33 | try { 34 | if (channel.recipient) { 35 | await channel.delete(); 36 | closedCount++; 37 | await delay(200); // Small delay 38 | } 39 | } catch (error) { 40 | Logger.error(`Error closing DM channel ${channel.id} with ${channel.recipient?.tag}: ${error}`); 41 | } 42 | } 43 | await editMessage.edit(CodeBlock(`Successfully closed ${closedCount} DM channels.`)); 44 | 45 | } else { 46 | const targetUser = await ParseUsr(client, args[0], message); 47 | if (!targetUser || targetUser.id === message.author.id) { 48 | await message.reply(CodeBlock("Invalid user mention or ID provided.")); 49 | return; 50 | } 51 | 52 | editMessage = await SendEditRep(message, `Closing DM channel with ${targetUser.tag}...`); 53 | if (!editMessage) return; 54 | 55 | const dmChannel = client.channels.cache.find( 56 | ch => ch instanceof DMChannel && ch.recipient?.id === targetUser.id 57 | ) as DMChannel | undefined; 58 | 59 | 60 | if (dmChannel) { 61 | try { 62 | await dmChannel.delete(); 63 | await editMessage.edit(CodeBlock(`Successfully closed DM channel with ${targetUser.tag}`)); 64 | } catch (error) { 65 | Logger.error(`Error closing DM channel with ${targetUser.tag}: ${error}`); 66 | await editMessage.edit(CodeBlock(`Failed to close DM channel with ${targetUser.tag}. It might already be closed or an error occurred.`)); 67 | } 68 | } else { 69 | await editMessage.edit(CodeBlock(`Could not find an active DM channel with ${targetUser.tag}.`)); 70 | } 71 | } 72 | } catch (error) { 73 | const errorMsg = "An error occurred while closing DM channels."; 74 | if (editMessage) { 75 | try { await editMessage.edit(CodeBlock(errorMsg)); } catch {} 76 | } 77 | await HandleError(error, exports.default.name, message, errorMsg); 78 | } 79 | }, 80 | }; -------------------------------------------------------------------------------- /Commands/Fun/Ascii.disabled: -------------------------------------------------------------------------------- 1 | // Im headache with this command. 2 | import { Client, Message } from "discord.js-selfbot-v13"; 3 | import axios from "axios"; 4 | import sharp from "sharp"; 5 | 6 | const Chars = " .,:;i1tfLCG08@"; 7 | 8 | export default { 9 | name: "ascii", 10 | description: "Convert anime character image to special ASCII art", 11 | usage: 'ascii anime \nExample: !ascii anime "Yue"', 12 | execute: async (client: Client, message: Message, args: string[]) => { 13 | try { 14 | if (args.length < 2 || args[0].toLowerCase() !== "anime") { 15 | await message.reply( 16 | '```\nUsage: !ascii anime \nExample: !ascii anime "Yue"\n```', 17 | ); 18 | return; 19 | } 20 | 21 | const CharName = args.slice(1).join(" "); 22 | const StatusMsg = await message.reply( 23 | "```\n🔍 Searching for character...\n```", 24 | ); 25 | 26 | const query = ` 27 | query ($search: String) { 28 | Character(search: $search) { 29 | image { 30 | large 31 | } 32 | name { 33 | full 34 | } 35 | } 36 | } 37 | `; 38 | 39 | const FuckingRes = await axios.post("https://graphql.anilist.co", { 40 | query, 41 | variables: { search: CharName }, 42 | }); 43 | 44 | const ImgURL = FuckingRes.data?.data?.Character?.image?.large; 45 | const FullName = FuckingRes.data?.data?.Character?.name?.full; 46 | 47 | if (!ImgURL) { 48 | await StatusMsg.edit("```\nCharacter not found\n```"); 49 | return; 50 | } 51 | 52 | await StatusMsg.edit("```\n🎨 Converting image to ASCII art...\n```"); 53 | 54 | const ImgRes = await axios.get(ImgURL, { 55 | responseType: "arraybuffer", 56 | }); 57 | const ImageBuff = Buffer.from(ImgRes.data); 58 | 59 | const ihatethis = 75; 60 | const godhelpme = Math.floor(ihatethis * 0.5); 61 | 62 | const PleaseProcess = await sharp(ImageBuff) 63 | .resize(ihatethis, godhelpme, { fit: "fill", position: "center" }) 64 | .grayscale() 65 | .normalize() 66 | .raw() 67 | .toBuffer(); 68 | 69 | let FuckingArt = ""; 70 | for (let y = 0; y < godhelpme; y++) { 71 | let row = ""; 72 | for (let x = 0; x < ihatethis; x++) { 73 | const pixel = PleaseProcess[y * ihatethis + x]; 74 | const CharIndex = Math.floor((pixel / 255) * (Chars.length - 1)); 75 | row += Chars[CharIndex]; 76 | } 77 | FuckingArt += row + "\n"; 78 | } 79 | 80 | const FinallyAResult = [ 81 | "```", 82 | `Character: ${FullName}`, 83 | "═".repeat(40), 84 | FuckingArt, 85 | "```", 86 | ].join("\n"); 87 | 88 | if (FinallyAResult.length > 4000) { 89 | await StatusMsg.edit( 90 | "```\nGenerated ASCII art is too large for Discord\n```", 91 | ); 92 | return; 93 | } 94 | 95 | await StatusMsg.edit(FinallyAResult); 96 | } catch (error) { 97 | console.error(`Error in ascii command: ${(error as Error).message}`); 98 | await message.reply("```\nAn error occurred\n```"); 99 | } 100 | }, 101 | }; 102 | -------------------------------------------------------------------------------- /Commands/Utility/Information.ts: -------------------------------------------------------------------------------- 1 | import { Message } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import si from "systeminformation"; 4 | import os from "os"; 5 | import { Logger } from "../../Utils/Logger"; 6 | import { CodeBlock } from "../../Utils/MessageUtils"; 7 | import { HandleError } from "../../Utils/ErrorUtils"; 8 | 9 | async function getGpuInfo(): Promise { 10 | try { 11 | const gpuData = await si.graphics(); 12 | if (gpuData && gpuData.controllers && gpuData.controllers.length > 0) { 13 | return gpuData.controllers 14 | .map((gpu) => `${gpu.vendor || ''} ${gpu.model || 'Unknown GPU'}`.trim()) 15 | .join(", ") || "N/A"; 16 | } 17 | return "No GPU detected"; 18 | } catch (error) { 19 | Logger.warn(`Could not detect GPU: ${error}`); 20 | return "Unable to detect GPU"; 21 | } 22 | } 23 | 24 | function getHostingType(): string { 25 | const hostname = os.hostname().toLowerCase(); 26 | const virtualizationKeywords = ["vps", "vm", "cloud", "aws", "gcp", "azure", "kvm", "qemu", "virtualbox", "vmware", "hyper-v", "docker", "container"]; 27 | 28 | if (virtualizationKeywords.some((keyword) => hostname.includes(keyword) || (os.version && os.version().toLowerCase().includes(keyword)))) { 29 | return "Virtual Environment / Cloud / VPS"; 30 | } 31 | return "Local Machine / Unknown"; 32 | } 33 | 34 | 35 | export default { 36 | name: "information", 37 | description: "Get detailed information about the client and system.", 38 | usage: "information", 39 | execute: async (client: ClientInit, message: Message, args: string[]) => { 40 | try { 41 | // Uptime Calculation 42 | const uptimeSeconds = process.uptime(); 43 | const days = Math.floor(uptimeSeconds / (3600 * 24)); 44 | const hours = Math.floor((uptimeSeconds % (3600 * 24)) / 3600); 45 | const minutes = Math.floor((uptimeSeconds % 3600) / 60); 46 | const seconds = Math.floor(uptimeSeconds % 60); 47 | const uptimeString = `${days}d ${hours}h ${minutes}m ${seconds}s`; 48 | 49 | // System Info 50 | const bunVersion = typeof Bun !== 'undefined' ? Bun.version : "N/A"; // Check if Bun exists 51 | const platform = os.platform(); 52 | const release = os.release(); 53 | const arch = os.arch(); 54 | 55 | // Memory 56 | const totalMemGB = (os.totalmem() / 1024 ** 3).toFixed(2); 57 | const freeMemGB = (os.freemem() / 1024 ** 3).toFixed(2); 58 | 59 | // CPU Info 60 | const cpus = os.cpus(); 61 | const cpuModel = cpus.length > 0 ? cpus[0].model : "N/A"; 62 | const cpuCores = cpus.length; 63 | const cpuSpeed = cpus.length > 0 ? (cpus[0].speed / 1000).toFixed(2) : "N/A"; // Current speed 64 | 65 | let cpuMaxSpeed = "N/A"; 66 | try { 67 | const cpuData = await si.cpu(); 68 | cpuMaxSpeed = cpuData.speedMax ? cpuData.speedMax.toFixed(2) : cpuSpeed; 69 | } catch (siError) { 70 | Logger.warn(`Could not get max CPU speed from systeminformation: ${siError}`); 71 | cpuMaxSpeed = cpuSpeed; 72 | } 73 | 74 | 75 | const gpuInfo = await getGpuInfo(); 76 | const hostingType = getHostingType(); 77 | 78 | const infoContent = ` 79 | Client User: ${client.user?.tag || "Unknown"} 80 | 81 | [ BOT STATUS ] 82 | - Uptime : ${uptimeString} 83 | - Bun Version : ${bunVersion} 84 | 85 | [ SYSTEM INFO ] 86 | - OS Platform : ${platform} 87 | - OS Release : ${release} 88 | - Architecture : ${arch} 89 | - Hosting Type : ${hostingType} 90 | 91 | [ HARDWARE ] 92 | - CPU Model : ${cpuModel} 93 | - CPU Cores : ${cpuCores} 94 | - CPU Speed : ${cpuSpeed} GHz (Max: ${cpuMaxSpeed} GHz) 95 | - GPU(s) : ${gpuInfo} 96 | - Total Memory : ${totalMemGB} GB 97 | - Free Memory : ${freeMemGB} GB 98 | `; 99 | 100 | await message.reply(CodeBlock(infoContent.trim(), 'ini')); 101 | 102 | } catch (error) { 103 | await HandleError(error, exports.default.name, message); 104 | } 105 | }, 106 | }; 107 | -------------------------------------------------------------------------------- /Scripts/Token.ts: -------------------------------------------------------------------------------- 1 | import { execSync } from "child_process"; 2 | import { platform } from "os"; 3 | import { existsSync, readFileSync, writeFileSync } from "fs"; 4 | import * as readline from "readline"; 5 | 6 | const rl = readline.createInterface({ 7 | input: process.stdin, 8 | output: process.stdout, 9 | }); 10 | 11 | function isWindows(): boolean { 12 | return platform() === "win32"; 13 | } 14 | 15 | function commandExists(command: string): boolean { 16 | try { 17 | execSync(isWindows() ? `where ${command}` : `command -v ${command}`, { 18 | stdio: "ignore", 19 | }); 20 | return true; 21 | } catch { 22 | return false; 23 | } 24 | } 25 | 26 | function askQuestion(query: string): Promise { 27 | return new Promise((resolve) => rl.question(query, resolve)); 28 | } 29 | 30 | async function addManualToken() { 31 | const name = await askQuestion("Enter bot name: "); 32 | const prefix = await askQuestion("Enter prefix: "); 33 | const token = await askQuestion("Enter token: "); 34 | const allowUsersInput = await askQuestion( 35 | 'Enter allowed users (comma-separated) or "all": ', 36 | ); 37 | 38 | const allowUsers = 39 | allowUsersInput === "all" 40 | ? "all" 41 | : allowUsersInput.split(",").map((u) => u.trim()); 42 | 43 | const config = existsSync("./config/tokens.json") 44 | ? JSON.parse(readFileSync("./config/tokens.json", "utf8")) 45 | : {}; 46 | 47 | config[name] = { 48 | prefix, 49 | token, 50 | allowUsers 51 | /*, allowEveryone*/ 52 | }; 53 | writeFileSync("./config/tokens.json", JSON.stringify(config, null, 2)); 54 | 55 | console.log(`Token for ${name} added successfully.`); 56 | } 57 | 58 | async function addQRCodeToken() { 59 | if (!commandExists("bun")) { 60 | console.log("Bun is not installed. Please install Bun manually."); 61 | process.exit(1); 62 | } 63 | 64 | try { 65 | const { Client } = await import("discord.js-selfbot-v13"); 66 | const client = new Client(); 67 | 68 | client.on("qrCode", (qrCode) => { 69 | console.log("Please scan this QR code with your Discord mobile app:"); 70 | console.log(qrCode); 71 | }); 72 | 73 | client.on("ready", async () => { 74 | const token = client.token; 75 | const name = await askQuestion("Enter bot name for this token: "); 76 | const prefix = await askQuestion("Enter prefix: "); 77 | const allowUsersInput = await askQuestion( 78 | 'Enter allowed users (comma-separated) or "all": ', 79 | ); 80 | const allowEveryoneInput = await askQuestion( 81 | "Allow everyone? (yes/no): ", 82 | ); 83 | 84 | const allowUsers = 85 | allowUsersInput === "all" 86 | ? "all" 87 | : allowUsersInput.split(",").map((u) => u.trim()); 88 | const allowEveryone = allowEveryoneInput.toLowerCase() === "yes"; 89 | 90 | const config = existsSync("./config/tokens.json") 91 | ? JSON.parse(readFileSync("./config/tokens.json", "utf8")) 92 | : {}; 93 | 94 | config[name] = { prefix, token, allowUsers, allowEveryone }; 95 | writeFileSync("./config/tokens.json", JSON.stringify(config, null, 2)); 96 | 97 | console.log(`Token for ${name} added successfully via QR code.`); 98 | client.destroy(); 99 | rl.close(); 100 | process.exit(0); 101 | }); 102 | 103 | await client.QRLogin(); 104 | } catch (error) { 105 | console.log("Failed to generate QR code:", error.message); 106 | process.exit(1); 107 | } 108 | } 109 | 110 | async function manageTokens() { 111 | if (!commandExists("bun")) { 112 | console.log("Bun is not installed. Please install Bun manually."); 113 | process.exit(1); 114 | } 115 | 116 | const addToken = await askQuestion("Do you want to add a token? (yes/no): "); 117 | if (addToken.toLowerCase() !== "yes") { 118 | console.log("No token added. Exiting..."); 119 | rl.close(); 120 | return; 121 | } 122 | 123 | const method = await askQuestion("Choose method (1 = Manual, 2 = QR Code): "); 124 | if (method === "1") { 125 | await addManualToken(); 126 | } else if (method === "2") { 127 | await addQRCodeToken(); 128 | } else { 129 | console.log("Invalid method selected. Exiting..."); 130 | } 131 | 132 | rl.close(); 133 | } 134 | 135 | manageTokens(); 136 | -------------------------------------------------------------------------------- /Commands/Moderation/DeleteDM.ts: -------------------------------------------------------------------------------- 1 | import { Message, DMChannel } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../../Types/Client"; 3 | import { Logger } from "../../Utils/Logger"; 4 | import { ParseUsr } from "../../Utils/DiscordUtils"; 5 | import { SendEditRep, FetchChMsg, SafeDelMsg, CodeBlock } from "../../Utils/MessageUtils"; 6 | import { HandleError } from "../../Utils/ErrorUtils"; 7 | import { delay } from "../../Utils/MiscUtils"; 8 | 9 | export default { 10 | name: "deletedm", 11 | description: "Delete your messages in DMs with a user or all users", 12 | usage: "deletedm <@user/userid | all>", 13 | execute: async (client: ClientInit, message: Message, args: string[]) => { 14 | let editMessage: Message | null = null; 15 | try { 16 | if (!args[0]) { 17 | await message.reply(CodeBlock(`Usage: ${client.prefix}${exports.default.usage}\nExample: ${client.prefix}deletedm @user or ${client.prefix}deletedm all`)); 18 | return; 19 | } 20 | 21 | const targetArg = args[0].toLowerCase(); 22 | 23 | if (targetArg === "all") { 24 | editMessage = await SendEditRep(message, "Deleting all your previous DM messages..."); 25 | if (!editMessage) return; 26 | 27 | const dmChannels = client.channels.cache.filter( 28 | (channel): channel is DMChannel => channel instanceof DMChannel // Type guard 29 | ); 30 | let totalDeletedCount = 0; 31 | let checkedChannels = 0; 32 | 33 | for (const [, channel] of dmChannels) { 34 | checkedChannels++; 35 | const messages = await FetchChMsg(channel, 100); 36 | if (messages) { 37 | let deletedInChannel = 0; 38 | for (const msg of messages.values()) { 39 | if (msg.author.id === client.user?.id) { 40 | await SafeDelMsg(msg); 41 | deletedInChannel++; 42 | totalDeletedCount++; 43 | await delay(150); // Small delay 44 | } 45 | } 46 | if (deletedInChannel > 0) { 47 | Logger.log(`Deleted ${deletedInChannel} messages in DM with ${channel.recipient?.tag || 'Unknown User'}`); 48 | } 49 | } 50 | if (checkedChannels % 10 === 0) { 51 | await editMessage.edit(CodeBlock(`Checked ${checkedChannels}/${dmChannels.size} DMs... Deleted ${totalDeletedCount} messages so far...`)); 52 | } 53 | } 54 | await editMessage.edit(CodeBlock(`Finished deleting. Total messages deleted: ${totalDeletedCount}`)); 55 | 56 | } else { 57 | const targetUser = await ParseUsr(client, args[0], message); 58 | if (!targetUser || targetUser.id === message.author.id) { 59 | await message.reply(CodeBlock("Invalid user mention or ID provided.")); 60 | return; 61 | } 62 | 63 | editMessage = await SendEditRep(message, `Deleting your messages in DM with ${targetUser.tag}...`); 64 | if (!editMessage) return; 65 | 66 | const dmChannel = client.channels.cache.find( 67 | ch => ch instanceof DMChannel && ch.recipient?.id === targetUser.id 68 | ) as DMChannel | undefined; 69 | 70 | if (dmChannel) { 71 | const messages = await FetchChMsg(dmChannel, 100); 72 | let deletedCount = 0; 73 | if (messages) { 74 | for (const msg of messages.values()) { 75 | if (msg.author.id === client.user?.id) { 76 | await SafeDelMsg(msg); 77 | deletedCount++; 78 | await delay(150); // Small delay 79 | } 80 | } 81 | } 82 | await editMessage.edit(CodeBlock(`Finished deleting. Deleted ${deletedCount} messages in DM with ${targetUser.tag}.`)); 83 | } else { 84 | await editMessage.edit(CodeBlock(`Could not find an active DM channel with ${targetUser.tag} to delete messages from.`)); 85 | } 86 | } 87 | } catch (error) { 88 | const errorMsg = "An error occurred while deleting DM messages."; 89 | if (editMessage) { 90 | try { await editMessage.edit(CodeBlock(errorMsg)); } catch {} 91 | } 92 | await HandleError(error, exports.default.name, message, errorMsg); 93 | } 94 | }, 95 | }; -------------------------------------------------------------------------------- /Utils/MessageUtils.ts: -------------------------------------------------------------------------------- 1 | import { Message, TextChannel, DMChannel } from "discord.js-selfbot-v13"; 2 | import { Logger } from "./Logger"; // Assuming Logger is in the same directory 3 | 4 | /** 5 | * Sends a temporary reply message that will be deleted after a specified duration. 6 | * @param message The original message to reply to. 7 | * @param content The content of the reply. 8 | * @param duration Duration in milliseconds before deleting the reply. Default is 3000ms. 9 | */ 10 | export async function SendTempRep(message: Message, content: string, duration: number = 3000): Promise { 11 | try { 12 | const reply = await message.reply(content); 13 | if (reply && reply.deletable) { 14 | setTimeout(() => { 15 | reply.delete().catch(err => { 16 | // Handle deletion error 17 | const errMsg = (err instanceof Error) ? err.message : String(err); 18 | Logger.warn(`Failed to delete temporary reply: ${errMsg}`); 19 | }); 20 | }, duration); 21 | } 22 | } catch (error) { 23 | // Handle sending error 24 | const errMsg = (error instanceof Error) ? error.message : String(error); 25 | Logger.error(`Failed to send temporary reply: ${errMsg}`); 26 | } 27 | } 28 | 29 | /** 30 | * Sends a reply message that can be edited later. 31 | * @param message The original message to reply to. 32 | * @param initialContent The initial content of the reply message. 33 | * @returns The sent Message object, or null if sending failed. 34 | */ 35 | export async function SendEditRep(message: Message, initialContent: string): Promise { 36 | try { 37 | const reply = await message.reply(initialContent); 38 | return reply; 39 | } catch (error) { 40 | // Handle sending error 41 | const errMsg = (error instanceof Error) ? error.message : String(error); 42 | Logger.error(`Failed to send editable reply: ${errMsg}`); 43 | return null; 44 | } 45 | } 46 | 47 | /** 48 | * Safely deletes a message (doesn't throw an error if deletion fails). 49 | * @param message The message to delete. 50 | * @param reason Optional reason for audit logs (if applicable). 51 | */ 52 | export async function SafeDelMsg(message: Message | null | undefined, reason?: string): Promise { 53 | if (message && message.deletable) { 54 | try { 55 | await message.delete(); 56 | } catch (error) { 57 | // Handle deletion error 58 | const errMsg = (error instanceof Error) ? error.message : String(error); 59 | Logger.warn(`Failed to safely delete message ${message.id}: ${errMsg}`); 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Safely deletes the command invocation message. 66 | * @param message The command message to delete. 67 | * @param delay Delay in milliseconds before deleting. Default is 0. 68 | */ 69 | export function SafeDelCmd(message: Message, delay: number = 0): void { 70 | if (delay > 0) { 71 | setTimeout(() => SafeDelMsg(message), delay); 72 | } else { 73 | SafeDelMsg(message); 74 | } 75 | } 76 | 77 | 78 | /** 79 | * Fetches messages in a channel with error handling. 80 | * @param channel The channel to fetch messages from. 81 | * @param limit Maximum number of messages to fetch (default 100). 82 | * @returns A collection of messages, or null if fetching failed. 83 | */ 84 | export async function FetchChMsg(channel: TextChannel | DMChannel, limit: number = 100): Promise | null> { 85 | try { 86 | const messages = await channel.messages.fetch({ limit }); 87 | return messages; 88 | } catch (error) { 89 | // Handle fetching error 90 | const errMsg = (error instanceof Error) ? error.message : String(error); 91 | Logger.error(`Failed to fetch messages in channel ${channel.id}: ${errMsg}`); 92 | return null; 93 | } 94 | } 95 | 96 | /** 97 | * Formats content into a Markdown code block. 98 | * @param content Message content inside the code block. 99 | * @param language Code block language ('ts', 'js', 'json', or leave blank). 100 | * @returns The formatted code block string. 101 | */ 102 | export function CodeBlock(content: string, language?: string): string { 103 | return `\`\`\`${language || ''}\n${content}\n\`\`\``; 104 | } 105 | -------------------------------------------------------------------------------- /Types/Client.ts: -------------------------------------------------------------------------------- 1 | import { Client, Message } from "discord.js-selfbot-v13"; 2 | import fs from "fs"; 3 | import path from "path"; 4 | import { Logger } from "../Utils/Logger"; 5 | import { SendTempRep } from "../Utils/MessageUtils"; 6 | 7 | interface Command { 8 | name: string; 9 | description: string; 10 | usage: string; 11 | execute: (client: ClientInit, message: Message, args: string[]) => void | Promise; 12 | } 13 | 14 | export class ClientInit extends Client { 15 | prefix: string; 16 | allowUsers: string[] | "all"; 17 | commands: Map = new Map(); 18 | 19 | afkStatus: Map = new Map(); 20 | autoReactConfig: { enabled: boolean; emoji: string } = { enabled: false, emoji: "" }; 21 | 22 | constructor( 23 | prefix: string, 24 | allowUsers: string[] | "all" 25 | ) { 26 | super(); 27 | this.prefix = prefix; 28 | this.allowUsers = allowUsers; 29 | this.LoadCommands(); 30 | } 31 | 32 | async start(token: string) { 33 | try { 34 | await this.login(token); 35 | Logger.log(`Logged in as ${this.user?.tag}`); 36 | 37 | this.on("messageCreate", (message) => { 38 | message.mentions.users.forEach(mentionedUser => { 39 | if (this.afkStatus.has(mentionedUser.id)) { 40 | if (message.author.id !== this.user?.id && message.author.id !== mentionedUser.id) { 41 | const reason = this.afkStatus.get(mentionedUser.id) || "AFK"; 42 | SendTempRep(message, `User ${mentionedUser.tag} is currently AFK. Reason: ${reason}`, 5000) 43 | .catch(Logger.error); 44 | } 45 | } 46 | }); 47 | 48 | if (this.autoReactConfig.enabled && this.autoReactConfig.emoji) { 49 | if(message.author.id !== this.user?.id) { 50 | message.react(this.autoReactConfig.emoji).catch((err) => { 51 | Logger.warn(`Failed to auto-react: ${err}`); 52 | }); 53 | } 54 | } 55 | this.HandleMessage(message); 56 | 57 | }); 58 | // -------------------------------------------------------------- 59 | 60 | } catch (error) { 61 | Logger.error(`Failed to login for token associated with prefix "${this.prefix}". Error: ${error}`); 62 | } 63 | } 64 | 65 | LoadCommands() { 66 | const commandsDir = path.join(__dirname, "../Commands"); 67 | 68 | const loadDir = (dir: string) => { 69 | try { 70 | const items = fs.readdirSync(dir, { withFileTypes: true }); 71 | for (const item of items) { 72 | const fullPath = path.join(dir, item.name); 73 | if (item.isDirectory()) { 74 | loadDir(fullPath); // Recursively load subdirectories 75 | } else if (item.isFile() && (item.name.endsWith(".ts") || item.name.endsWith(".js"))) { 76 | try { 77 | const commandModule = require(fullPath); 78 | const command: Command = commandModule.default || commandModule; 79 | 80 | if (command && command.name && typeof command.execute === 'function') { 81 | this.commands.set(command.name.toLowerCase(), command); 82 | Logger.log(`Loaded command: ${command.name}`); 83 | } else { 84 | Logger.warn(`Skipping invalid command file: ${item.name} (missing name, description, usage, or execute function)`); 85 | } 86 | } catch (error) { 87 | Logger.error(`Failed to load command from ${item.name}: ${error}`); 88 | } 89 | } 90 | } 91 | } catch (error) { 92 | Logger.error(`Failed to read command directory ${dir}: ${error}`); 93 | } 94 | }; 95 | 96 | loadDir(commandsDir); 97 | Logger.log(`Total commands loaded: ${this.commands.size}`); 98 | } 99 | 100 | 101 | HandleMessage(Msg: Message) { 102 | if (Msg.author.id !== this.user?.id) return; 103 | if (!Msg.content.startsWith(this.prefix)) return; 104 | 105 | const Args = Msg.content.slice(this.prefix.length).trim().split(/\s+/); 106 | const CmdName = Args.shift()?.toLowerCase(); 107 | 108 | if (!CmdName) return; 109 | 110 | if (this.allowUsers !== "all" && !this.allowUsers.includes(Msg.author.id)) { 111 | return; 112 | } 113 | 114 | const command = this.commands.get(CmdName); 115 | if (!command) return; 116 | 117 | try { 118 | command.execute(this, Msg, Args); 119 | } catch (error) { 120 | Logger.error(`Error executing command ${CmdName}: ${error}`); 121 | Msg.reply(`❌ An error occurred while executing command: ${CmdName}`).catch(Logger.error); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Commands/Protection/DeleteInvite.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Message, 3 | TextChannel, 4 | DMChannel 5 | } from "discord.js-selfbot-v13"; 6 | import { ClientInit } from "../../Types/Client"; 7 | import { Logger } from "../../Utils/Logger"; 8 | import { SendEditRep, FetchChMsg, SafeDelMsg, CodeBlock } from "../../Utils/MessageUtils"; 9 | import { HandleError } from "../../Utils/ErrorUtils"; 10 | import { delay } from "../../Utils/MiscUtils"; 11 | 12 | export default { 13 | name: "deleteinvite", 14 | description: 15 | "Deletes your messages containing URLs (including Discord invites) sent within the last 7 days across accessible servers and DMs.", 16 | usage: "deleteinvite", 17 | execute: async (client: ClientInit, message: Message, args: string[]) => { 18 | let editMessage: Message | null = null; 19 | try { 20 | if (!client.user) { 21 | await message.reply(CodeBlock("Client is not fully initialized yet.")); 22 | return; 23 | } 24 | 25 | editMessage = await SendEditRep(message, CodeBlock("Checking for your messages containing URLs (last 7 days)...")); 26 | if (!editMessage) return; 27 | 28 | const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000; 29 | const urlRegex = /(discord\.gg\/[a-zA-Z0-9]+|https?:\/\/[^\s]+)/i; 30 | let totalDeletedCount = 0; 31 | let checkedGuilds = 0; 32 | let checkedDMs = 0; 33 | 34 | const guilds = Array.from(client.guilds.cache.values()); 35 | Logger.log(`Checking ${guilds.length} guilds...`); 36 | 37 | for (const guild of guilds) { 38 | checkedGuilds++; 39 | if (checkedGuilds % 5 === 0 || checkedGuilds === guilds.length) { 40 | await editMessage.edit(CodeBlock(`Checked ${checkedGuilds}/${guilds.length} guilds... Deleted ${totalDeletedCount} messages so far...`)).catch(Logger.warn); 41 | } 42 | 43 | const textChannels = guild.channels.cache.filter( 44 | (ch): ch is TextChannel => ch instanceof TextChannel 45 | ); 46 | 47 | for (const [, channel] of textChannels) { 48 | try { 49 | const messages = await FetchChMsg(channel, 100); 50 | if (!messages) continue; 51 | 52 | const messagesToDelete = Array.from(messages.values()).filter( 53 | (msg) => 54 | msg.author.id === client.user?.id && 55 | urlRegex.test(msg.content) && 56 | msg.createdTimestamp > sevenDaysAgo 57 | ); 58 | 59 | for (const msg of messagesToDelete) { 60 | await SafeDelMsg(msg); 61 | totalDeletedCount++; 62 | await delay(150); 63 | } 64 | } catch (channelError) { 65 | Logger.warn(`Could not process channel ${channel.name} (${channel.id}) in guild ${guild.name}: ${channelError}`); 66 | } 67 | } 68 | await delay(300); 69 | } 70 | 71 | const dmChannels = client.channels.cache.filter( 72 | (ch): ch is DMChannel => ch instanceof DMChannel 73 | ); 74 | Logger.log(`Checking ${dmChannels.size} DM channels...`); 75 | await editMessage.edit(CodeBlock(`Finished guilds. Now checking ${dmChannels.size} DMs... Deleted ${totalDeletedCount} messages so far...`)).catch(Logger.warn); 76 | 77 | 78 | for (const [, channel] of dmChannels) { 79 | checkedDMs++; 80 | if (checkedDMs % 20 === 0 || checkedDMs === dmChannels.size) { 81 | await editMessage.edit(CodeBlock(`Checked ${checkedDMs}/${dmChannels.size} DMs... Deleted ${totalDeletedCount} messages so far...`)).catch(Logger.warn); 82 | } 83 | 84 | try { 85 | const messages = await FetchChMsg(channel, 100); 86 | if (!messages) continue; 87 | 88 | const messagesToDelete = Array.from(messages.values()).filter( 89 | (msg) => 90 | msg.author.id === client.user?.id && 91 | urlRegex.test(msg.content) && 92 | msg.createdTimestamp > sevenDaysAgo 93 | ); 94 | 95 | for (const msg of messagesToDelete) { 96 | await SafeDelMsg(msg); 97 | totalDeletedCount++; 98 | await delay(150); 99 | } 100 | } catch (dmError) { 101 | Logger.warn(`Could not process DM channel ${channel.id}: ${dmError}`); 102 | } 103 | } 104 | 105 | const finalMessage = totalDeletedCount === 0 106 | ? "No messages containing URLs found from you within the last 7 days." 107 | : `Finished! Deleted ${totalDeletedCount} message(s) containing URLs.`; 108 | await editMessage.edit(CodeBlock(finalMessage)); 109 | Logger.log(finalMessage); 110 | 111 | } catch (error) { 112 | const errorMsg = "An error occurred while checking/deleting messages."; 113 | if (editMessage) { 114 | try { await editMessage.edit(CodeBlock(errorMsg)); } catch {} 115 | } 116 | await HandleError(error, exports.default.name, message, errorMsg); 117 | } 118 | }, 119 | }; 120 | -------------------------------------------------------------------------------- /Commands/Utility/Library.ts: -------------------------------------------------------------------------------- 1 | import { Client, Message } from "discord.js-selfbot-v13"; 2 | import fs from "fs"; 3 | import path from "path"; 4 | import { Logger } from "../../Utils/Logger"; 5 | 6 | interface LibraryEntry { 7 | LibID: number; 8 | Content: string; 9 | JumpBabyJump?: string; 10 | } 11 | 12 | const LibPath = path.join(__dirname, "../../Memory/library.json"); 13 | 14 | let library: LibraryEntry[] = []; 15 | if (fs.existsSync(LibPath)) { 16 | try { 17 | library = JSON.parse(fs.readFileSync(LibPath, "utf-8")); 18 | } catch (error) { 19 | Logger.error(`Error reading library.json: ${(error as Error).message}`); 20 | library = []; 21 | } 22 | } 23 | let nextId = 24 | library.length > 0 ? Math.max(...library.map((e) => e.LibID)) + 1 : 1; 25 | 26 | const SaveLib = () => { 27 | const dir = path.dirname(LibPath); 28 | try { 29 | if (!fs.existsSync(dir)) { 30 | fs.mkdirSync(dir, { recursive: true }); 31 | } 32 | fs.writeFileSync(LibPath, JSON.stringify(library, null, 2)); 33 | } catch (error) { 34 | Logger.error(`Error saving library: ${(error as Error).message}`); 35 | throw error; 36 | } 37 | }; 38 | 39 | export default { 40 | name: "library", 41 | description: "Manage a personal message library (like personal pins).", 42 | usage: 43 | "library [add | add (reply) | remove | search | (no args to list)]", 44 | execute: async (client: Client, message: Message, args: string[]) => { 45 | try { 46 | if (!client.user) { 47 | await message.channel.send( 48 | "```\nClient is not fully initialized yet\n```", 49 | ); 50 | return; 51 | } 52 | 53 | if (args.length === 0) { 54 | if (library.length === 0) { 55 | await message.channel.send("```\n📚 Your library is empty\n```"); 56 | return; 57 | } 58 | const LibList = library 59 | .map((entry) => { 60 | const jump = entry.JumpBabyJump 61 | ? ` [Jump](${entry.JumpBabyJump})` 62 | : ""; 63 | return `[${entry.LibID}]: ${entry.Content}${jump}`; 64 | }) 65 | .join("\n"); 66 | await message.channel.send( 67 | `\`\`\`\n📚 Your Library:\n${LibList}\n\`\`\``, 68 | ); 69 | return; 70 | } 71 | 72 | if (args[0].toLowerCase() === "add") { 73 | if (args.length === 1) { 74 | if (message.reference && message.reference.messageId) { 75 | const RepliedMsg = await message.channel.messages.fetch( 76 | message.reference.messageId, 77 | ); 78 | if (!RepliedMsg) { 79 | await message.reply( 80 | "```\nCould not fetch the replied message\n```", 81 | ); 82 | return; 83 | } 84 | 85 | library.push({ 86 | LibID: nextId++, 87 | Content: RepliedMsg.content, 88 | JumpBabyJump: RepliedMsg.url, 89 | }); 90 | SaveLib(); 91 | await message.reply( 92 | `\`\`\`\n✅ Added to library as ID ${nextId - 1}\n\`\`\``, 93 | ); 94 | } else { 95 | await message.reply( 96 | "```\nUsage: !library add or reply to a message\n```", 97 | ); 98 | } 99 | return; 100 | } 101 | 102 | const Content = args.slice(1).join(" "); 103 | library.push({ 104 | LibID: nextId++, 105 | Content, 106 | }); 107 | SaveLib(); 108 | await message.reply( 109 | `\`\`\`\n✅ Added to library as ID ${nextId - 1}\n\`\`\``, 110 | ); 111 | return; 112 | } 113 | 114 | if (args[0].toLowerCase() === "remove" && args.length === 2) { 115 | const Id2RM = parseInt(args[1]); 116 | const index = library.findIndex((entry) => entry.LibID === Id2RM); 117 | 118 | if (index === -1) { 119 | await message.reply("```\nLibrary entry with that ID not found\n```"); 120 | return; 121 | } 122 | 123 | library.splice(index, 1); 124 | SaveLib(); 125 | await message.reply( 126 | `\`\`\`\n✅ Removed library entry ID ${Id2RM}\n\`\`\``, 127 | ); 128 | return; 129 | } 130 | 131 | if (args[0].toLowerCase() === "search" && args.length >= 2) { 132 | const keyword = args.slice(1).join(" ").toLowerCase(); 133 | const matches = library.filter((entry) => 134 | entry.Content.toLowerCase().includes(keyword), 135 | ); 136 | 137 | if (matches.length === 0) { 138 | await message.reply("```\n🔍 No matches found in your library\n```"); 139 | return; 140 | } 141 | 142 | const matchList = matches 143 | .map((entry) => { 144 | const jump = entry.JumpBabyJump 145 | ? ` [Jump](${entry.JumpBabyJump})` 146 | : ""; 147 | return `[${entry.LibID}]: ${entry.Content}${jump}`; 148 | }) 149 | .join("\n"); 150 | 151 | await message.reply(`\`\`\`\n🔍 Search Results:\n${matchList}\n\`\`\``); 152 | return; 153 | } 154 | 155 | await message.reply( 156 | "```\nUsage: !library [add | add (reply) | remove | search | (no args to list)]\n```", 157 | ); 158 | } catch (error) { 159 | Logger.error(`Error in library command: ${(error as Error).message}`); 160 | await message.reply("```\nAn error occurred\n```"); 161 | } 162 | }, 163 | }; 164 | -------------------------------------------------------------------------------- /Utils/DiscordUtils.ts: -------------------------------------------------------------------------------- 1 | import { Message, User, GuildMember, TextChannel, Channel } from "discord.js-selfbot-v13"; 2 | import { ClientInit } from "../Types/Client"; 3 | import { Logger } from "./Logger"; 4 | 5 | /** 6 | * Attempts to find a User from an argument (mention or ID), or defaults to the message author. 7 | * @param client Client instance. 8 | * @param arg Argument string (mention or ID). 9 | * @param message The original message object. 10 | * @returns The found User object or the message author. 11 | */ 12 | export async function ParseUsr(client: ClientInit, arg: string | undefined, message: Message): Promise { 13 | if (!arg) return message.author; 14 | 15 | const mentions = message.mentions.users; 16 | if (mentions.size > 0) { 17 | const firstMention = mentions.first(); 18 | if (firstMention) return firstMention; 19 | } 20 | 21 | try { 22 | const fetchedUser = await client.users.fetch(arg); 23 | if (fetchedUser) return fetchedUser; 24 | } catch { 25 | // Ignore error if ID is invalid 26 | } 27 | 28 | return message.author; 29 | } 30 | 31 | /** 32 | * Attempts to find a GuildMember from an argument (mention or ID). Must be used within a Guild context. 33 | * @param message The original message object (must be in a guild). 34 | * @param arg Argument string (mention or ID). 35 | * @returns The found GuildMember object, or null if not found or not in a guild. 36 | */ 37 | export async function ParseMbr(message: Message, arg: string | undefined): Promise { 38 | if (!message.guild || !arg) return null; 39 | 40 | const mentions = message.mentions.members; 41 | if (mentions && mentions.size > 0) { 42 | const firstMention = mentions.first(); 43 | if (firstMention) return firstMention; 44 | } 45 | 46 | try { 47 | const fetchedMember = await message.guild.members.fetch(arg); 48 | if (fetchedMember) return fetchedMember; 49 | } catch { 50 | // Ignore error if ID is invalid 51 | } 52 | 53 | return null; 54 | } 55 | 56 | /** 57 | * Ensures the command is used within a Guild (Server) context. Sends a reply if not. 58 | * @param message The message object. 59 | * @param replyMessage Optional custom message to send if not in a guild. 60 | * @returns True if in a guild, false otherwise. 61 | */ 62 | export async function EnsureGC(message: Message, replyMessage: string = "This command can only be used in a server."): Promise { 63 | if (!message.guild) { 64 | await message.reply(replyMessage).catch(err => { 65 | const errMsg = (err instanceof Error) ? err.message : String(err); 66 | Logger.error(`Failed to send guild context reply: ${errMsg}`); 67 | }); 68 | return false; 69 | } 70 | return true; 71 | } 72 | 73 | /** 74 | * Checks if the Client User can manage the specified Member (Kick, Ban, Nickname). 75 | * @param message The message object. 76 | * @param targetMember The GuildMember to check. 77 | * @param actionName Name of the action for logging/reply (e.g., 'kick', 'ban', 'manage nickname for'). 78 | * @returns True if manageable, false otherwise (sends reply on failure). 79 | */ 80 | export async function CheckManageable(message: Message, targetMember: GuildMember | null, actionName: string = 'manage'): Promise { 81 | if (!targetMember) { 82 | await message.reply("Please mention a valid member or provide a valid ID.").catch(err => { 83 | const errMsg = (err instanceof Error) ? err.message : String(err); 84 | Logger.error(`Failed to send manageable check reply (no target): ${errMsg}`); 85 | }); 86 | return false; 87 | } 88 | if (!targetMember.manageable) { 89 | await message.reply(`I cannot ${actionName} this member.`).catch(err => { 90 | const errMsg = (err instanceof Error) ? err.message : String(err); 91 | Logger.error(`Failed to send manageable check reply (not manageable): ${errMsg}`); 92 | }); 93 | return false; 94 | } 95 | return true; 96 | } 97 | 98 | /** 99 | * Checks if the Client User can kick the specified Member. 100 | * @param message The message object. 101 | * @param targetMember The GuildMember to check. 102 | * @returns True if kickable, false otherwise (sends reply on failure). 103 | */ 104 | export async function CheckKickable(message: Message, targetMember: GuildMember | null): Promise { 105 | if (!targetMember) { 106 | await message.reply("Please mention a valid member or provide a valid ID.").catch(err => { 107 | const errMsg = (err instanceof Error) ? err.message : String(err); 108 | Logger.error(`Failed to send kickable check reply (no target): ${errMsg}`); 109 | }); 110 | return false; 111 | } 112 | if (!targetMember.kickable) { 113 | await message.reply("I cannot kick this member.").catch(err => { 114 | const errMsg = (err instanceof Error) ? err.message : String(err); 115 | Logger.error(`Failed to send kickable check reply (not kickable): ${errMsg}`); 116 | }); 117 | return false; 118 | } 119 | return true; 120 | } 121 | 122 | /** 123 | * Checks if the Client User can ban the specified Member. 124 | * @param message The message object. 125 | * @param targetMember The GuildMember to check. 126 | * @returns True if bannable, false otherwise (sends reply on failure). 127 | */ 128 | export async function CheckBanable(message: Message, targetMember: GuildMember | null): Promise { 129 | if (!targetMember) { 130 | await message.reply("Please mention a valid member or provide a valid ID.").catch(err => { 131 | const errMsg = (err instanceof Error) ? err.message : String(err); 132 | Logger.error(`Failed to send bannable check reply (no target): ${errMsg}`); 133 | }); 134 | return false; 135 | } 136 | if (!targetMember.bannable) { 137 | await message.reply("I cannot ban this member.").catch(err => { 138 | const errMsg = (err instanceof Error) ? err.message : String(err); 139 | Logger.error(`Failed to send bannable check reply (not bannable): ${errMsg}`); 140 | }); 141 | return false; 142 | } 143 | return true; 144 | } 145 | --------------------------------------------------------------------------------