diff --git a/src/actions/tickets/create.ts b/src/actions/tickets/create.ts index 66bbe42..e1821ef 100644 --- a/src/actions/tickets/create.ts +++ b/src/actions/tickets/create.ts @@ -1,4 +1,4 @@ -import Discord, { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; +import Discord, { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction } from "discord.js"; import { tickets, toHexArray } from "../../utils/calculate.js"; import client from "../../utils/client.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; @@ -9,7 +9,8 @@ function capitalize(s: string) { return s.length < 3 ? s.toUpperCase() : s[0].toUpperCase() + s.slice(1).toLowerCase(); } -export default async function (interaction) { +export default async function (interaction: CommandInteraction) { + if (!interaction.guild) return; const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger; const config = await client.database.guilds.read(interaction.guild.id); diff --git a/src/actions/tickets/delete.ts b/src/actions/tickets/delete.ts index 0db4232..33860b7 100644 --- a/src/actions/tickets/delete.ts +++ b/src/actions/tickets/delete.ts @@ -4,9 +4,10 @@ import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import getEmojiByName from "../../utils/getEmojiByName.js"; export default async function (interaction: Discord.CommandInteraction) { + if (!interaction.guild) return; const { log, NucleusColors, entry, renderUser, renderChannel, renderDelta } = client.logger; - const config = await client.database.guilds.read(interaction.guild!.id); + const config = await client.database.guilds.read(interaction.guild.id); let thread = false; if (interaction.channel instanceof Discord.ThreadChannel) thread = true; const threadChannel = interaction.channel as Discord.ThreadChannel; diff --git a/src/commands/mod/ban.ts b/src/commands/mod/ban.ts index 6deb4ad..e32a720 100644 --- a/src/commands/mod/ban.ts +++ b/src/commands/mod/ban.ts @@ -24,6 +24,7 @@ const command = (builder: SlashCommandSubcommandBuilder) => const callback = async (interaction: CommandInteraction): Promise => { + if (!interaction.guild) return; const { renderUser } = client.logger; // TODO:[Modals] Replace this with a modal let reason = null; @@ -69,7 +70,7 @@ const callback = async (interaction: CommandInteraction): Promise => { reason = reason.length ? reason : null let dmSent = false; let dmMessage; - const config = await client.database.guilds.read(interaction.guild!.id); + const config = await client.database.guilds.read(interaction.guild.id); try { if (notify) { const messageData: { @@ -81,7 +82,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .setEmoji("PUNISH.BAN.RED") .setTitle("Banned") .setDescription( - `You have been banned in ${interaction.guild!.name}` + (reason ? ` for:\n> ${reason}` : ".") + `You have been banned in ${interaction.guild.name}` + (reason ? ` for:\n> ${reason}` : ".") ) .setStatus("Danger") ], @@ -110,7 +111,7 @@ const callback = async (interaction: CommandInteraction): Promise => { deleteMessageSeconds: days * 24 * 60 * 60, reason: reason ?? "*No reason provided*" }); - await client.database.history.create("ban", interaction.guild!.id, member.user, interaction.user, reason); + await client.database.history.create("ban", interaction.guild.id, member.user, interaction.user, reason); const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; const data = { meta: { @@ -128,10 +129,10 @@ const callback = async (interaction: CommandInteraction): Promise => { bannedBy: entry(interaction.user.id, renderUser(interaction.user)), reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"), accountCreated: entry(member.user.createdAt.toString(), renderDelta(member.user.createdAt.getTime())), - serverMemberCount: interaction.guild!.memberCount + serverMemberCount: interaction.guild.memberCount }, hidden: { - guild: interaction.guild!.id + guild: interaction.guild.id } }; log(data); @@ -175,20 +176,21 @@ const callback = async (interaction: CommandInteraction): Promise => { }; const check = async (interaction: CommandInteraction) => { + if (!interaction.guild) return; const member = interaction.member as GuildMember; - const me = interaction.guild!.members.me!; + const me = interaction.guild.members.me!; let apply = interaction.options.getUser("user") as User | GuildMember; const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0; const mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0; let applyPos = 0 try { - apply = await interaction.guild!.members.fetch(apply.id) as GuildMember + apply = await interaction.guild.members.fetch(apply.id) as GuildMember applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0; } catch { apply = apply as User } // Do not allow banning the owner - if (member.id === interaction.guild!.ownerId) throw new Error("You cannot ban the owner of the server"); + if (member.id === interaction.guild.ownerId) throw new Error("You cannot ban the owner of the server"); // Check if Nucleus can ban the member if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member"); // Check if Nucleus has permission to ban @@ -196,7 +198,7 @@ const check = async (interaction: CommandInteraction) => { // Do not allow banning Nucleus if (member.id === me.id) throw new Error("I cannot ban myself"); // Allow the owner to ban anyone - if (member.id === interaction.guild!.ownerId) return true; + if (member.id === interaction.guild.ownerId) return true; // Check if the user has ban_members permission if (!member.permissions.has("BanMembers")) throw new Error("You do not have the *Ban Members* permission"); // Check if the user is below on the role list diff --git a/src/commands/mod/kick.ts b/src/commands/mod/kick.ts index f6052f8..2feb5d7 100644 --- a/src/commands/mod/kick.ts +++ b/src/commands/mod/kick.ts @@ -1,7 +1,9 @@ +import { LinkWarningFooter } from './../../utils/defaultEmbeds'; import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; // @ts-expect-error import humanizeDuration from "humanize-duration"; import type { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import type Discord from "discord.js"; import confirmationMessage from "../../utils/confirmationMessage.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import keyValueList from "../../utils/generateKeyValueList.js"; @@ -14,77 +16,103 @@ const command = (builder: SlashCommandSubcommandBuilder) => .addUserOption((option) => option.setName("user").setDescription("The user to kick").setRequired(true)); const callback = async (interaction: CommandInteraction): Promise => { + if (!interaction.guild) return; const { renderUser } = client.logger; // TODO:[Modals] Replace this with a modal - let reason = null; + let reason: string | null = null; let notify = true; let confirmation; let timedOut = false; - let chosen = false; - while (!timedOut && !chosen) { + let success = false; + do { confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.KICK.RED") .setTitle("Kick") .setDescription( keyValueList({ - user: renderUser(interaction.options.getUser("user")), + user: renderUser(interaction.options.getUser("user")!), reason: reason ? "\n> " + (reason).replaceAll("\n", "\n> ") : "*No reason provided*" }) + - `The user **will${notify ? "" : " not"}** be notified\n\n` + `Are you sure you want to kick <@!${(interaction.options.getMember("user") as GuildMember).id}>?` ) .setColor("Danger") + .addCustomBoolean( + "notify", + "Notify user", + false, + null, + "The user will be sent a DM", + "ICONS.NOTIFY." + (notify ? "ON" : "OFF"), + notify + ) .addReasonButton(reason ?? "") .send(reason !== null); reason = reason ?? ""; if (confirmation.cancelled) timedOut = true; - else if (confirmation.success !== undefined) chosen = true; + else if (confirmation.success !== undefined) success = true; else if (confirmation.newReason) reason = confirmation.newReason; else if (confirmation.components) { notify = confirmation.components["notify"]!.active; } - } + } while (!timedOut && !success) if (timedOut) return; - let dmd = false; - let dm; - const config = await client.database.guilds.read(interaction.guild!.id); + if (!confirmation.success) { + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.KICK.GREEN") + .setTitle("Kick") + .setDescription("No changes were made") + .setStatus("Success") + ], + components: [] + }); + return; + } + let dmSent = false; + let dmMessage; + const config = await client.database.guilds.read(interaction.guild.id); try { if (notify) { - dm = await (interaction.options.getMember("user") as GuildMember).send({ + const messageData: { + embeds: EmojiEmbed[]; + components: ActionRowBuilder[]; + } = { embeds: [ new EmojiEmbed() .setEmoji("PUNISH.KICK.RED") .setTitle("Kicked") .setDescription( - `You have been kicked in ${interaction.guild!.name}` + (reason ? ` for:\n> ${reason}` : ".") + `You have been kicked from ${interaction.guild.name}` + + (reason ? ` for:\n> ${reason}` : ".\n*No reason was provided.*") ) .setStatus("Danger") ], - components: [ - new ActionRowBuilder().addComponents( - config.moderation.kick.text - ? [ - new ButtonBuilder() - .setStyle(ButtonStyle.Link) - .setLabel(config.moderation.kick.text) - .setURL(config.moderation.kick.link) - ] - : [] - ) - ] - }); - dmd = true; + components: [] + }; + if (config.moderation.kick.text && config.moderation.kick.link) { + messageData.embeds[0]!.setFooter(LinkWarningFooter) + messageData.components.push(new ActionRowBuilder() + .addComponents(new ButtonBuilder() + .setStyle(ButtonStyle.Link) + .setLabel(config.moderation.kick.text) + .setURL(config.moderation.kick.link) + ) + ) + } + dmMessage = await (interaction.options.getMember("user") as GuildMember).send(messageData); + dmSent = true; } } catch { - dmd = false; + dmSent = false; } try { - (interaction.options.getMember("user") as GuildMember).kick(reason ?? "No reason provided."); + (interaction.options.getMember("user") as GuildMember).kick(reason || "No reason provided"); const member = interaction.options.getMember("user") as GuildMember; - await client.database.history.create("kick", interaction.guild!.id, member.user, interaction.user, reason); + await client.database.history.create("kick", interaction.guild.id, member.user, interaction.user, reason); const { log, NucleusColors, entry, renderUser, renderDelta } = client.logger; const timeInServer = member.joinedTimestamp ? entry( - new Date().getTime() - member.joinedTimestamp, + (new Date().getTime() - member.joinedTimestamp).toString(), humanizeDuration(new Date().getTime() - member.joinedTimestamp, { round: true }) @@ -101,18 +129,27 @@ const callback = async (interaction: CommandInteraction): Promise => { list: { memberId: entry(member.id, `\`${member.id}\``), name: entry(member.id, renderUser(member.user)), - joined: entry(member.joinedAt, renderDelta(member.joinedAt)), - kicked: entry(new Date().getTime(), renderDelta(new Date().getTime())), + joined: undefined as (unknown | typeof entry), + kicked: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())), kickedBy: entry(interaction.user.id, renderUser(interaction.user)), reason: entry(reason, reason ? `\n> ${reason}` : "*No reason provided.*"), timeInServer: timeInServer, - accountCreated: entry(member.user.createdAt, renderDelta(member.user.createdAt)), serverMemberCount: member.guild.memberCount }, hidden: { guild: member.guild.id } }; + if (member.joinedTimestamp) { + data.list.joined = entry(member.joinedTimestamp.toString(), renderDelta(member.joinedTimestamp)) + } + await client.database.history.create( + "kick", + interaction.guild.id, + member.user, + interaction.user, + reason + ) log(data); } catch { await interaction.editReply({ @@ -125,10 +162,10 @@ const callback = async (interaction: CommandInteraction): Promise => { ], components: [] }); - if (dmd && dm) await dm.delete(); + if (dmSent && dmMessage) await dmMessage.delete(); return; } - const failed = !dmd && notify; + const failed = !dmSent && notify; await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -142,24 +179,25 @@ const callback = async (interaction: CommandInteraction): Promise => { }; const check = (interaction: CommandInteraction) => { + if (!interaction.guild) return; const member = interaction.member as GuildMember; - const me = interaction.guild!.me!; + const me = interaction.guild.members.me!; const apply = interaction.options.getMember("user") as GuildMember; const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0; const mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0; const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0; // Do not allow kicking the owner - if (member.id === interaction.guild!.ownerId) throw new Error("You cannot kick the owner of the server"); + if (member.id === interaction.guild.ownerId) throw new Error("You cannot kick the owner of the server"); // Check if Nucleus can kick the member if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member"); // Check if Nucleus has permission to kick - if (!me.permissions.has("KICK_MEMBERS")) throw new Error("I do not have the *Kick Members* permission"); + if (!me.permissions.has("KickMembers")) throw new Error("I do not have the *Kick Members* permission"); // Do not allow kicking Nucleus - if (member.id === interaction.guild!.me!.id) throw new Error("I cannot kick myself"); + if (member.id === interaction.guild.members.me!.id) throw new Error("I cannot kick myself"); // Allow the owner to kick anyone - if (member.id === interaction.guild!.ownerId) return true; + if (member.id === interaction.guild.ownerId) return true; // Check if the user has kick_members permission - if (!member.permissions.has("KICK_MEMBERS")) throw new Error("You do not have the *Kick Members* permission"); + if (!member.permissions.has("KickMembers")) throw new Error("You do not have the *Kick Members* permission"); // Check if the user is below on the role list if (!(memberPos > applyPos)) throw new Error("You do not have a role higher than that member"); // Allow kick diff --git a/src/commands/mod/mute.ts b/src/commands/mod/mute.ts index db0d68b..0e76cba 100644 --- a/src/commands/mod/mute.ts +++ b/src/commands/mod/mute.ts @@ -49,6 +49,7 @@ const command = (builder: SlashCommandSubcommandBuilder) => ); const callback = async (interaction: CommandInteraction): Promise => { + if (!interaction.guild) return; const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger; const user = interaction.options.getMember("user") as GuildMember; const time = { @@ -57,7 +58,7 @@ const callback = async (interaction: CommandInteraction): Promise => { minutes: interaction.options.getInteger("minutes") ?? 0, seconds: interaction.options.getInteger("seconds") ?? 0 }; - const config = await client.database.guilds.read(interaction.guild!.id); + const config = await client.database.guilds.read(interaction.guild.id); let serverSettingsDescription = config.moderation.mute.timeout ? "given a timeout" : ""; if (config.moderation.mute.role) serverSettingsDescription += @@ -187,9 +188,9 @@ const callback = async (interaction: CommandInteraction): Promise => { .addCustomBoolean( "appeal", "Create appeal ticket", - !(await areTicketsEnabled(interaction.guild!.id)), + !(await areTicketsEnabled(interaction.guild.id)), async () => - await create(interaction.guild!, interaction.options.getUser("user")!, interaction.user, reason), + await create(interaction.guild, interaction.options.getUser("user")!, interaction.user, reason), "An appeal ticket will be created when Confirm is clicked", "CONTROL.TICKET", createAppealTicket @@ -226,7 +227,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .setEmoji("PUNISH.MUTE.RED") .setTitle("Muted") .setDescription( - `You have been muted in ${interaction.guild!.name}` + + `You have been muted in ${interaction.guild.name}` + (reason ? ` for:\n> ${reason}` : ".\n\n" + @@ -267,7 +268,7 @@ const callback = async (interaction: CommandInteraction): Promise => { if (config.moderation.mute.role !== null) { await member.roles.add(config.moderation.mute.role); await client.database.eventScheduler.schedule("naturalUnmute", new Date().getTime() + muteTime * 1000, { - guild: interaction.guild!.id, + guild: interaction.guild.id, user: user.id, expires: new Date().getTime() + muteTime * 1000 }); @@ -280,7 +281,7 @@ const callback = async (interaction: CommandInteraction): Promise => { if (config.moderation.mute.role !== null) { await member.roles.add(config.moderation.mute.role); await client.database.eventScheduler.schedule("unmuteRole", new Date().getTime() + muteTime * 1000, { - guild: interaction.guild!.id, + guild: interaction.guild.id, user: user.id, role: config.moderation.mute.role }); @@ -303,7 +304,7 @@ const callback = async (interaction: CommandInteraction): Promise => { if (dmd && dm) await dm.delete(); return; } - await client.database.history.create("mute", interaction.guild!.id, member.user, interaction.user, reason); + await client.database.history.create("mute", interaction.guild.id, member.user, interaction.user, reason); const failed = !dmd && notify; await interaction.editReply({ embeds: [ @@ -342,7 +343,7 @@ const callback = async (interaction: CommandInteraction): Promise => { reason: entry(reason, reason ? reason : "*No reason provided*") }, hidden: { - guild: interaction.guild!.id + guild: interaction.guild.id } }; log(data); @@ -361,14 +362,15 @@ const callback = async (interaction: CommandInteraction): Promise => { }; const check = (interaction: CommandInteraction) => { + if (!interaction.guild) return; const member = interaction.member as GuildMember; - const me = interaction.guild!.me!; + const me = interaction.guild.me!; const apply = interaction.options.getMember("user") as GuildMember; const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0; const mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0; const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0; // Do not allow muting the owner - if (member.id === interaction.guild!.ownerId) throw new Error("You cannot mute the owner of the server"); + if (member.id === interaction.guild.ownerId) throw new Error("You cannot mute the owner of the server"); // Check if Nucleus can mute the member if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member"); // Check if Nucleus has permission to mute @@ -376,7 +378,7 @@ const check = (interaction: CommandInteraction) => { // Do not allow muting Nucleus if (member.id === me.id) throw new Error("I cannot mute myself"); // Allow the owner to mute anyone - if (member.id === interaction.guild!.ownerId) return true; + if (member.id === interaction.guild.ownerId) return true; // Check if the user has moderate_members permission if (!member.permissions.has("MODERATE_MEMBERS")) throw new Error("You do not have the *Moderate Members* permission"); diff --git a/src/commands/mod/purge.ts b/src/commands/mod/purge.ts index a19a627..e9aa41a 100644 --- a/src/commands/mod/purge.ts +++ b/src/commands/mod/purge.ts @@ -26,7 +26,7 @@ const command = (builder: SlashCommandSubcommandBuilder) => ); const callback = async (interaction: CommandInteraction): Promise => { - const user = (interaction.options.getMember("user") as GuildMember) ?? null; + const user = (interaction.options.getMember("user") as GuildMember); const channel = interaction.channel as GuildChannel; if ( !["GUILD_TEXT", "GUILD_NEWS", "GUILD_NEWS_THREAD", "GUILD_PUBLIC_THREAD", "GUILD_PRIVATE_THREAD"].includes( diff --git a/src/commands/mod/slowmode.ts b/src/commands/mod/slowmode.ts index 3e883b3..b565deb 100644 --- a/src/commands/mod/slowmode.ts +++ b/src/commands/mod/slowmode.ts @@ -34,7 +34,7 @@ const command = (builder: SlashCommandSubcommandBuilder) => ); const callback = async (interaction: CommandInteraction): Promise => { - let time = parseInt(interaction.options.getString("time") ?? "0"); + let time = parseInt(interaction.options.get("time")?.value as string || "0"); if (time === 0 && (interaction.channel as TextChannel).rateLimitPerUser === 0) { time = 10; } @@ -91,10 +91,9 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction) => { const member = interaction.member as GuildMember; // Check if Nucleus can set the slowmode - if (!interaction.guild.me.permissions.has("MANAGE_CHANNELS")) - throw new Error("I do not have the *Manage Channels* permission"); + if (!interaction.guild!.members.me!.permissions.has("ManageChannels")) throw new Error("I do not have the *Manage Channels* permission"); // Check if the user has manage_channel permission - if (!member.permissions.has("MANAGE_CHANNELS")) throw new Error("You do not have the *Manage Channels* permission"); + if (!member.permissions.has("ManageChannels")) throw new Error("You do not have the *Manage Channels* permission"); // Allow slowmode return true; }; diff --git a/src/commands/mod/softban.ts b/src/commands/mod/softban.ts index 6c0396d..c9a71f6 100644 --- a/src/commands/mod/softban.ts +++ b/src/commands/mod/softban.ts @@ -1,5 +1,5 @@ import { CommandInteraction, GuildMember, ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import type { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import confirmationMessage from "../../utils/confirmationMessage.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import keyValueList from "../../utils/generateKeyValueList.js"; diff --git a/src/commands/mod/unban.ts b/src/commands/mod/unban.ts index 81a29a2..d34f303 100644 --- a/src/commands/mod/unban.ts +++ b/src/commands/mod/unban.ts @@ -14,6 +14,7 @@ const command = (builder: SlashCommandSubcommandBuilder) => ); const callback = async (interaction: CommandInteraction): Promise => { + if (!interaction.guild) return; const bans = await interaction.guild.bans.fetch(); const user = interaction.options.getString("user"); let resolved = bans.find((ban) => ban.user.id === user); @@ -107,6 +108,7 @@ const callback = async (interaction: CommandInteraction): Promise => { }; const check = (interaction: CommandInteraction) => { + if (!interaction.guild) return; const member = interaction.member as GuildMember; const me = interaction.guild.me!; // Check if Nucleus can unban members diff --git a/src/commands/mod/unmute.ts b/src/commands/mod/unmute.ts index 0ce63d1..40510a8 100644 --- a/src/commands/mod/unmute.ts +++ b/src/commands/mod/unmute.ts @@ -1,5 +1,5 @@ -import { CommandInteraction, GuildMember } from "discord.js"; -import { SlashCommandSubcommandBuilder } from "@discordjs/builders"; +import type { CommandInteraction, GuildMember } from "discord.js"; +import type { SlashCommandSubcommandBuilder } from "@discordjs/builders"; import confirmationMessage from "../../utils/confirmationMessage.js"; import EmojiEmbed from "../../utils/generateEmojiEmbed.js"; import keyValueList from "../../utils/generateKeyValueList.js"; @@ -12,40 +12,51 @@ const command = (builder: SlashCommandSubcommandBuilder) => .addUserOption((option) => option.setName("user").setDescription("The user to unmute").setRequired(true)); const callback = async (interaction: CommandInteraction): Promise => { + if (!interaction.guild) return; const { log, NucleusColors, renderUser, entry, renderDelta } = client.logger; // TODO:[Modals] Replace this with a modal - let reason = null; + let reason: string | null = null; let notify = false; let confirmation; + let timedOut = false; let success = false; - while (!success) { + do { confirmation = await new confirmationMessage(interaction) .setEmoji("PUNISH.MUTE.RED") .setTitle("Unmute") .setDescription( keyValueList({ - user: renderUser(interaction.options.getUser("user")), + user: renderUser(interaction.options.getUser("user")!), reason: `\n> ${reason ? reason : "*No reason provided*"}` }) + - `The user **will${notify ? "" : " not"}** be notified\n\n` + `Are you sure you want to unmute <@!${(interaction.options.getMember("user") as GuildMember).id}>?` ) .setColor("Danger") + .addCustomBoolean( + "notify", + "Notify user", + false, + null, + "The user will be sent a DM", + "ICONS.NOTIFY." + (notify ? "ON" : "OFF"), + notify + ) .addReasonButton(reason ?? "") .send(reason !== null); - if (confirmation.success) success = true; + if (confirmation.cancelled) timedOut = true; + else if (confirmation.success !== undefined) success = true; else if (confirmation.newReason) reason = confirmation.newReason; else if (confirmation.components) { - notify = confirmation.components.notify.active; + notify = confirmation.components!["notify"]!.active; } - } + } while (!timedOut && !success); if (confirmation.cancelled) return; if (confirmation.success) { - let dmd = false; - let dm; + let dmSent = false; + let dmMessage; try { if (notify) { - dm = await (interaction.options.getMember("user") as GuildMember).send({ + dmMessage = await (interaction.options.getMember("user") as GuildMember).send({ embeds: [ new EmojiEmbed() .setEmoji("PUNISH.MUTE.GREEN") @@ -57,10 +68,10 @@ const callback = async (interaction: CommandInteraction): Promise => { .setStatus("Success") ] }); - dmd = true; + dmSent = true; } } catch { - dmd = false; + dmSent = false; } const member = interaction.options.getMember("user") as GuildMember; try { @@ -76,7 +87,7 @@ const callback = async (interaction: CommandInteraction): Promise => { ], components: [] }); - if (dmd) await dm.delete(); + if (dmSent && dmMessage) await dmMessage.delete(); return; } await client.database.history.create( @@ -98,7 +109,7 @@ const callback = async (interaction: CommandInteraction): Promise => { list: { memberId: entry(member.user.id, `\`${member.user.id}\``), name: entry(member.user.id, renderUser(member.user)), - unmuted: entry(new Date().getTime(), renderDelta(new Date().getTime())), + unmuted: entry(new Date().getTime().toString(), renderDelta(new Date().getTime())), unmutedBy: entry(interaction.user.id, renderUser(interaction.user)) }, hidden: { @@ -106,7 +117,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } }; log(data); - const failed = !dmd && notify; + const failed = !dmSent && notify; await interaction.editReply({ embeds: [ new EmojiEmbed() @@ -132,10 +143,10 @@ const callback = async (interaction: CommandInteraction): Promise => { }; const check = (interaction: CommandInteraction) => { + if (!interaction.guild) return; const member = interaction.member as GuildMember; - const me = interaction.guild.me!; + const me = interaction.guild.members.me!; const apply = interaction.options.getMember("user") as GuildMember; - if (member === null || me === null || apply === null) throw new Error("That member is not in the server"); const memberPos = member.roles.cache.size > 1 ? member.roles.highest.position : 0; const mePos = me.roles.cache.size > 1 ? me.roles.highest.position : 0; const applyPos = apply.roles.cache.size > 1 ? apply.roles.highest.position : 0; @@ -144,11 +155,11 @@ const check = (interaction: CommandInteraction) => { // Check if Nucleus can unmute the member if (!(mePos > applyPos)) throw new Error("I do not have a role higher than that member"); // Check if Nucleus has permission to unmute - if (!me.permissions.has("MODERATE_MEMBERS")) throw new Error("I do not have the *Moderate Members* permission"); + if (!me.permissions.has("ModerateMembers")) throw new Error("I do not have the *Moderate Members* permission"); // Allow the owner to unmute anyone if (member.id === interaction.guild.ownerId) return true; // Check if the user has moderate_members permission - if (!member.permissions.has("MODERATE_MEMBERS")) + if (!member.permissions.has("ModerateMembers")) throw new Error("You do not have the *Moderate Members* permission"); // Check if the user is below on the role list if (!(memberPos > applyPos)) throw new Error("You do not have a role higher than that member"); diff --git a/src/commands/mod/warn.ts b/src/commands/mod/warn.ts index 5125587..1c5223f 100644 --- a/src/commands/mod/warn.ts +++ b/src/commands/mod/warn.ts @@ -14,6 +14,7 @@ const command = (builder: SlashCommandSubcommandBuilder) => .addUserOption((option) => option.setName("user").setDescription("The user to warn").setRequired(true)); const callback = async (interaction: CommandInteraction): Promise => { + if (interaction.guild === null) return; const { log, NucleusColors, renderUser, entry } = client.logger; // TODO:[Modals] Replace this with a modal let reason: string | null = null; @@ -37,7 +38,7 @@ const callback = async (interaction: CommandInteraction): Promise => { .addCustomBoolean( "appeal", "Create appeal ticket", - !(await areTicketsEnabled(interaction.guild!.id)), + !(await areTicketsEnabled(interaction.guild.id)), async () => await create(interaction.guild!, interaction.options.getUser("user")!, interaction.user, reason), "An appeal ticket will be created", "CONTROL.TICKET", @@ -64,79 +65,166 @@ const callback = async (interaction: CommandInteraction): Promise => { } } while (!timedOut && !success) if (timedOut) return; - if (confirmation.success) { - let dmSent = false; - const config = await client.database.guilds.read(interaction.guild!.id); - try { - if (notify) { - const messageData: { - embeds: EmojiEmbed[]; - components: ActionRowBuilder[]; - } = { - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.WARN.RED") - .setTitle("Warned") - .setDescription( - `You have been warned in ${interaction.guild!.name}` + - (reason ? ` for:\n> ${reason}` : ".\n*No reason was provided*") + - "\n\n" + - (createAppealTicket - ? `You can appeal this in the ticket created in <#${confirmation.components!["appeal"]!.response}>` - : "") - ) - .setStatus("Danger") - ], - components: [] - }; - if (config.moderation.warn.text && config.moderation.warn.link) { - messageData.embeds[0]!.setFooter(LinkWarningFooter) - messageData.components.push(new ActionRowBuilder() - .addComponents(new ButtonBuilder() - .setStyle(ButtonStyle.Link) - .setLabel(config.moderation.warn.text) - .setURL(config.moderation.warn.link) - ) - ) - } - await (interaction.options.getMember("user") as GuildMember).send(messageData); - dmSent = true; + if (!confirmation.success) { + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.WARN.GREEN") + .setTitle("Warn") + .setDescription("No changes were made") + .setStatus("Success") + ], + components: [] + }); + return; + } + let dmSent = false; + const config = await client.database.guilds.read(interaction.guild.id); + try { + if (notify) { + const messageData: { + embeds: EmojiEmbed[]; + components: ActionRowBuilder[]; + } = { + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.WARN.RED") + .setTitle("Warned") + .setDescription( + `You have been warned in ${interaction.guild.name}` + + (reason ? ` for:\n> ${reason}` : ".\n*No reason was provided*") + + "\n\n" + + (createAppealTicket + ? `You can appeal this in the ticket created in <#${confirmation.components!["appeal"]!.response}>` + : "") + ) + .setStatus("Danger") + ], + components: [] + }; + if (config.moderation.warn.text && config.moderation.warn.link) { + messageData.embeds[0]!.setFooter(LinkWarningFooter) + messageData.components.push(new ActionRowBuilder() + .addComponents(new ButtonBuilder() + .setStyle(ButtonStyle.Link) + .setLabel(config.moderation.warn.text) + .setURL(config.moderation.warn.link) + ) + ) } + await (interaction.options.getMember("user") as GuildMember).send(messageData); + dmSent = true; + } + } catch (e) { + dmSent = false; + } + const data = { + meta: { + type: "memberWarn", + displayName: "Member warned", + calculateType: "guildMemberPunish", + color: NucleusColors.yellow, + emoji: "PUNISH.WARN.YELLOW", + timestamp: new Date().getTime() + }, + list: { + user: entry( + (interaction.options.getMember("user") as GuildMember).user.id, + renderUser((interaction.options.getMember("user") as GuildMember).user) + ), + warnedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)), + reason: reason ? `\n> ${reason}` : "*No reason provided*" + }, + hidden: { + guild: interaction.guild.id + } + }; + await client.database.history.create( + "warn", + interaction.guild.id, + (interaction.options.getMember("user") as GuildMember).user, + interaction.user, + reason + ); + log(data); + const failed = !dmSent && notify; + if (!failed) { + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.WARN.GREEN") + .setTitle("Warn") + .setDescription( + "The user was warned" + + (createAppealTicket + ? ` and an appeal ticket was opened in <#${confirmation.components!["appeal"]!.response}>` + : "") + ) + .setStatus("Success") + ], + components: [] + }); + } else { + const canSeeChannel = (interaction.options.getMember("user") as GuildMember) + .permissionsIn(interaction.channel as Discord.TextChannel) + .has("ViewChannel"); + const m = (await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.WARN.RED") + .setTitle("Warn") + .setDescription("The user's DMs are not open\n\nWhat would you like to do?") + .setStatus("Danger") + ], + components: [ + new ActionRowBuilder().addComponents( + new Discord.ButtonBuilder().setCustomId("log").setLabel("Ignore and log").setStyle(ButtonStyle.Secondary), + new Discord.ButtonBuilder() + .setCustomId("here") + .setLabel("Warn here") + .setStyle(canSeeChannel ? ButtonStyle.Primary : ButtonStyle.Secondary) + .setDisabled(!canSeeChannel), + new Discord.ButtonBuilder() + .setCustomId("ticket") + .setLabel("Create ticket") + .setStyle(canSeeChannel ? ButtonStyle.Primary : ButtonStyle.Secondary) + .setDisabled(createAppealTicket) + ) + ] + })) as Discord.Message; + let component; + try { + component = await m.awaitMessageComponent({ + filter: (m) => m.user.id === interaction.user.id, + time: 300000 + }); } catch (e) { - dmSent = false; + return await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.WARN.GREEN") + .setTitle("Warn") + .setDescription("No changes were made") + .setStatus("Success") + ], + components: [] + }); } - const data = { - meta: { - type: "memberWarn", - displayName: "Member warned", - calculateType: "guildMemberPunish", - color: NucleusColors.yellow, - emoji: "PUNISH.WARN.YELLOW", - timestamp: new Date().getTime() - }, - list: { - user: entry( - (interaction.options.getMember("user") as GuildMember).user.id, - renderUser((interaction.options.getMember("user") as GuildMember).user) - ), - warnedBy: entry(interaction.member!.user.id, renderUser(interaction.member!.user as Discord.User)), - reason: reason ? `\n> ${reason}` : "*No reason provided*" - }, - hidden: { - guild: interaction.guild!.id - } - }; - await client.database.history.create( - "warn", - interaction.guild!.id, - (interaction.options.getMember("user") as GuildMember).user, - interaction.user, - reason - ); - log(data); - const failed = !dmSent && notify; - if (!failed) { - await interaction.editReply({ + if (component.customId === "here") { + await interaction.channel!.send({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.WARN.RED") + .setTitle("Warn") + .setDescription("You have been warned" + (reason ? ` for:\n> ${reason}` : ".")) + .setStatus("Danger") + ], + content: `<@!${(interaction.options.getMember("user") as GuildMember).id}>`, + allowedMentions: { + users: [(interaction.options.getMember("user") as GuildMember).id] + } + }); + return await interaction.editReply({ embeds: [ new EmojiEmbed() .setEmoji("PUNISH.WARN.GREEN") @@ -151,139 +239,53 @@ const callback = async (interaction: CommandInteraction): Promise => { ], components: [] }); - } else { - const canSeeChannel = (interaction.options.getMember("user") as GuildMember) - .permissionsIn(interaction.channel as Discord.TextChannel) - .has("ViewChannel"); - const m = (await interaction.editReply({ + } else if (component.customId === "log") { + await interaction.editReply({ embeds: [ new EmojiEmbed() - .setEmoji("PUNISH.WARN.RED") + .setEmoji("PUNISH.WARN.GREEN") .setTitle("Warn") - .setDescription("The user's DMs are not open\n\nWhat would you like to do?") - .setStatus("Danger") + .setDescription("The warn was logged") + .setStatus("Success") ], - components: [ - new ActionRowBuilder().addComponents( - new Discord.ButtonBuilder().setCustomId("log").setLabel("Ignore and log").setStyle(ButtonStyle.Secondary), - new Discord.ButtonBuilder() - .setCustomId("here") - .setLabel("Warn here") - .setStyle(canSeeChannel ? ButtonStyle.Primary : ButtonStyle.Secondary) - .setDisabled(!canSeeChannel), - new Discord.ButtonBuilder() - .setCustomId("ticket") - .setLabel("Create ticket") - .setStyle(canSeeChannel ? ButtonStyle.Primary : ButtonStyle.Secondary) - .setDisabled(createAppealTicket) - ) - ] - })) as Discord.Message; - let component; - try { - component = await m.awaitMessageComponent({ - filter: (m) => m.user.id === interaction.user.id, - time: 300000 - }); - } catch (e) { + components: [] + }); + } else if (component.customId === "ticket") { + const ticketChannel = await create( + interaction.guild, + interaction.options.getUser("user")!, + interaction.user, + reason, + "Warn Notification" + ); + if (ticketChannel === null) { return await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.WARN.GREEN") - .setTitle("Warn") - .setDescription("No changes were made") - .setStatus("Success") - ], - components: [] - }); - } - if (component.customId === "here") { - await interaction.channel!.send({ embeds: [ new EmojiEmbed() .setEmoji("PUNISH.WARN.RED") .setTitle("Warn") - .setDescription("You have been warned" + (reason ? ` for:\n> ${reason}` : ".")) + .setDescription("A ticket could not be created") .setStatus("Danger") ], - content: `<@!${(interaction.options.getMember("user") as GuildMember).id}>`, - allowedMentions: { - users: [(interaction.options.getMember("user") as GuildMember).id] - } - }); - return await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.WARN.GREEN") - .setTitle("Warn") - .setDescription( - "The user was warned" + - (createAppealTicket - ? ` and an appeal ticket was opened in <#${confirmation.components!["appeal"]!.response}>` - : "") - ) - .setStatus("Success") - ], - components: [] - }); - } else if (component.customId === "log") { - await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.WARN.GREEN") - .setTitle("Warn") - .setDescription("The warn was logged") - .setStatus("Success") - ], - components: [] - }); - } else if (component.customId === "ticket") { - const ticketChannel = await create( - interaction.guild!, - interaction.options.getUser("user")!, - interaction.user, - reason, - "Warn Notification" - ); - if (ticketChannel === null) { - return await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.WARN.RED") - .setTitle("Warn") - .setDescription("A ticket could not be created") - .setStatus("Danger") - ], - components: [] - }); - } - await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.WARN.GREEN") - .setTitle("Warn") - .setDescription(`A ticket was created in <#${ticketChannel}>`) - .setStatus("Success") - ], components: [] }); } + await interaction.editReply({ + embeds: [ + new EmojiEmbed() + .setEmoji("PUNISH.WARN.GREEN") + .setTitle("Warn") + .setDescription(`A ticket was created in <#${ticketChannel}>`) + .setStatus("Success") + ], + components: [] + }); } - } else { - await interaction.editReply({ - embeds: [ - new EmojiEmbed() - .setEmoji("PUNISH.WARN.GREEN") - .setTitle("Warn") - .setDescription("No changes were made") - .setStatus("Success") - ], - components: [] - }); } }; const check = (interaction: CommandInteraction) => { + if (!interaction.guild) return; const member = interaction.member as GuildMember; const apply = interaction.options.getMember("user") as GuildMember | null; if (apply === null) throw new Error("That member is not in the server"); @@ -292,7 +294,7 @@ const check = (interaction: CommandInteraction) => { // Do not allow warning bots if (member.user.bot) throw new Error("I cannot warn bots"); // Allow the owner to warn anyone - if (member.id === interaction.guild!.ownerId) return true; + if (member.id === interaction.guild.ownerId) return true; // Check if the user has moderate_members permission if (!member.permissions.has("ModerateMembers")) throw new Error("You do not have the *Moderate Members* permission"); diff --git a/src/commands/settings/tickets.ts b/src/commands/settings/tickets.ts index 48d419d..70ba52d 100644 --- a/src/commands/settings/tickets.ts +++ b/src/commands/settings/tickets.ts @@ -62,6 +62,7 @@ const command = (builder: SlashCommandSubcommandBuilder) => ); const callback = async (interaction: CommandInteraction): Promise => { + if (!interaction.guild) return; let m = (await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, @@ -77,7 +78,7 @@ const callback = async (interaction: CommandInteraction): Promise => { if (options.category) { let channel: GuildChannel | null; try { - channel = await interaction.guild!.channels.fetch(options.category.id); + channel = await interaction.guild.channels.fetch(options.category.id); } catch { return await interaction.editReply({ embeds: [ @@ -91,7 +92,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } if (!channel) return; channel = channel as Discord.CategoryChannel; - if (channel.guild.id !== interaction.guild!.id) + if (channel.guild.id !== interaction.guild.id) return interaction.editReply({ embeds: [ new EmojiEmbed() @@ -117,7 +118,7 @@ const callback = async (interaction: CommandInteraction): Promise => { let role: Role | null; if (options.supportping) { try { - role = await interaction.guild!.roles.fetch(options.supportping.id); + role = await interaction.guild.roles.fetch(options.supportping.id); } catch { return await interaction.editReply({ embeds: [ @@ -131,7 +132,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } if (!role) return; role = role as Discord.Role; - if (role.guild.id !== interaction.guild!.id) + if (role.guild.id !== interaction.guild.id) return interaction.editReply({ embeds: [ new EmojiEmbed() @@ -170,7 +171,7 @@ const callback = async (interaction: CommandInteraction): Promise => { if (options.maxtickets) toUpdate["tickets.maxTickets"] = options.maxtickets; if (options.supportping) toUpdate["tickets.supportRole"] = options.supportping.id; try { - await client.database.guilds.write(interaction.guild!.id, toUpdate); + await client.database.guilds.write(interaction.guild.id, toUpdate); } catch (e) { return interaction.editReply({ embeds: [ @@ -196,7 +197,7 @@ const callback = async (interaction: CommandInteraction): Promise => { }); } } - let data = await client.database.guilds.read(interaction.guild!.id); + let data = await client.database.guilds.read(interaction.guild.id); data.tickets.customTypes = (data.tickets.customTypes || []).filter( (value: string, index: number, array: string[]) => array.indexOf(value) === index ); @@ -284,19 +285,19 @@ const callback = async (interaction: CommandInteraction): Promise => { if ((i.component as Component).customId === "clearCategory") { if (lastClicked === "cat") { lastClicked = ""; - await client.database.guilds.write(interaction.guild!.id, null, ["tickets.category"]); + await client.database.guilds.write(interaction.guild.id, null, ["tickets.category"]); data.category = undefined; } else lastClicked = "cat"; } else if ((i.component as Component).customId === "clearMaxTickets") { if (lastClicked === "max") { lastClicked = ""; - await client.database.guilds.write(interaction.guild!.id, null, ["tickets.maxTickets"]); + await client.database.guilds.write(interaction.guild.id, null, ["tickets.maxTickets"]); data.maxTickets = 5; } else lastClicked = "max"; } else if ((i.component as Component).customId === "clearSupportPing") { if (lastClicked === "sup") { lastClicked = ""; - await client.database.guilds.write(interaction.guild!.id, null, ["tickets.supportRole"]); + await client.database.guilds.write(interaction.guild.id, null, ["tickets.supportRole"]); data.supportRole = undefined; } else lastClicked = "sup"; } else if ((i.component as Component).customId === "send") { @@ -732,7 +733,7 @@ async function manageTypes(interaction: CommandInteraction, data: GuildConfig["t const check = (interaction: CommandInteraction) => { const member = interaction.member as Discord.GuildMember; - if (!member.permissions.has("MANAGE_GUILD")) + if (!member.permissions.has("ManageGuild")) throw new Error("You must have the *Manage Server* permission to use this command"); return true; }; diff --git a/src/commands/settings/verify.ts b/src/commands/settings/verify.ts index b3eb11a..cceadae 100644 --- a/src/commands/settings/verify.ts +++ b/src/commands/settings/verify.ts @@ -30,6 +30,7 @@ const command = (builder: SlashCommandSubcommandBuilder) => ); const callback = async (interaction: CommandInteraction): Promise => { + if (!interaction.guild) return; const m = (await interaction.reply({ embeds: LoadingEmbed, ephemeral: true, @@ -51,7 +52,7 @@ const callback = async (interaction: CommandInteraction): Promise => { }); } role = role as Discord.Role; - if (role.guild.id !== interaction.guild!.id) { + if (role.guild.id !== interaction.guild.id) { return interaction.editReply({ embeds: [ new EmojiEmbed() @@ -72,7 +73,7 @@ const callback = async (interaction: CommandInteraction): Promise => { if (confirmation.cancelled) return; if (confirmation.success) { try { - await client.database.guilds.write(interaction.guild!.id, { + await client.database.guilds.write(interaction.guild.id, { "verify.role": role.id, "verify.enabled": true }); @@ -92,7 +93,7 @@ const callback = async (interaction: CommandInteraction): Promise => { role: entry(role.id, renderRole(role)) }, hidden: { - guild: interaction.guild!.id + guild: interaction.guild.id } }; log(data); @@ -123,7 +124,7 @@ const callback = async (interaction: CommandInteraction): Promise => { } } let clicks = 0; - const data = await client.database.guilds.read(interaction.guild!.id); + const data = await client.database.guilds.read(interaction.guild.id); let role = data.verify.role; let timedOut = false; @@ -166,7 +167,7 @@ const callback = async (interaction: CommandInteraction): Promise => { clicks += 1; if (clicks === 2) { clicks = 0; - await client.database.guilds.write(interaction.guild!.id, null, ["verify.role", "verify.enabled"]); + await client.database.guilds.write(interaction.guild.id, null, ["verify.role", "verify.enabled"]); role = undefined; } } else if ((i.component as Component).customId === "send") { @@ -379,7 +380,7 @@ const callback = async (interaction: CommandInteraction): Promise => { const check = (interaction: CommandInteraction) => { const member = interaction.member as Discord.GuildMember; - if (!member.permissions.has("MANAGE_GUILD")) + if (!member.permissions.has("ManageGuild")) throw new Error("You must have the *Manage Server* permission to use this command"); return true; }; diff --git a/src/config/format.ts b/src/config/format.ts index ddd164e..53aba72 100644 --- a/src/config/format.ts +++ b/src/config/format.ts @@ -11,6 +11,8 @@ const defaultDict: Record = { owners: [], commandsFolder: "Your built commands folder (usually dist/commands)", eventsFolder: "Your built events folder (usually dist/events)", + messageContextFolder: "Your built message context folder (usually dist/context/messages)", + userContextFolder: "Your built user context folder (usually dist/context/users)", verifySecret: "If using verify, enter a code here which matches the secret sent back by your website. You can use a random code if you do not have one already. (Optional)", mongoUrl: "Your Mongo connection string, e.g. mongodb://127.0.0.1:27017", diff --git a/src/utils/commandRegistration/register.ts b/src/utils/commandRegistration/register.ts index 64f6e77..d1b58f1 100644 --- a/src/utils/commandRegistration/register.ts +++ b/src/utils/commandRegistration/register.ts @@ -1,4 +1,5 @@ -import { Interaction, SlashCommandBuilder } from 'discord.js'; +import { typeSlashCommandSubcommandBuilder } from '@discordjs/builders'; +import { Interaction, SlashCommandBuilder, ApplicationCommandType } from 'discord.js'; // @ts-expect-error import config from "../../config/main.json" assert { type: "json" }; import client from "../client.js"; @@ -88,8 +89,64 @@ async function registerEvents() { console.log(`Loaded ${files.length - errors} events (${errors} failed)`) }; +async function registerContextMenus() { + console.log("Reading context menus") + const messageFiles = fs.readdirSync(config.messageContextFolder, { withFileTypes: true }).filter( + file => !file.name.endsWith(".ts") && !file.name.endsWith(".map") + ); + const userFiles = fs.readdirSync(config.userContextFolder, { withFileTypes: true }).filter( + file => !file.name.endsWith(".ts") && !file.name.endsWith(".map") + ); + console.log(`Registering ${messageFiles.length} message context menus and ${userFiles.length} user context menus`) + let i = 0; + let errors = 0; + for (const file of messageFiles) { + const last = i === messageFiles.length - 1 ? "└" : "├"; + i++; + try { + console.log(`${last}─ ${colours.yellow}Loading message context menu ${file.name}${colours.none}`) + const context = (await import(`../../../${config.messageContextFolder}/${file.name}`)); + context.command.setType(ApplicationCommandType.Message); + + client.commands["contextCommands/message/" + file.name.replace(".js", "")] = context; + + console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.green}Loaded ${file.name} [${i} / ${messageFiles.length}]${colours.none}`) + } catch (e) { + errors++; + console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.red}Failed to load ${file.name} [${i} / ${messageFiles.length}]${colours.none}`) + } + } + for (const file of userFiles) { + const last = i === userFiles.length - 1 ? "└" : "├"; + i++; + try { + console.log(`${last}─ ${colours.yellow}Loading user context menu ${file.name}${colours.none}`) + const context = (await import(`../../../${config.userContextFolder}/${file.name}`)); + context.command.setType(ApplicationCommandType.User); + + client.commands["contextCommands/user/" + file.name.replace(".js", "")] = context; + + console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.green}Loaded ${file.name} [${i} / ${userFiles.length}]${colours.none}`) + } catch (e) { + errors++; + console.log(`${last.replace("└", " ").replace("├", "│")} └─ ${colours.red}Failed to load ${file.name} [${i} / ${userFiles.length}]${colours.none}`) + } + } + + console.log(`Loaded ${messageFiles.length + userFiles.length - errors} context menus (${errors} failed)`) +}; + async function registerCommandHandler() { client.on("interactionCreate", async (interaction: Interaction) => { + if (interaction.isUserContextMenuCommand()) {; + const commandName = interaction.commandName; + console.log(commandName); + return; + } else if (interaction.isMessageContextMenuCommand()) { + const commandName = interaction.commandName; + console.log(commandName); + return; + } if (!interaction.isChatInputCommand()) return; const commandName = interaction.commandName; @@ -118,7 +175,8 @@ async function registerCommandHandler() { } export default async function register() { - await registerCommands(); + let commandList: (SlashCommandBuilder | SlashCommandSubcommandBuilder)[] = [] + commandList.concat(await registerCommands()); await registerCommandHandler(); await registerEvents(); console.log(`${colours.green}Registered commands and events${colours.none}`)