From e6979c1d5ae7a62b181bd9c1361ae58e8cc68580 Mon Sep 17 00:00:00 2001 From: MrSerge01 <86667481+MrSerge01@users.noreply.github.com> Date: Wed, 27 Nov 2024 22:19:14 +0500 Subject: [PATCH] wow uh yes --- src/bot.ts | 9 +- src/commands/Leaderboard.ts | 6 +- src/commands/moderation/Ban.ts | 9 +- src/commands/moderation/Delwarn.ts | 10 +- src/commands/moderation/Kick.ts | 3 +- src/commands/moderation/Mute.ts | 10 +- src/commands/moderation/Unban.ts | 2 +- src/commands/moderation/Unmute.ts | 2 +- src/events/guildCreate.ts | 16 ++- src/events/guildMemberAdd.ts | 6 +- src/events/interactionCreate.ts | 16 ++- src/handlers/commands.ts | 197 ++++++++++------------------- src/handlers/events.ts | 25 ++-- src/utils/embeds/modEmbed.ts | 33 ++--- 14 files changed, 147 insertions(+), 197 deletions(-) diff --git a/src/bot.ts b/src/bot.ts index bd8e19b..3a9eb41 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -1,6 +1,6 @@ import { ActivityType, Client } from "discord.js"; -import { Commands } from "./handlers/commands"; -import { Events } from "./handlers/events"; +import { registerCommands } from "./handlers/commands"; +import { loadEvents } from "./handlers/events"; import { rescheduleUnbans } from "./utils/unbanScheduler"; const client = new Client({ @@ -12,15 +12,14 @@ const client = new Client({ "GuildMembers", "GuildMessages", "GuildEmojisAndStickers", - "GuildPresences", "GuildBans", "MessageContent" ] }); client.on("ready", async () => { - await new Events(client).loadEvents(); - await new Commands(client).registerCommands(); + await loadEvents(client); + await registerCommands(client); console.log("ちーっす!"); rescheduleUnbans(client); }); diff --git a/src/commands/Leaderboard.ts b/src/commands/Leaderboard.ts index b07c8f3..b10e56d 100644 --- a/src/commands/Leaderboard.ts +++ b/src/commands/Leaderboard.ts @@ -38,12 +38,12 @@ export default class Leaderboard { else return b.xp - a.xp; }); - const totalPages = Math.ceil(leaderboardData.length / 5); + const totalPages = Math.ceil(leaderboardData.length / 6); let page = interaction.options.getNumber("page") || 1; page = Math.max(1, Math.min(page, totalPages)); const generateEmbed = async () => { - const start = (page - 1) * 5; - const end = start + 5; + const start = (page - 1) * 6; + const end = start + 6; const pageData = leaderboardData.slice(start, end); const embed = new EmbedBuilder() diff --git a/src/commands/moderation/Ban.ts b/src/commands/moderation/Ban.ts index 0dbc4de..9370e85 100644 --- a/src/commands/moderation/Ban.ts +++ b/src/commands/moderation/Ban.ts @@ -60,14 +60,13 @@ export default class Ban { } try { + await modEmbed( + { interaction, user, action: "Banned", duration, dm: true, dbAction: "BAN", expiresAt }, + reason + ); await guild.members.ban(user.id, { reason: reason ?? undefined }); } catch (err) { console.error("Failed to ban user:", err); } - - await modEmbed( - { interaction, user, action: "Banned", duration, dm: true, dbAction: "BAN", expiresAt }, - reason - ); } } diff --git a/src/commands/moderation/Delwarn.ts b/src/commands/moderation/Delwarn.ts index 88af050..2e610d2 100644 --- a/src/commands/moderation/Delwarn.ts +++ b/src/commands/moderation/Delwarn.ts @@ -38,7 +38,7 @@ export default class Delwarn { if ( await errorCheck( PermissionsBitField.Flags.ModerateMembers, - { interaction, user, action: "Remove a warn" }, + { interaction, user, action: "Remove a warning" }, { allErrors: true, botError: false }, "Moderate Members" ) @@ -46,7 +46,7 @@ export default class Delwarn { return; if (newWarns.length == warns.length) - return await errorEmbed(interaction, `There is no warn with the id of ${id}.`); + return await errorEmbed(interaction, `There is no warning with the id of ${id}.`); const embed = new EmbedBuilder() .setAuthor({ name: `• Removed a warning from ${name}`, iconURL: user.displayAvatarURL() }) @@ -65,6 +65,10 @@ export default class Delwarn { const dmChannel = (await user.createDM().catch(() => null)) as DMChannel | null; if (!dmChannel) return; if (user.bot) return; - await dmChannel.send({ embeds: [embed.setTitle("Your warning has been removed.")] }); + try { + await dmChannel.send({ embeds: [embed.setTitle("Your warning has been removed.")] }); + } catch (e) { + console.log(e); + } } } diff --git a/src/commands/moderation/Kick.ts b/src/commands/moderation/Kick.ts index c67bae8..44b18cf 100644 --- a/src/commands/moderation/Kick.ts +++ b/src/commands/moderation/Kick.ts @@ -40,11 +40,10 @@ export default class Kick { ); const reason = interaction.options.getString("reason"); + await modEmbed({ interaction, user, action: "Kicked", dm: true, dbAction: "KICK" }, reason); await interaction.guild?.members.cache .get(user.id) ?.kick(reason ?? undefined) .catch(error => console.error(error)); - - await modEmbed({ interaction, user, action: "Kicked", dm: true, dbAction: "KICK" }, reason); } } diff --git a/src/commands/moderation/Mute.ts b/src/commands/moderation/Mute.ts index b32ad71..8212cf1 100644 --- a/src/commands/moderation/Mute.ts +++ b/src/commands/moderation/Mute.ts @@ -52,15 +52,15 @@ export default class Mute { Date.parse(new Date().toISOString()) + Date.parse(new Date(ms(duration)).toISOString()) ).toISOString(); - await interaction.guild?.members.cache - .get(user.id) - ?.edit({ communicationDisabledUntil: time, reason: reason ?? undefined }) - .catch(error => console.error(error)); - await modEmbed( { interaction, user, action: "Muted", duration, dm: true, dbAction: "MUTE" }, reason, true ); + + await interaction.guild?.members.cache + .get(user.id) + ?.edit({ communicationDisabledUntil: time, reason: reason ?? undefined }) + .catch(error => console.error(error)); } } diff --git a/src/commands/moderation/Unban.ts b/src/commands/moderation/Unban.ts index a8d5d8e..bff235d 100644 --- a/src/commands/moderation/Unban.ts +++ b/src/commands/moderation/Unban.ts @@ -46,7 +46,7 @@ export default class Unban { "The user was never banned." ); - await guild.members.unban(id, reason ?? undefined).catch(error => console.error(error)); await modEmbed({ interaction, user: target, action: "Unbanned" }, reason); + await guild.members.unban(id, reason ?? undefined).catch(error => console.error(error)); } } diff --git a/src/commands/moderation/Unmute.ts b/src/commands/moderation/Unmute.ts index 622e95b..3ec6fad 100644 --- a/src/commands/moderation/Unmute.ts +++ b/src/commands/moderation/Unmute.ts @@ -37,7 +37,7 @@ export default class Unmute { "The user was never muted." ); - await target.edit({ communicationDisabledUntil: null }).catch(error => console.error(error)); await modEmbed({ interaction, user, action: "Unmuted" }); + await target.edit({ communicationDisabledUntil: null }).catch(error => console.error(error)); } } diff --git a/src/events/guildCreate.ts b/src/events/guildCreate.ts index dd2242d..353f0bc 100644 --- a/src/events/guildCreate.ts +++ b/src/events/guildCreate.ts @@ -1,14 +1,14 @@ import { EmbedBuilder, type DMChannel } from "discord.js"; -import { Commands } from "../handlers/commands"; +import { commands } from "../handlers/commands"; import { genColor } from "../utils/colorGen"; import { randomise } from "../utils/randomise"; import { Event } from "../utils/types"; export default (async function run(guild) { - const dmChannel = (await (await guild.fetchOwner()).createDM().catch(() => null)) as - | DMChannel - | undefined; + const owner = await guild.fetchOwner(); + if (owner.user.bot) return; + const dmChannel = (await owner.createDM().catch(() => null)) as DMChannel | undefined; let emojis = ["💖", "💝", "💓", "💗", "💘", "💟", "💕", "💞"]; if (Math.round(Math.random() * 100) <= 5) emojis = ["⌨️", "💻", "🖥️"]; @@ -29,6 +29,10 @@ export default (async function run(guild) { .setThumbnail(client.user.displayAvatarURL()) .setColor(genColor(200)); - await new Commands(client).registerCommandsForGuild(guild); - if (dmChannel) await dmChannel.send({ embeds: [embed] }); + await guild.commands.set(commands.map(command => command.data)); + try { + if (dmChannel) await dmChannel.send({ embeds: [embed] }); + } catch (e) { + console.log(e); + } } as Event<"guildCreate">); diff --git a/src/events/guildMemberAdd.ts b/src/events/guildMemberAdd.ts index fa6673a..8913927 100644 --- a/src/events/guildMemberAdd.ts +++ b/src/events/guildMemberAdd.ts @@ -31,5 +31,9 @@ export default (async function run(member) { if (user.bot) return; replace(member, getSetting(guildID, "welcome", "dm_text") as string, embed); - await dmChannel.send({ embeds: [embed] }).catch(() => null); + try { + await dmChannel.send({ embeds: [embed] }).catch(() => null); + } catch (e) { + return console.log(e); + } } as Event<"guildMemberAdd">); diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index e5efaf6..6103392 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -1,14 +1,18 @@ -import { Commands } from "../handlers/commands"; +import { commands, subCommands } from "../handlers/commands"; import { Event } from "../utils/types"; export default (async function run(interaction) { if (!interaction.isChatInputCommand() && !interaction.isAutocomplete()) return; - const command = await new Commands(interaction.client).getCommand( - interaction.commandName, - interaction.options - ); - if (!command) return; + let command; + const subCommand = subCommands.filter( + subCommand => subCommand.data.name == interaction.options.getSubcommand(false) + )[0]; + + if (!subCommand) + command = commands.filter(command => command.data.name == interaction.commandName)[0]; + else command = subCommand; + if (!command) return; if (interaction.isChatInputCommand()) command.run(interaction); if (command.autocomplete) command.autocomplete(interaction); } as Event<"interactionCreate">); diff --git a/src/handlers/commands.ts b/src/handlers/commands.ts index b0f7e4a..a271bde 100644 --- a/src/handlers/commands.ts +++ b/src/handlers/commands.ts @@ -1,148 +1,89 @@ -import { - Guild, - SlashCommandBuilder, - SlashCommandSubcommandBuilder, - SlashCommandSubcommandGroupBuilder, - type Client -} from "discord.js"; +import { SlashCommandBuilder, SlashCommandSubcommandBuilder, type Client } from "discord.js"; import { readdirSync } from "fs"; import { join } from "path"; import { pathToFileURL } from "url"; import { getDisabledCommands } from "../utils/database/disabledCommands"; export let commands: { data: SlashCommandBuilder; run: any; autocomplete: any }[] = []; -let subcommands: { data: SlashCommandSubcommandBuilder; run: any; autocomplete: any }[] = []; -export class Commands { - client: Client; - constructor(client: Client) { - this.client = client; - } - - private async createSubCommand(name: string, ...disabledCommands: string[]) { - const commandsPath = join(process.cwd(), "src", "commands"); - const run = []; - const autocomplete = []; - const command = new SlashCommandBuilder() - .setName(name.toLowerCase()) - .setDescription("This command has no description."); - - for (const subCommandFile of readdirSync(join(commandsPath, name), { - withFileTypes: true - })) { - const subCommandName = subCommandFile.name.replaceAll(".ts", ""); - if ( - disabledCommands?.find( - command => command?.split("/")?.[0] == name && command?.split("/")?.[1] == subCommandName - ) +export let subCommands: { data: SlashCommandSubcommandBuilder; run: any; autocomplete: any }[] = []; +async function createSubCommand(name: string, client: Client, ...disabledCommands: string[]) { + const commandsPath = join(process.cwd(), "src", "commands"); + const run = []; + const autocomplete = []; + const command = new SlashCommandBuilder() + .setName(name.toLowerCase()) + .setDescription("This command has no description."); + + for (const subCommandFile of readdirSync(join(commandsPath, name), { + withFileTypes: true + })) { + const subCommandName = subCommandFile.name.replaceAll(".ts", ""); + if ( + disabledCommands?.find( + command => command?.split("/")?.[0] == name && command?.split("/")?.[1] == subCommandName ) - continue; - - if (subCommandFile.isFile()) { - const subCommandModule = await import( - pathToFileURL(join(commandsPath, name, subCommandFile.name)).toString() - ); - const subCommand = new subCommandModule.default(); - - command.addSubcommand(subCommand.data); - run.push(subCommand.run); - subcommands.push({ - data: subCommand.data, - run: subCommand.run, - autocomplete: subCommand.autocomplete - }); - - if ("autocompleteHandler" in subCommand) { - subCommand.autocompleteHandler(this.client); - autocomplete.push(subCommand.autocomplete); - } - continue; - } - - const subCommandGroup = new SlashCommandSubcommandGroupBuilder() - .setName(subCommandName.toLowerCase()) - .setDescription("This subcommand group has no description."); - - const subCommandGroupFiles = readdirSync(join(commandsPath, name, subCommandFile.name), { - withFileTypes: true - }); - for (const subCommandGroupFile of subCommandGroupFiles) { - if (!subCommandGroupFile.isFile()) continue; - if ( - disabledCommands?.find( - command => - command?.split("/")?.[0] == name && - command?.split("/")?.[1] == subCommandFile.name.replaceAll(".ts", "") && - command?.split("/")?.[2] == subCommandGroupFile.name.replaceAll(".ts", "") - ) - ) - continue; - - const subCommand = await import( - pathToFileURL( - join(commandsPath, name, subCommandFile.name, subCommandGroupFile.name) - ).toString() - ); - subCommandGroup.addSubcommand(new subCommand.default().data); - } - command.addSubcommandGroup(subCommandGroup); + ) + continue; + + if (!subCommandFile.isFile()) continue; + const subCommandModule = await import( + pathToFileURL(join(commandsPath, name, subCommandFile.name)).toString() + ); + const subCommand = new subCommandModule.default(); + + command.addSubcommand(subCommand.data); + run.push(subCommand.run); + subCommands.push({ + data: subCommand.data, + run: subCommand.run, + autocomplete: subCommand.autocomplete + }); + + if ("autocompleteHandler" in subCommand) { + subCommand.autocompleteHandler(client); + autocomplete.push(subCommand.autocomplete); } - - return { data: command, run: run, autocomplete: autocomplete }; } - async loadCommands(...disabledCommands: string[]) { - const commandsPath = join(process.cwd(), "src", "commands"); - const commandFiles = readdirSync(commandsPath, { withFileTypes: true }); - - for (const commandFile of commandFiles) { - const name = commandFile.name; - if (disabledCommands?.includes(name.replaceAll(".ts", ""))) continue; - - if (commandFile.isFile()) { - const commandImport = await import(pathToFileURL(join(commandsPath, name)).toString()); - commands.push(new commandImport.default()); - continue; - } + return { data: command, run: run, autocomplete: autocomplete }; +} - const subCommand = await this.createSubCommand( - name, - join(commandsPath, name), - ...disabledCommands - ); +async function loadCommands(client: Client, ...disabledCommands: string[]) { + const commandsPath = join(process.cwd(), "src", "commands"); + for (const commandFile of readdirSync(commandsPath, { withFileTypes: true })) { + const name = commandFile.name; + if (disabledCommands?.includes(name.replaceAll(".ts", ""))) continue; - commands.push({ - data: subCommand.data, - run: subCommand.run, - autocomplete: subCommand.autocomplete - }); + if (commandFile.isFile()) { + const commandImport = await import(pathToFileURL(join(commandsPath, name)).toString()); + commands.push(new commandImport.default()); + continue; } - return commands; - } - - async registerCommandsForGuild(guild: Guild) { - await guild.commands.set(commands.map(command => command.data)); + const subCommand = await createSubCommand( + name, + client, + join(commandsPath, name), + ...disabledCommands + ); + + commands.push({ + data: subCommand.data, + run: subCommand.run, + autocomplete: subCommand.autocomplete + }); } - async registerCommands(): Promise<any[]> { - await this.loadCommands(); - const guilds = this.client.guilds.cache; - - for (const guildID of guilds.keys()) { - const disabledCommands = getDisabledCommands(guildID); - if (disabledCommands.length > 0) await this.loadCommands(...disabledCommands); - await guilds.get(guildID)?.commands.set(commands.map(command => command.data)); - } - - return commands; - } + return commands; +} - async getCommand(name: string, options: any) { - const subcommandName = options.getSubcommand(false); +export async function registerCommands(client: Client) { + await loadCommands(client); + const guilds = client.guilds.cache; - const command = commands.filter(command => command.data.name == name)[0]; - const subcommand = subcommands.filter(subcommand => subcommand.data.name == subcommandName)[0]; - if (!subcommand) return command; - return subcommand; + for (const guildID of guilds.keys()) { + const disabledCommands = getDisabledCommands(guildID); + if (disabledCommands.length > 0) await loadCommands(client, ...disabledCommands); + await guilds.get(guildID)?.commands.set(commands.map(command => command.data)); } } diff --git a/src/handlers/events.ts b/src/handlers/events.ts index 3ac1b39..707f232 100644 --- a/src/handlers/events.ts +++ b/src/handlers/events.ts @@ -3,24 +3,17 @@ import { readdirSync } from "fs"; import { join } from "path"; import { pathToFileURL } from "url"; -export class Events { - client: Client; - events: any[] = []; - constructor(client: Client) { - this.client = client; - } - - async loadEvents() { - const eventsPath = join(process.cwd(), "src", "events"); +let events = []; +export async function loadEvents(client: Client) { + const eventsPath = join(process.cwd(), "src", "events"); - for (const eventFile of readdirSync(eventsPath)) { - if (!eventFile.endsWith("ts")) continue; + for (const eventFile of readdirSync(eventsPath)) { + if (!eventFile.endsWith("ts")) continue; - const event = (await import(pathToFileURL(join(eventsPath, eventFile)).toString())).default; - const eventName = eventFile.split(".ts")[0]; - const clientEvent = this.client.on(eventName, event); + const event = (await import(pathToFileURL(join(eventsPath, eventFile)).toString())).default; + const eventName = eventFile.split(".ts")[0]; + const clientEvent = client.on(eventName, event); - this.events.push({ name: eventName, event: clientEvent }); - } + events.push({ name: eventName, event: clientEvent }); } } diff --git a/src/utils/embeds/modEmbed.ts b/src/utils/embeds/modEmbed.ts index b598c5e..26a5ef7 100644 --- a/src/utils/embeds/modEmbed.ts +++ b/src/utils/embeds/modEmbed.ts @@ -110,7 +110,7 @@ export async function modEmbed( let author = `• ${action} ${name}`; reason ? generalValues.push(`**Reason**: ${reason}`) : generalValues.push("*No reason provided*"); if (duration) generalValues.push(`**Duration**: ${ms(ms(duration), { long: true })}`); - if (dbAction) { + if (dbAction) try { const id = addModeration( guild.id, @@ -124,7 +124,6 @@ export async function modEmbed( } catch (error) { console.error(error); } - } const embed = new EmbedBuilder() .setAuthor({ name: author, iconURL: user.displayAvatarURL() }) @@ -139,17 +138,21 @@ export async function modEmbed( if (!dm) return; const dmChannel = await user.createDM().catch(() => null); if (!dmChannel || !guild.members.cache.get(user.id) || user.bot) return; - await dmChannel - .send({ - embeds: [ - embed - .setAuthor({ - name: `• You got ${action.toLowerCase()}.`, - iconURL: user.displayAvatarURL() - }) - .setDescription(generalValues.slice(+!showModerator, generalValues.length).join("\n")) - .setColor(genColor(0)) - ] - }) - .catch(() => null); + try { + await dmChannel + .send({ + embeds: [ + embed + .setAuthor({ + name: `• You got ${action.toLowerCase()}.`, + iconURL: user.displayAvatarURL() + }) + .setDescription(generalValues.slice(+!showModerator, generalValues.length).join("\n")) + .setColor(genColor(0)) + ] + }) + .catch(() => null); + } catch (e) { + return console.log(e); + } }