From 1dc7bb2f38629dda605d9ae6f1bfd9486feeff6d Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Mon, 12 Sep 2022 18:32:49 +0100 Subject: [PATCH 1/6] Update discord.js --- package.json | 3 +- src/commands/about.ts | 24 +- src/commands/audits.ts | 18 +- src/commands/ban.ts | 30 +-- src/commands/bunny.ts | 9 +- src/commands/clear.ts | 19 +- src/commands/help.ts | 17 +- src/commands/kick.ts | 30 +-- src/commands/mute.ts | 37 +-- src/commands/poll.ts | 7 +- src/commands/role.ts | 39 +-- src/commands/rules.ts | 4 +- src/commands/unmute.ts | 35 +-- src/commands/warn.ts | 25 +- src/contracts/ICommandReturnContext.ts | 7 - src/contracts/IEventReturnContext.ts | 6 - src/events/MemberEvents.ts | 8 +- src/events/MemberEvents/GuildMemberUpdate.ts | 6 +- src/events/MessageEvents.ts | 12 +- src/helpers/embeds/ErrorEmbed.ts | 45 +++- src/helpers/embeds/EventEmbed.ts | 74 ++++-- src/helpers/embeds/LogEmbed.ts | 77 ++++-- src/helpers/embeds/PublicEmbed.ts | 52 +++- src/type/command.ts | 3 + src/vylbot.ts | 9 +- yarn.lock | 248 ++++++++++++------- 26 files changed, 444 insertions(+), 400 deletions(-) delete mode 100644 src/contracts/ICommandReturnContext.ts delete mode 100644 src/contracts/IEventReturnContext.ts diff --git a/package.json b/package.json index 01955ab..8ff2a64 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,10 @@ "homepage": "https://github.com/Vylpes/vylbot-app", "funding": "https://ko-fi.com/vylpes", "dependencies": { + "@discordjs/rest": "^1.1.0", "@types/jest": "^27.0.3", "@types/uuid": "^8.3.4", - "discord.js": "^13.6.0", + "discord.js": "^14.3.0", "dotenv": "^10.0.0", "emoji-regex": "^9.2.0", "jest": "^27.4.5", diff --git a/src/commands/about.ts b/src/commands/about.ts index b688df6..0bcbeb9 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -1,5 +1,4 @@ -import { MessageActionRow, MessageButton } from "discord.js"; -import { MessageButtonStyles } from "discord.js/typings/enums"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; import { ICommandContext } from "../contracts/ICommandContext"; import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; @@ -14,29 +13,30 @@ export default class About extends Command { const fundingLink = process.env.ABOUT_FUNDING; const repoLink = process.env.ABOUT_REPO; - const embed = new PublicEmbed(context, "About", "") - .addField("Version", process.env.BOT_VER!, true) - .addField("Author", process.env.BOT_AUTHOR!, true) - .addField("Date", process.env.BOT_DATE!, true); + const embed = new PublicEmbed(context, "About", "Discord Bot made by Vylpes"); - const row = new MessageActionRow(); + embed.AddField("Version", process.env.BOT_VER!, true); + embed.AddField("Author", process.env.BOT_AUTHOR!, true); + embed.AddField("Date", process.env.BOT_DATE!, true); + + const row = new ActionRowBuilder(); if (repoLink) { row.addComponents( - new MessageButton() + new ButtonBuilder() .setURL(repoLink) .setLabel("Repo") - .setStyle(MessageButtonStyles.LINK)); + .setStyle(ButtonStyle.Link)); } if (fundingLink) { row.addComponents( - new MessageButton() + new ButtonBuilder() .setURL(fundingLink) .setLabel("Funding") - .setStyle(MessageButtonStyles.LINK)); + .setStyle(ButtonStyle.Link)); } - await embed.SendToCurrentChannel({ components: [row] }); + await embed.SendToCurrentChannel({ components: [ row ] }); } } \ No newline at end of file diff --git a/src/commands/audits.ts b/src/commands/audits.ts index c53fe37..cbdd110 100644 --- a/src/commands/audits.ts +++ b/src/commands/audits.ts @@ -57,16 +57,16 @@ export default class Audits extends Command { const audits = await Audit.FetchAuditsByUserId(userId, context.message.guild!.id); if (!audits || audits.length == 0) { - const publicEmbed = new PublicEmbed(context, "", "There are no audits logged for this user."); + const publicEmbed = new PublicEmbed(context, "Audits", "There are no audits logged for this user."); await publicEmbed.SendToCurrentChannel(); return; } - const publicEmbed = new PublicEmbed(context, "Audit Log", ""); + const publicEmbed = new PublicEmbed(context, "Audit Log", `Audits: ${audits.length}`); for (let audit of audits) { - publicEmbed.addField(`${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`, audit.WhenCreated.toString()); + publicEmbed.AddField(`${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`, audit.WhenCreated.toString()); } await publicEmbed.SendToCurrentChannel(); @@ -89,11 +89,11 @@ export default class Audits extends Command { return; } - const publicEmbed = new PublicEmbed(context, `Audit ${audit.AuditId.toUpperCase()}`, ""); + const publicEmbed = new PublicEmbed(context, "Audit", audit.AuditId.toUpperCase()); - publicEmbed.addField("Reason", audit.Reason || "*none*", true); - publicEmbed.addField("Type", AuditTools.TypeToFriendlyText(audit.AuditType), true); - publicEmbed.addField("Moderator", `<@${audit.ModeratorId}>`, true); + publicEmbed.AddField("Reason", audit.Reason || "*none*", true); + publicEmbed.AddField("Type", AuditTools.TypeToFriendlyText(audit.AuditType), true); + publicEmbed.AddField("Moderator", `<@${audit.ModeratorId}>`, true); await publicEmbed.SendToCurrentChannel(); } @@ -117,7 +117,7 @@ export default class Audits extends Command { await Audit.Remove(Audit, audit); - const publicEmbed = new PublicEmbed(context, "", "Audit cleared"); + const publicEmbed = new PublicEmbed(context, "Audit", "Audit cleared"); await publicEmbed.SendToCurrentChannel(); } @@ -138,7 +138,7 @@ export default class Audits extends Command { await audit.Save(Audit, audit); - const publicEmbed = new PublicEmbed(context, "", `Created new audit with ID \`${audit.AuditId}\``); + const publicEmbed = new PublicEmbed(context, "Audit", `Created new audit with ID \`${audit.AuditId}\``); await publicEmbed.SendToCurrentChannel(); } } \ No newline at end of file diff --git a/src/commands/ban.ts b/src/commands/ban.ts index 8d50132..6c1b496 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -4,7 +4,6 @@ import LogEmbed from "../helpers/embeds/LogEmbed"; import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; import Audit from "../entity/Audit"; import { AuditType } from "../constants/AuditType"; @@ -18,17 +17,14 @@ export default class Ban extends Command { ]; } - public override async execute(context: ICommandContext): Promise { + public override async execute(context: ICommandContext) { const targetUser = context.message.mentions.users.first(); if (!targetUser) { const embed = new ErrorEmbed(context, "User does not exist"); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed], - }; + return; } const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); @@ -37,10 +33,7 @@ export default class Ban extends Command { const embed = new ErrorEmbed(context, "User is not in this server"); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed], - }; + return; } const reasonArgs = context.args; @@ -48,21 +41,13 @@ export default class Ban extends Command { const reason = reasonArgs.join(" "); - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [], - }; - } + if (!context.message.guild?.available) return; if (!targetMember.bannable) { const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed], - }; + return; } const logEmbed = new LogEmbed(context, "Member Banned"); @@ -82,10 +67,5 @@ export default class Ban extends Command { await audit.Save(Audit, audit); } - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed], - }; } } \ No newline at end of file diff --git a/src/commands/bunny.ts b/src/commands/bunny.ts index a41e179..eb0952e 100644 --- a/src/commands/bunny.ts +++ b/src/commands/bunny.ts @@ -26,10 +26,11 @@ export default class Bunny extends Command { const result = await randomBunny(selectedSubreddit, 'hot'); if (result.IsSuccess) { - const embed = new PublicEmbed(context, result.Result!.Title, "") - .setImage(result.Result!.Url) - .setURL(`https://reddit.com${result.Result!.Permalink}`) - .setFooter({ text: `r/${selectedSubreddit} · ${result.Result!.Ups} upvotes` }); + const embed = new PublicEmbed(context, result.Result!.Title, ""); + + embed.SetImage(result.Result!.Url) + embed.SetURL(`https://reddit.com${result.Result!.Permalink}`) + embed.SetFooter(`r/${selectedSubreddit} · ${result.Result!.Ups} upvotes`); await embed.SendToCurrentChannel(); } else { diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 1bd3e55..5cf855b 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -3,7 +3,6 @@ import { TextChannel } from "discord.js"; import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; export default class Clear extends Command { constructor() { @@ -15,15 +14,12 @@ export default class Clear extends Command { ]; } - public override async execute(context: ICommandContext): Promise { + public override async execute(context: ICommandContext) { if (context.args.length == 0) { const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); await errorEmbed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [errorEmbed] - }; + return; } const totalToClear = Number.parseInt(context.args[0]); @@ -31,20 +27,13 @@ export default class Clear extends Command { if (!totalToClear || totalToClear <= 0 || totalToClear > 100) { const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); await errorEmbed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [errorEmbed] - }; + + return; } await (context.message.channel as TextChannel).bulkDelete(totalToClear); const embed = new PublicEmbed(context, "", `${totalToClear} message(s) were removed`); await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; } } \ No newline at end of file diff --git a/src/commands/help.ts b/src/commands/help.ts index 5366acf..48908f3 100644 --- a/src/commands/help.ts +++ b/src/commands/help.ts @@ -1,4 +1,3 @@ -import { existsSync, readdirSync } from "fs"; import { CoreClient } from "../client/client"; import { ICommandContext } from "../contracts/ICommandContext"; import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; @@ -33,12 +32,12 @@ export default class Help extends Command { .filter(x => !x.ServerId || x.ServerId == context.message.guild?.id); const cateogries = [...new Set(allCommands.map(x => x.Command.Category))]; - const embed = new PublicEmbed(context, "Commands", ""); + const embed = new PublicEmbed(context, "Commands", `Commands: ${allCommands.length}`); cateogries.forEach(category => { let filtered = allCommands.filter(x => x.Command.Category == category); - embed.addField(StringTools.Capitalise(category || "Uncategorised"), StringTools.CapitaliseArray(filtered.flatMap(x => x.Name)).join(", ")); + embed.AddField(StringTools.Capitalise(category || "Uncategorised"), StringTools.CapitaliseArray(filtered.flatMap(x => x.Name)).join(", ")); }); await embed.SendToCurrentChannel(); @@ -49,15 +48,15 @@ export default class Help extends Command { const exclusiveCommand = CoreClient.commandItems.find(x => x.Name == context.args[0] && x.ServerId == context.message.guild?.id); if (exclusiveCommand) { - const embed = new PublicEmbed(context, StringTools.Capitalise(exclusiveCommand.Name), ""); - embed.addField("Category", StringTools.Capitalise(exclusiveCommand.Command.Category || "Uncategorised")); - embed.addField("Required Roles", StringTools.Capitalise(exclusiveCommand.Command.Roles.join(", ")) || "Everyone"); + const embed = new PublicEmbed(context, StringTools.Capitalise(exclusiveCommand.Name), "Coming Soon"); + embed.AddField("Category", StringTools.Capitalise(exclusiveCommand.Command.Category || "Uncategorised")); + embed.AddField("Required Roles", StringTools.Capitalise(exclusiveCommand.Command.Roles.join(", ")) || "Everyone"); await embed.SendToCurrentChannel(); } else if (command) { - const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name), ""); - embed.addField("Category", StringTools.Capitalise(command.Command.Category || "Uncategorised")); - embed.addField("Required Roles", StringTools.Capitalise(command.Command.Roles.join(", ")) || "Everyone"); + const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name), "Coming Soon"); + embed.AddField("Category", StringTools.Capitalise(command.Command.Category || "Uncategorised")); + embed.AddField("Required Roles", StringTools.Capitalise(command.Command.Roles.join(", ")) || "Everyone"); await embed.SendToCurrentChannel(); } else { diff --git a/src/commands/kick.ts b/src/commands/kick.ts index c1b2572..81afe71 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -1,7 +1,6 @@ import { AuditType } from "../constants/AuditType"; import ErrorMessages from "../constants/ErrorMessages"; import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; import Audit from "../entity/Audit"; import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import LogEmbed from "../helpers/embeds/LogEmbed"; @@ -18,17 +17,14 @@ export default class Kick extends Command { ]; } - public override async execute(context: ICommandContext): Promise { + public override async execute(context: ICommandContext) { const targetUser = context.message.mentions.users.first(); if (!targetUser) { const embed = new ErrorEmbed(context, "User does not exist"); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); @@ -37,10 +33,7 @@ export default class Kick extends Command { const embed = new ErrorEmbed(context, "User is not in this server"); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } const reasonArgs = context.args; @@ -48,21 +41,13 @@ export default class Kick extends Command { const reason = reasonArgs.join(" "); - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; - } + if (!context.message.guild?.available) return; if (!targetMember.kickable) { const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } const logEmbed = new LogEmbed(context, "Member Kicked"); @@ -82,10 +67,5 @@ export default class Kick extends Command { await audit.Save(Audit, audit); } - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; } } \ No newline at end of file diff --git a/src/commands/mute.ts b/src/commands/mute.ts index 7a45b87..438441f 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -1,7 +1,6 @@ import { AuditType } from "../constants/AuditType"; import ErrorMessages from "../constants/ErrorMessages"; import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; import Audit from "../entity/Audit"; import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import LogEmbed from "../helpers/embeds/LogEmbed"; @@ -18,17 +17,14 @@ export default class Mute extends Command { ]; } - public override async execute(context: ICommandContext): Promise { + public override async execute(context: ICommandContext) { const targetUser = context.message.mentions.users.first(); if (!targetUser) { const embed = new ErrorEmbed(context, "User does not exist"); await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; + + return; } const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); @@ -37,10 +33,7 @@ export default class Mute extends Command { const embed = new ErrorEmbed(context, "User is not in this server"); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } const reasonArgs = context.args; @@ -48,21 +41,13 @@ export default class Mute extends Command { const reason = reasonArgs.join(" "); - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; - } + if (!context.message.guild?.available) return; if (!targetMember.manageable) { const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } const logEmbed = new LogEmbed(context, "Member Muted"); @@ -79,10 +64,7 @@ export default class Mute extends Command { const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } await targetMember.roles.add(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); @@ -95,10 +77,5 @@ export default class Mute extends Command { await audit.Save(Audit, audit); } - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; } } \ No newline at end of file diff --git a/src/commands/poll.ts b/src/commands/poll.ts index 3e84220..bbd501a 100644 --- a/src/commands/poll.ts +++ b/src/commands/poll.ts @@ -1,5 +1,4 @@ import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; @@ -11,7 +10,7 @@ export default class Poll extends Command { super.Category = "General"; } - public override async execute(context: ICommandContext): Promise { + public override async execute(context: ICommandContext) { const argsJoined = context.args.join(" "); const argsSplit = argsJoined.split(";"); @@ -49,7 +48,9 @@ export default class Poll extends Command { const embed = new PublicEmbed(context, title, description.join("\n")); - const message = await context.message.channel.send({ embeds: [ embed ]}); + const message = await embed.SendToCurrentChannel(); + + if (!message) return; description.forEach(async (value, index) => { await message.react(reactionEmojis[index]); diff --git a/src/commands/role.ts b/src/commands/role.ts index 952b590..a632cd4 100644 --- a/src/commands/role.ts +++ b/src/commands/role.ts @@ -3,7 +3,6 @@ import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Role as DiscordRole } from "discord.js"; import { Command } from "../type/command"; import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; import SettingsHelper from "../helpers/SettingsHelper"; import { readFileSync } from "fs"; import { default as eRole } from "../entity/Role"; @@ -56,7 +55,7 @@ export default class Role extends Command { return stringArray; } - public async SendRolesList(context: ICommandContext, serverId: string): Promise { + public async SendRolesList(context: ICommandContext, serverId: string) { const roles = await this.GetRolesList(context); const botPrefix = await SettingsHelper.GetServerPrefix(serverId); @@ -64,14 +63,9 @@ export default class Role extends Command { const embed = new PublicEmbed(context, "Roles", description); await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; } - public async ToggleRole(context: ICommandContext): Promise { + public async ToggleRole(context: ICommandContext) { const roles = await this.GetRolesList(context); const requestedRole = context.args.join(" "); @@ -79,10 +73,7 @@ export default class Role extends Command { const errorEmbed = new ErrorEmbed(context, "This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters"); await errorEmbed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [errorEmbed] - }; + return; } const assignRole = context.message.guild?.roles.cache.find(x => x.name == requestedRole); @@ -91,10 +82,7 @@ export default class Role extends Command { const errorEmbed = new ErrorEmbed(context, "The current server doesn't have this role. Please contact the server's moderators"); await errorEmbed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [errorEmbed] - }; + return; } const role = context.message.member?.roles.cache.find(x => x.name == requestedRole) @@ -104,35 +92,20 @@ export default class Role extends Command { } else { await this.RemoveRole(context, assignRole); } - - return { - commandContext: context, - embeds: [] - }; } - public async AddRole(context: ICommandContext, role: DiscordRole): Promise { + public async AddRole(context: ICommandContext, role: DiscordRole) { await context.message.member?.roles.add(role, "Toggled with role command"); const embed = new PublicEmbed(context, "", `Gave role: \`${role.name}\``); await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; } - public async RemoveRole(context: ICommandContext, role: DiscordRole): Promise { + public async RemoveRole(context: ICommandContext, role: DiscordRole) { await context.message.member?.roles.remove(role, "Toggled with role command"); const embed = new PublicEmbed(context, "", `Removed role: \`${role.name}\``); await embed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [embed] - }; } // ====== diff --git a/src/commands/rules.ts b/src/commands/rules.ts index 8730057..23d709b 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -37,8 +37,8 @@ export default class Rules extends Command { rules.forEach(rule => { const embed = new PublicEmbed(context, rule.title || "", rule.description?.join("\n") || ""); - embed.setImage(rule.image || ""); - embed.setFooter({ text: rule.footer || "" }); + embed.SetImage(rule.image || ""); + embed.SetFooter(rule.footer || ""); embeds.push(embed); }); diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts index b006dab..537919f 100644 --- a/src/commands/unmute.ts +++ b/src/commands/unmute.ts @@ -1,6 +1,5 @@ import ErrorMessages from "../constants/ErrorMessages"; import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import LogEmbed from "../helpers/embeds/LogEmbed"; import PublicEmbed from "../helpers/embeds/PublicEmbed"; @@ -16,17 +15,14 @@ export default class Unmute extends Command { ]; } - public override async execute(context: ICommandContext): Promise { + public override async execute(context: ICommandContext) { const targetUser = context.message.mentions.users.first(); if (!targetUser) { const embed = new ErrorEmbed(context, "User does not exist"); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); @@ -35,10 +31,7 @@ export default class Unmute extends Command { const embed = new ErrorEmbed(context, "User is not in this server"); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } const reasonArgs = context.args; @@ -46,21 +39,13 @@ export default class Unmute extends Command { const reason = reasonArgs.join(" "); - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; - } + if (!context.message.guild?.available) return; if (!targetMember.manageable) { const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } const logEmbed = new LogEmbed(context, "Member Unmuted"); @@ -77,20 +62,12 @@ export default class Unmute extends Command { const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound); await embed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [embed] - }; + return; } await targetMember.roles.remove(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); await logEmbed.SendToModLogsChannel(); await publicEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; } } \ No newline at end of file diff --git a/src/commands/warn.ts b/src/commands/warn.ts index 00f9efe..b5eb346 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -1,6 +1,5 @@ import { AuditType } from "../constants/AuditType"; import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandReturnContext from "../contracts/ICommandReturnContext"; import Audit from "../entity/Audit"; import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import LogEmbed from "../helpers/embeds/LogEmbed"; @@ -17,17 +16,14 @@ export default class Warn extends Command { ]; } - public override async execute(context: ICommandContext): Promise { + public override async execute(context: ICommandContext) { const user = context.message.mentions.users.first(); if (!user) { const errorEmbed = new ErrorEmbed(context, "User does not exist"); await errorEmbed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [errorEmbed] - }; + return; } const member = context.message.guild?.members.cache.find(x => x.user.id == user.id); @@ -36,10 +32,7 @@ export default class Warn extends Command { const errorEmbed = new ErrorEmbed(context, "User is not in this server"); await errorEmbed.SendToCurrentChannel(); - return { - commandContext: context, - embeds: [errorEmbed] - }; + return; } const reasonArgs = context.args; @@ -47,12 +40,7 @@ export default class Warn extends Command { const reason = reasonArgs.join(" "); - if (!context.message.guild?.available) { - return { - commandContext: context, - embeds: [] - }; - } + if (!context.message.guild?.available) return; const logEmbed = new LogEmbed(context, "Member Warned"); logEmbed.AddUser("User", user, true); @@ -70,10 +58,5 @@ export default class Warn extends Command { await audit.Save(Audit, audit); } - - return { - commandContext: context, - embeds: [logEmbed, publicEmbed] - }; } } \ No newline at end of file diff --git a/src/contracts/ICommandReturnContext.ts b/src/contracts/ICommandReturnContext.ts deleted file mode 100644 index 144e981..0000000 --- a/src/contracts/ICommandReturnContext.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { MessageEmbed } from "discord.js"; -import { ICommandContext } from "./ICommandContext"; - -export default interface ICommandReturnContext { - commandContext: ICommandContext, - embeds: MessageEmbed[] -} \ No newline at end of file diff --git a/src/contracts/IEventReturnContext.ts b/src/contracts/IEventReturnContext.ts deleted file mode 100644 index ccbe56d..0000000 --- a/src/contracts/IEventReturnContext.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { MessageEmbed } from "discord.js"; -import { ICommandContext } from "./ICommandContext"; - -export default interface ICommandReturnContext { - embeds: MessageEmbed[] -} \ No newline at end of file diff --git a/src/events/MemberEvents.ts b/src/events/MemberEvents.ts index 43d07e8..b3f5f8d 100644 --- a/src/events/MemberEvents.ts +++ b/src/events/MemberEvents.ts @@ -17,8 +17,8 @@ export default class MemberEvents extends Event { const embed = new EventEmbed(member.client, member.guild, "Member Joined"); embed.AddUser("User", member.user, true); - embed.addField("Created", member.user.createdAt.toISOString()); - embed.setFooter({ text: `Id: ${member.user.id}` }); + embed.AddField("Created", member.user.createdAt.toISOString()); + embed.SetFooter(`Id: ${member.user.id}`); const channel = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id); if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return; @@ -34,8 +34,8 @@ export default class MemberEvents extends Event { const embed = new EventEmbed(member.client, member.guild, "Member Left"); embed.AddUser("User", member.user, true); - embed.addField("Joined", member.joinedAt?.toISOString() || "n/a"); - embed.setFooter({ text: `Id: ${member.user.id}` }); + embed.AddField("Joined", member.joinedAt?.toISOString() || "n/a"); + embed.SetFooter(`Id: ${member.user.id}`); const channel = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id); if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return; diff --git a/src/events/MemberEvents/GuildMemberUpdate.ts b/src/events/MemberEvents/GuildMemberUpdate.ts index 0abd37d..6ba3a0b 100644 --- a/src/events/MemberEvents/GuildMemberUpdate.ts +++ b/src/events/MemberEvents/GuildMemberUpdate.ts @@ -20,9 +20,9 @@ export default class GuildMemberUpdate { const embed = new EventEmbed(this.oldMember.client, this.newMember.guild, "Nickname Changed"); embed.AddUser("User", this.newMember.user, true); - embed.addField("Before", oldNickname, true); - embed.addField("After", newNickname, true); - embed.setFooter({ text: `Id: ${this.newMember.user.id}` }); + embed.AddField("Before", oldNickname, true); + embed.AddField("After", newNickname, true); + embed.SetFooter(`Id: ${this.newMember.user.id}`); const channel = await SettingsHelper.GetSetting("event.member.update.channel", this.newMember.guild.id); if (!channel || channel.toLowerCase() != "true") return; diff --git a/src/events/MessageEvents.ts b/src/events/MessageEvents.ts index 9f37a9e..e769456 100644 --- a/src/events/MessageEvents.ts +++ b/src/events/MessageEvents.ts @@ -22,11 +22,11 @@ export default class MessageEvents extends Event { const embed = new EventEmbed(message.client, message.guild, "Message Deleted"); embed.AddUser("User", message.author, true); - embed.addField("Channel", message.channel.toString(), true); - embed.addField("Content", `\`\`\`${message.content || "*none*"}\`\`\``); + embed.AddField("Channel", message.channel.toString(), true); + embed.AddField("Content", `\`\`\`${message.content || "*none*"}\`\`\``); if (message.attachments.size > 0) { - embed.addField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``); + embed.AddField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``); } const channel = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id); @@ -48,9 +48,9 @@ export default class MessageEvents extends Event { const embed = new EventEmbed(newMessage.client, newMessage.guild, "Message Edited"); embed.AddUser("User", newMessage.author, true); - embed.addField("Channel", newMessage.channel.toString(), true); - embed.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``); - embed.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``); + embed.AddField("Channel", newMessage.channel.toString(), true); + embed.AddField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``); + embed.AddField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``); const channel = await SettingsHelper.GetSetting("event.message.update.channel", newMessage.guild.id); if (!channel || !newMessage.guild.channels.cache.find(x => x.name == channel)) return; diff --git a/src/helpers/embeds/ErrorEmbed.ts b/src/helpers/embeds/ErrorEmbed.ts index 958b8fc..8ac2754 100644 --- a/src/helpers/embeds/ErrorEmbed.ts +++ b/src/helpers/embeds/ErrorEmbed.ts @@ -1,19 +1,46 @@ -import { MessageEmbed, Permissions, TextChannel } from "discord.js"; +import { EmbedBuilder, Message, PermissionsBitField, TextChannel } from "discord.js"; import { ICommandContext } from "../../contracts/ICommandContext"; -export default class ErrorEmbed extends MessageEmbed { +export default class ErrorEmbed { public context: ICommandContext; + private _embedBuilder: EmbedBuilder; + constructor(context: ICommandContext, message: string) { - super(); - - super.setColor(0xd52803); - super.setDescription(message); + this._embedBuilder = new EmbedBuilder() + .setColor(0xd52803) + .setDescription(message); this.context = context; } - public async SendToCurrentChannel() { + // Detail Methods + public AddField(name: string, value: string, inline: boolean = false) { + this._embedBuilder.addFields([ + { + name, + value, + inline + } + ]) + } + + public SetFooter(text: string) { + this._embedBuilder.setFooter({ + text + }); + } + + public SetImage(imageUrl: string) { + this._embedBuilder.setImage(imageUrl); + } + + public SetURL(url: string) { + this._embedBuilder.setURL(url); + } + + // Send Methods + public async SendToCurrentChannel(): Promise { const channel = this.context.message.channel as TextChannel; const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); @@ -21,8 +48,8 @@ export default class ErrorEmbed extends MessageEmbed { const permissions = channel.permissionsFor(botMember); - if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return; + if (!permissions.has(PermissionsBitField.Flags.SendMessages)) return; - this.context.message.channel.send({ embeds: [ this ]}); + return this.context.message.channel.send({ embeds: [ this._embedBuilder ]}); } } \ No newline at end of file diff --git a/src/helpers/embeds/EventEmbed.ts b/src/helpers/embeds/EventEmbed.ts index 782f98a..92f66ca 100644 --- a/src/helpers/embeds/EventEmbed.ts +++ b/src/helpers/embeds/EventEmbed.ts @@ -1,37 +1,71 @@ -import { MessageEmbed, TextChannel, User, Guild, Client, Permissions } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; +import { EmbedBuilder, TextChannel, User, Guild, Client, PermissionsBitField, Message } from "discord.js"; import SettingsHelper from "../SettingsHelper"; -export default class EventEmbed extends MessageEmbed { +export default class EventEmbed { public guild: Guild; - + private client: Client; + private _embedBuilder: EmbedBuilder; constructor(client: Client, guild: Guild, title: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); + this._embedBuilder = new EmbedBuilder() + .setColor(0x3050ba) + .setTitle(title); this.guild = guild; this.client = client; } // Detail methods + public AddField(name: string, value: string, inline: boolean = false) { + this._embedBuilder.addFields([ + { + name, + value, + inline + } + ]) + } + + public SetFooter(text: string) { + this._embedBuilder.setFooter({ + text + }); + } + + public SetImage(imageUrl: string) { + this._embedBuilder.setImage(imageUrl); + } + public AddUser(title: string, user: User, setThumbnail: boolean = false) { - this.addField(title, `${user} \`${user.tag}\``, true); + this._embedBuilder.addFields([ + { + name: title, + value: `${user} \`${user.tag}\``, + inline: true, + } + ]) if (setThumbnail) { - this.setThumbnail(user.displayAvatarURL()); + this._embedBuilder.setThumbnail(user.displayAvatarURL()); } } public AddReason(message: string) { - this.addField("Reason", message || "*none*"); + this._embedBuilder.addFields([ + { + name: "Reason", + value: message || "*none*", + } + ]) + } + + public SetURL(url: string) { + this._embedBuilder.setURL(url); } // Send methods - public async SendToChannel(name: string) { + public async SendToChannel(name: string): Promise { const channel = this.guild.channels.cache .find(channel => channel.name == name) as TextChannel; @@ -46,38 +80,38 @@ export default class EventEmbed extends MessageEmbed { const permissions = channel.permissionsFor(botMember); - if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return; + if (!permissions.has(PermissionsBitField.Flags.SendMessages)) return; - channel.send({embeds: [ this ]}); + return channel.send({embeds: [ this._embedBuilder ]}); } - public async SendToMessageLogsChannel() { + public async SendToMessageLogsChannel(): Promise { const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.guild.id); if (!channelName) { return; } - this.SendToChannel(channelName); + return this.SendToChannel(channelName); } - public async SendToMemberLogsChannel() { + public async SendToMemberLogsChannel(): Promise { const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.guild.id); if (!channelName) { return; } - this.SendToChannel(channelName); + return this.SendToChannel(channelName); } - public async SendToModLogsChannel() { + public async SendToModLogsChannel(): Promise { const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.guild.id); if (!channelName) { return; } - this.SendToChannel(channelName); + return this.SendToChannel(channelName); } } \ No newline at end of file diff --git a/src/helpers/embeds/LogEmbed.ts b/src/helpers/embeds/LogEmbed.ts index 105c6a9..7c41260 100644 --- a/src/helpers/embeds/LogEmbed.ts +++ b/src/helpers/embeds/LogEmbed.ts @@ -1,36 +1,72 @@ -import { MessageEmbed, Permissions, TextChannel, User } from "discord.js"; +import { TextChannel, User, EmbedBuilder, PermissionsBitField, Message } from "discord.js"; import ErrorMessages from "../../constants/ErrorMessages"; import { ICommandContext } from "../../contracts/ICommandContext"; import SettingsHelper from "../SettingsHelper"; import ErrorEmbed from "./ErrorEmbed"; -export default class LogEmbed extends MessageEmbed { +export default class LogEmbed { public context: ICommandContext; + private _embedBuilder: EmbedBuilder; + constructor(context: ICommandContext, title: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); + this._embedBuilder = new EmbedBuilder() + .setColor(0x3050ba) + .setTitle(title); this.context = context; } // Detail methods + public AddField(name: string, value: string, inline: boolean = false) { + this._embedBuilder.addFields([ + { + name, + value, + inline + } + ]) + } + + public SetFooter(text: string) { + this._embedBuilder.setFooter({ + text + }); + } + + public SetImage(imageUrl: string) { + this._embedBuilder.setImage(imageUrl); + } + public AddUser(title: string, user: User, setThumbnail: boolean = false) { - this.addField(title, `${user} \`${user.tag}\``, true); + this._embedBuilder.addFields([ + { + name: title, + value: `${user} \`${user.tag}\``, + inline: true, + } + ]); if (setThumbnail) { - this.setThumbnail(user.displayAvatarURL()); + this._embedBuilder.setThumbnail(user.displayAvatarURL()); } } public AddReason(message: string) { - this.addField("Reason", message || "*none*"); + this._embedBuilder.addFields([ + { + name: "Reason", + value: message || "*none*", + } + ]) + } + + public SetURL(url: string) { + this._embedBuilder.setURL(url); } // Send methods - public async SendToCurrentChannel() { + public async SendToCurrentChannel(): Promise { const channel = this.context.message.channel as TextChannel; const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); @@ -38,51 +74,52 @@ export default class LogEmbed extends MessageEmbed { const permissions = channel.permissionsFor(botMember); - if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return; + if (!permissions.has(PermissionsBitField.Flags.SendMessages)) return; - this.context.message.channel.send({ embeds: [ this ]}); + return this.context.message.channel.send({ embeds: [ this._embedBuilder ]}); } - public SendToChannel(name: string) { + public async SendToChannel(name: string): Promise { const channel = this.context.message.guild?.channels.cache .find(channel => channel.name == name) as TextChannel; if (!channel) { const errorEmbed = new ErrorEmbed(this.context, ErrorMessages.ChannelNotFound); errorEmbed.SendToCurrentChannel(); + return; } - channel.send({ embeds: [ this ]}); + return await channel.send({ embeds: [ this._embedBuilder ]}); } - public async SendToMessageLogsChannel() { + public async SendToMessageLogsChannel(): Promise { const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.context.message.guild?.id!); if (!channelName) { return; } - this.SendToChannel(channelName); + return this.SendToChannel(channelName); } - public async SendToMemberLogsChannel() { + public async SendToMemberLogsChannel(): Promise { const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.context.message.guild?.id!); if (!channelName) { return; } - this.SendToChannel(channelName); + return this.SendToChannel(channelName); } - public async SendToModLogsChannel() { + public async SendToModLogsChannel(): Promise { const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.context.message.guild?.id!); if (!channelName) { return; } - this.SendToChannel(channelName); + return this.SendToChannel(channelName); } } \ No newline at end of file diff --git a/src/helpers/embeds/PublicEmbed.ts b/src/helpers/embeds/PublicEmbed.ts index 059826b..55ed5e4 100644 --- a/src/helpers/embeds/PublicEmbed.ts +++ b/src/helpers/embeds/PublicEmbed.ts @@ -1,26 +1,56 @@ -import { MessageEmbed, MessageOptions, Permissions, TextChannel } from "discord.js"; +import { EmbedBuilder, Message, MessageOptions, PermissionsBitField, TextChannel } from "discord.js"; import { ICommandContext } from "../../contracts/ICommandContext"; -export default class PublicEmbed extends MessageEmbed { +export default class PublicEmbed { public context: ICommandContext; + private _embedBuilder: EmbedBuilder; + constructor(context: ICommandContext, title: string, description: string) { - super(); - - super.setColor(0x3050ba); - super.setTitle(title); - super.setDescription(description); + this._embedBuilder = new EmbedBuilder() + .setColor(0x3050ba) + .setTitle(title) + .setDescription(description); this.context = context; } // Detail methods + public AddField(name: string, value: string, inline: boolean = false) { + this._embedBuilder.addFields([ + { + name, + value, + inline + } + ]) + } + + public SetFooter(text: string) { + this._embedBuilder.setFooter({ + text + }); + } + + public SetImage(imageUrl: string) { + this._embedBuilder.setImage(imageUrl); + } + public AddReason(message: string) { - this.addField("Reason", message || "*none*"); + this._embedBuilder.addFields([ + { + name: "Reason", + value: message || "*none*", + } + ]) + } + + public SetURL(url: string) { + this._embedBuilder.setURL(url); } // Send methods - public async SendToCurrentChannel(options?: MessageOptions) { + public async SendToCurrentChannel(options?: MessageOptions): Promise { const channel = this.context.message.channel as TextChannel; const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); @@ -28,8 +58,8 @@ export default class PublicEmbed extends MessageEmbed { const permissions = channel.permissionsFor(botMember); - if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return; + if (!permissions.has(PermissionsBitField.Flags.SendMessages)) return; - this.context.message.channel.send({ embeds: [ this ], ...options}); + return this.context.message.channel.send({ embeds: [ this._embedBuilder ], ...options}); } } \ No newline at end of file diff --git a/src/type/command.ts b/src/type/command.ts index 1d04686..2ffb39a 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,7 +1,10 @@ import { CommandResponse } from "../constants/CommandResponse"; import { ICommandContext } from "../contracts/ICommandContext"; +import { SlashCommandBuilder } from "discord.js"; export class Command { + public SlashCommandBuilder: SlashCommandBuilder; + public Roles: string[]; public Category?: string; diff --git a/src/vylbot.ts b/src/vylbot.ts index 1d6a420..33fc026 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -1,7 +1,7 @@ import { CoreClient } from "./client/client"; import * as dotenv from "dotenv"; import registry from "./registry"; -import { Intents } from "discord.js"; +import { IntentsBitField } from "discord.js"; dotenv.config(); @@ -20,9 +20,10 @@ requiredConfigs.forEach(config => { }); const client = new CoreClient([ - Intents.FLAGS.GUILDS, - Intents.FLAGS.GUILD_MESSAGES, - Intents.FLAGS.GUILD_MEMBERS, + IntentsBitField.Flags.Guilds, + IntentsBitField.Flags.GuildMessages, + IntentsBitField.Flags.GuildMembers, + IntentsBitField.Flags.MessageContent, ]); registry.RegisterCommands(); diff --git a/yarn.lock b/yarn.lock index 52ebe72..f543110 100644 --- a/yarn.lock +++ b/yarn.lock @@ -286,21 +286,34 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@discordjs/builders@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.11.0.tgz#4102abe3e0cd093501f3f71931df43eb92f5b0cc" - integrity sha512-ZTB8yJdJKrKlq44dpWkNUrAtEJEq0gqpb7ASdv4vmq6/mZal5kOv312hQ56I/vxwMre+VIkoHquNUAfnTbiYtg== +"@discordjs/builders@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.2.0.tgz#4f5059e258c30a26931ae384985ef8c6b371ba05" + integrity sha512-ARy4BUTMU+S0ZI6605NDqfWO+qZqV2d/xfY32z3hVSsd9IaAKJBZ1ILTZLy87oIjW8+gUpQmk9Kt0ZP9bmmd8Q== dependencies: - "@sindresorhus/is" "^4.2.0" - discord-api-types "^0.26.0" - ts-mixer "^6.0.0" - tslib "^2.3.1" - zod "^3.11.6" + "@sapphire/shapeshift" "^3.5.1" + discord-api-types "^0.37.3" + fast-deep-equal "^3.1.3" + ts-mixer "^6.0.1" + tslib "^2.4.0" -"@discordjs/collection@^0.4.0": - version "0.4.0" - resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.4.0.tgz#b6488286a1cc7b41b644d7e6086f25a1c1e6f837" - integrity sha512-zmjq+l/rV35kE6zRrwe8BHqV78JvIh2ybJeZavBi5NySjWXqN3hmmAKg7kYMMXSeiWtSsMoZ/+MQi0DiQWy2lw== +"@discordjs/collection@^1.0.1", "@discordjs/collection@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.1.0.tgz#5f9f926404fd48ccde86a0d2268f202cbec77833" + integrity sha512-PQ2Bv6pnT7aGPCKWbvvNRww5tYCGpggIQVgpuF9TdDPeR6n6vQYxezXiLVOS9z2B62Dp4c+qepQ15SgJbLYtCQ== + +"@discordjs/rest@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.1.0.tgz#5c283571a22b911ca334316245487af40baffe8b" + integrity sha512-yCrthRTQeUyNThQEpCk7bvQJlwQmz6kU0tf3dcWBv2WX3Bncl41x7Wc+v5b5OsIxfNYq38PvVtWircu9jtYZug== + dependencies: + "@discordjs/collection" "^1.0.1" + "@sapphire/async-queue" "^1.5.0" + "@sapphire/snowflake" "^3.2.2" + discord-api-types "^0.37.3" + file-type "^17.1.6" + tslib "^2.4.0" + undici "^5.9.1" "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -487,12 +500,25 @@ "@types/yargs" "^16.0.0" chalk "^4.0.0" -"@sapphire/async-queue@^1.1.9": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.1.tgz#9d861e626dbffae02d808e13f823d4510e450a78" - integrity sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g== +"@sapphire/async-queue@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8" + integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== -"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.2.0": +"@sapphire/shapeshift@^3.5.1": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.6.0.tgz#988ff6576162a581a29bc26deb5492f7d1bf419f" + integrity sha512-tu2WLRdo5wotHRvsCkspg3qMiP6ETC3Q1dns1Q5V6zKUki+1itq6AbhMwohF9ZcLoYqg+Y8LkgRRtVxxTQVTBQ== + dependencies: + fast-deep-equal "^3.1.3" + lodash.uniqwith "^4.5.0" + +"@sapphire/snowflake@^3.2.2": + version "3.2.2" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.2.2.tgz#faacdc1b5f7c43145a71eddba917de2b707ef780" + integrity sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ== + +"@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== @@ -523,6 +549,11 @@ dependencies: defer-to-connect "^2.0.0" +"@tokenizer/token@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" + integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -622,14 +653,6 @@ dependencies: "@types/node" "*" -"@types/node-fetch@^2.5.12": - version "2.6.1" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" - integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== - dependencies: - "@types/node" "*" - form-data "^3.0.0" - "@types/node@*": version "18.0.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" @@ -662,7 +685,7 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== -"@types/ws@^8.2.2": +"@types/ws@^8.5.3": version "8.5.3" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== @@ -1181,25 +1204,27 @@ diff-sequences@^27.4.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== -discord-api-types@^0.26.0: - version "0.26.1" - resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.26.1.tgz#726f766ddc37d60da95740991d22cb6ef2ed787b" - integrity sha512-T5PdMQ+Y1MEECYMV5wmyi9VEYPagEDEi4S0amgsszpWY0VB9JJ/hEvM6BgLhbdnKky4gfmZEXtEEtojN8ZKJQQ== +discord-api-types@^0.37.3: + version "0.37.9" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.9.tgz#3ceba0f047c5239967c50b180fe9d72c55c82464" + integrity sha512-mAJv7EcDDo6YztR3XSIED2IAHJcAlzWFD31Q2cHLURMKPb0CW0TM/r32HhAHO9uHw74N3cviOzJIZfZVB/e/5A== -discord.js@^13.6.0: - version "13.6.0" - resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.6.0.tgz#d8a8a591dbf25cbcf9c783d5ddf22c4694860475" - integrity sha512-tXNR8zgsEPxPBvGk3AQjJ9ljIIC6/LOPjzKwpwz8Y1Q2X66Vi3ZqFgRHYwnHKC0jC0F+l4LzxlhmOJsBZDNg9g== +discord.js@^14.3.0: + version "14.3.0" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.3.0.tgz#9e43df7e4d2d14b11f3de751236e983159c3d3d0" + integrity sha512-CpIwoAAuELiHSgVKRMzsCADS6ZlJwAZ9RlvcJYdEgS00aW36dSvXyBgE+S3pigkc7G+jU6BEalMUWIJFveqrBQ== dependencies: - "@discordjs/builders" "^0.11.0" - "@discordjs/collection" "^0.4.0" - "@sapphire/async-queue" "^1.1.9" - "@types/node-fetch" "^2.5.12" - "@types/ws" "^8.2.2" - discord-api-types "^0.26.0" - form-data "^4.0.0" - node-fetch "^2.6.1" - ws "^8.4.0" + "@discordjs/builders" "^1.2.0" + "@discordjs/collection" "^1.1.0" + "@discordjs/rest" "^1.1.0" + "@sapphire/snowflake" "^3.2.2" + "@types/ws" "^8.5.3" + discord-api-types "^0.37.3" + fast-deep-equal "^3.1.3" + lodash.snakecase "^4.1.1" + tslib "^2.4.0" + undici "^5.9.1" + ws "^8.8.1" domexception@^2.0.1: version "2.0.1" @@ -1319,6 +1344,11 @@ expect@^27.4.2: jest-message-util "^27.4.2" jest-regex-util "^27.4.0" +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -1336,6 +1366,15 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +file-type@^17.1.6: + version "17.1.6" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-17.1.6.tgz#18669e0577a4849ef6e73a41f8bdf1ab5ae21023" + integrity sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw== + dependencies: + readable-web-to-node-stream "^3.0.2" + strtok3 "^7.0.0-alpha.9" + token-types "^5.0.0-alpha.2" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1360,15 +1399,6 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1559,7 +1589,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2201,6 +2231,16 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.uniqwith@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz#7a0cbf65f43b5928625a9d4d0dc54b18cadc7ef3" + integrity sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q== + lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -2323,13 +2363,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -node-fetch@^2.6.1: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -2454,6 +2487,11 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +peek-readable@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec" + integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -2553,6 +2591,22 @@ readable-stream@2.3.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-web-to-node-stream@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" + integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== + dependencies: + readable-stream "^3.6.0" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -2612,7 +2666,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1: +safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -2738,6 +2792,13 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -2762,6 +2823,14 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strtok3@^7.0.0-alpha.9: + version "7.0.0" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5" + integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ== + dependencies: + "@tokenizer/token" "^0.3.0" + peek-readable "^5.0.0" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -2849,6 +2918,14 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +token-types@^5.0.0-alpha.2: + version "5.0.1" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4" + integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg== + dependencies: + "@tokenizer/token" "^0.3.0" + ieee754 "^1.2.1" + tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -2865,11 +2942,6 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - ts-essentials@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" @@ -2889,16 +2961,21 @@ ts-jest@^27.1.2: semver "7.x" yargs-parser "20.x" -ts-mixer@^6.0.0: +ts-mixer@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.1.tgz#7c2627fb98047eb5f3c7f2fee39d1521d18fe87a" integrity sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg== -tslib@^2.1.0, tslib@^2.3.1: +tslib@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -2951,12 +3028,17 @@ typescript@^4.5.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== +undici@^5.9.1: + version "5.10.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.10.0.tgz#dd9391087a90ccfbd007568db458674232ebf014" + integrity sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g== + universalify@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -2996,11 +3078,6 @@ walker@^1.0.7: dependencies: makeerror "1.0.12" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -3023,14 +3100,6 @@ whatwg-mimetype@^2.3.0: resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - whatwg-url@^8.0.0, whatwg-url@^8.5.0: version "8.7.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" @@ -3081,10 +3150,10 @@ ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== -ws@^8.4.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" - integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +ws@^8.8.1: + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== xml-name-validator@^3.0.0: version "3.0.0" @@ -3167,8 +3236,3 @@ zen-observable@0.8.15: version "0.8.15" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== - -zod@^3.11.6: - version "3.14.4" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.4.tgz#e678fe9e5469f4663165a5c35c8f3dc66334a5d6" - integrity sha512-U9BFLb2GO34Sfo9IUYp0w3wJLlmcyGoMd75qU9yf+DrdGA4kEx6e+l9KOkAlyUO0PSQzZCa3TR4qVlcmwqSDuw== -- 2.43.4 From 5758cbd7f0b3b609bfa32c2d5dd6f99e376d7d21 Mon Sep 17 00:00:00 2001 From: Ethan Lane Date: Sat, 17 Sep 2022 12:50:01 +0100 Subject: [PATCH 2/6] Migrate to slash commands --- .dev.env | 2 +- .prod.env | 2 +- .stage.env | 2 +- src/client/client.ts | 5 +- src/client/events.ts | 80 ++++-- src/client/util.ts | 92 +++---- src/commands/501231711271780357/entry.ts | 21 +- src/commands/501231711271780357/lobby.ts | 173 ++++++------- src/commands/about.ts | 34 ++- src/commands/audits.ts | 207 +++++++++++----- src/commands/ban.ts | 98 ++++---- src/commands/bunny.ts | 29 ++- src/commands/clear.ts | 35 +-- src/commands/code.ts | 79 +++--- src/commands/config.ts | 198 ++++++++++----- src/commands/disable.ts | 97 ++++---- src/commands/help.ts | 67 ----- src/commands/ignore.ts | 22 +- src/commands/kick.ts | 106 ++++---- src/commands/mute.ts | 100 ++++---- src/commands/poll.ts | 68 ----- src/commands/role.ts | 245 ++++++++----------- src/commands/rules.ts | 42 ++-- src/commands/say.ts | 26 -- src/commands/setup.ts | 24 +- src/commands/unmute.ts | 99 ++++---- src/commands/warn.ts | 90 ++++--- src/constants/EmbedColours.ts | 3 + src/events/MemberEvents.ts | 62 +++-- src/events/MemberEvents/GuildMemberUpdate.ts | 37 ++- src/events/MessageEvents.ts | 80 ++++-- src/helpers/embeds/ErrorEmbed.ts | 55 ----- src/helpers/embeds/EventEmbed.ts | 117 --------- src/helpers/embeds/LogEmbed.ts | 125 ---------- src/helpers/embeds/PublicEmbed.ts | 65 ----- src/registry.ts | 6 - src/type/command.ts | 12 +- src/vylbot.ts | 2 +- 38 files changed, 1192 insertions(+), 1415 deletions(-) delete mode 100644 src/commands/help.ts delete mode 100644 src/commands/poll.ts delete mode 100644 src/commands/say.ts create mode 100644 src/constants/EmbedColours.ts delete mode 100644 src/helpers/embeds/ErrorEmbed.ts delete mode 100644 src/helpers/embeds/EventEmbed.ts delete mode 100644 src/helpers/embeds/LogEmbed.ts delete mode 100644 src/helpers/embeds/PublicEmbed.ts diff --git a/.dev.env b/.dev.env index 2b8f6a5..e4e3d28 100644 --- a/.dev.env +++ b/.dev.env @@ -9,8 +9,8 @@ BOT_TOKEN= BOT_VER=3.1 BOT_AUTHOR=Vylpes -BOT_DATE=06 Sep 2022 BOT_OWNERID=147392775707426816 +BOT_CLIENTID=682942374040961060 BOT_PREFIX=d! ABOUT_FUNDING=https://ko-fi.com/vylpes diff --git a/.prod.env b/.prod.env index 2884bbe..ff055a4 100644 --- a/.prod.env +++ b/.prod.env @@ -9,8 +9,8 @@ BOT_TOKEN= BOT_VER=3.1 BOT_AUTHOR=Vylpes -BOT_DATE=06 Sep 2022 BOT_OWNERID=147392775707426816 +BOT_CLIENTID=680083120896081954 BOT_PREFIX=v! ABOUT_FUNDING=https://ko-fi.com/vylpes diff --git a/.stage.env b/.stage.env index bb57e4d..6788ec2 100644 --- a/.stage.env +++ b/.stage.env @@ -9,8 +9,8 @@ BOT_TOKEN= BOT_VER=3.1 BOT_AUTHOR=Vylpes -BOT_DATE=06 Sep 2022 BOT_OWNERID=147392775707426816 +BOT_CLIENTID=1016767908740857949 BOT_PREFIX=s! ABOUT_FUNDING=https://ko-fi.com/vylpes diff --git a/src/client/client.ts b/src/client/client.ts index 1f46d7e..0eb2c81 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -47,14 +47,13 @@ export class CoreClient extends Client { return; }); - super.on("messageCreate", (message) => { - this._events.onMessageCreate(message, CoreClient._commandItems) - }); + super.on("interactionCreate", this._events.onInteractionCreate); super.on("ready", this._events.onReady); super.login(process.env.BOT_TOKEN); this._util.loadEvents(this, CoreClient._eventItems); + this._util.loadSlashCommands(this); } public static RegisterCommand(name: string, command: Command, serverId?: string) { diff --git a/src/client/events.ts b/src/client/events.ts index 059df52..79f618f 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,6 +1,10 @@ -import { Message } from "discord.js"; +import { CommandInteraction, GuildMemberRoleManager, Interaction, Message, messageLink } from "discord.js"; +import { CommandResponse } from "../constants/CommandResponse"; +import ErrorMessages from "../constants/ErrorMessages"; import ICommandItem from "../contracts/ICommandItem"; import SettingsHelper from "../helpers/SettingsHelper"; +import StringTools from "../helpers/StringTools"; +import { CoreClient } from "./client"; import { Util } from "./util"; export class Events { @@ -10,24 +14,70 @@ export class Events { this._util = new Util(); } - // Emit when a message is sent - // Used to check for commands - public async onMessageCreate(message: Message, commands: ICommandItem[]) { - if (!message.guild) return; - if (message.author.bot) return; + public async onInteractionCreate(interaction: Interaction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guild) return; + if (!interaction.member) return; - const prefix = await SettingsHelper.GetSetting("bot.prefix", message.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guild.id); + const disabledCommands = disabledCommandsString?.split(","); - if (!prefix) return; + if (disabledCommands?.find(x => x == interaction.commandName)) { + interaction.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); - if (message.content.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase()) { - const args = message.content.substring(prefix.length).split(" "); - const name = args.shift(); - - if (!name) return; - - await this._util.loadCommand(name, args, message, commands); + return; } + + const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId); + const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId); + + let itemToUse: ICommandItem; + + if (!itemForServer) { + if (!item) { + interaction.reply('Command not found'); + return; + } + + itemToUse = item; + } else { + itemToUse = itemForServer; + } + + const requiredRoles = itemToUse.Command.Roles; + + if (interaction.member.user.id != process.env.BOT_OWNERID && interaction.member.user.id != interaction.guild.ownerId) { + for (const i in requiredRoles) { + const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, interaction.guildId!); + + if (!setting) { + interaction.reply("Unable to verify if you have this role, please contact your bot administrator"); + return; + } + + const roles = interaction.member.roles as GuildMemberRoleManager; + + if (!roles.cache.find(role => role.name == setting)) { + interaction.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`); + return; + } + } + } + + const precheckResponse = itemToUse.Command.precheck(interaction); + const precheckAsyncResponse = await itemToUse.Command.precheckAsync(interaction); + + if (precheckResponse != CommandResponse.Ok) { + interaction.reply(ErrorMessages.GetErrorMessage(precheckResponse)); + return; + } + + if (precheckAsyncResponse != CommandResponse.Ok) { + interaction.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse)); + return; + } + + itemToUse.Command.execute(interaction); } // Emit when bot is logged in and ready to use diff --git a/src/client/util.ts b/src/client/util.ts index f136e26..832a84b 100644 --- a/src/client/util.ts +++ b/src/client/util.ts @@ -1,5 +1,5 @@ // Required Components -import { Client, Message } from "discord.js"; +import { Client, Message, REST, Routes, SlashCommandBuilder } from "discord.js"; import { ICommandContext } from "../contracts/ICommandContext"; import ICommandItem from "../contracts/ICommandItem"; import IEventItem from "../contracts/IEventItem"; @@ -7,77 +7,51 @@ import SettingsHelper from "../helpers/SettingsHelper"; import StringTools from "../helpers/StringTools"; import { CommandResponse } from "../constants/CommandResponse"; import ErrorMessages from "../constants/ErrorMessages"; +import { CoreClient } from "./client"; // Util Class export class Util { - public async loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]) { - if (!message.member) return; - if (!message.guild) return; + public loadSlashCommands(client: Client) { + const registeredCommands = CoreClient.commandItems; - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", message.guild?.id); - const disabledCommands = disabledCommandsString?.split(","); + const globalCommands = registeredCommands.filter(x => !x.ServerId); + const guildCommands = registeredCommands.filter(x => x.ServerId); - if (disabledCommands?.find(x => x == name)) { - message.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); - return; - } + const globalCommandData: SlashCommandBuilder[] = globalCommands + .filter(x => x.Command.CommandBuilder) + .flatMap(x => x.Command.CommandBuilder); - const item = commands.find(x => x.Name == name && !x.ServerId); - const itemForServer = commands.find(x => x.Name == name && x.ServerId == message.guild?.id); + const guildIds: string[] = []; - let itemToUse: ICommandItem; - - if (!itemForServer) { - if (!item) { - message.reply('Command not found'); - return; + for (let command of guildCommands) { + if (!guildIds.find(x => x == command.ServerId)) { + guildIds.push(command.ServerId!); } - - itemToUse = item; - } else { - itemToUse = itemForServer; } - const requiredRoles = itemToUse.Command.Roles; + const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN!); - if (message.author.id != process.env.BOT_OWNERID && message.author.id != message.guild.ownerId) { - for (const i in requiredRoles) { - if (message.guild) { - const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, message.guild?.id); - - if (!setting) { - message.reply("Unable to verify if you have this role, please contact your bot administrator"); - return; - } - - if (!message.member.roles.cache.find(role => role.name == setting)) { - message.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`); - return; - } + rest.put( + Routes.applicationCommands(process.env.BOT_CLIENTID!), + { + body: globalCommandData + } + ); + + for (let guild of guildIds) { + const guildCommandData = guildCommands.filter(x => x.ServerId == guild) + .filter(x => x.Command.CommandBuilder) + .flatMap(x => x.Command.CommandBuilder); + + if (!client.guilds.cache.has(guild)) continue; + + rest.put( + Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild), + { + body: guildCommandData } - } + ) } - - const context: ICommandContext = { - name: name, - args: args, - message: message - }; - - const precheckResponse = itemToUse.Command.precheck(context); - const precheckAsyncResponse = await itemToUse.Command.precheckAsync(context); - - if (precheckResponse != CommandResponse.Ok) { - message.reply(ErrorMessages.GetErrorMessage(precheckResponse)); - return; - } - - if (precheckAsyncResponse != CommandResponse.Ok) { - message.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse)); - return; - } - - itemToUse.Command.execute(context); } // Load the events diff --git a/src/commands/501231711271780357/entry.ts b/src/commands/501231711271780357/entry.ts index 2c0f4ae..f4e206d 100644 --- a/src/commands/501231711271780357/entry.ts +++ b/src/commands/501231711271780357/entry.ts @@ -1,5 +1,6 @@ +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; import { ICommandContext } from "../../contracts/ICommandContext"; -import PublicEmbed from "../../helpers/embeds/PublicEmbed"; import SettingsHelper from "../../helpers/SettingsHelper"; import { Command } from "../../type/command"; @@ -11,15 +12,23 @@ export default class Entry extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName('entry') + .setDescription('Sends the entry embed'); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!interaction.channel) return; - const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", context.message.guild.id) || "rules"; + const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", interaction.guildId) || "rules"; - const embedInfo = new PublicEmbed(context, "", `Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Welcome") + .setDescription(`Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`); - await embedInfo.SendToCurrentChannel(); + await interaction.channel.send({ embeds: [ embed ]}); } } \ No newline at end of file diff --git a/src/commands/501231711271780357/lobby.ts b/src/commands/501231711271780357/lobby.ts index ec74a7f..023cfa3 100644 --- a/src/commands/501231711271780357/lobby.ts +++ b/src/commands/501231711271780357/lobby.ts @@ -1,11 +1,9 @@ -import { TextChannel } from "discord.js"; +import { CommandInteraction, GuildMemberRoleManager, SlashCommandBuilder, TextChannel } from "discord.js"; import { ICommandContext } from "../../contracts/ICommandContext"; import { Command } from "../../type/command"; import { default as eLobby } from "../../entity/501231711271780357/Lobby"; import SettingsHelper from "../../helpers/SettingsHelper"; -import PublicEmbed from "../../helpers/embeds/PublicEmbed"; import { readFileSync } from "fs"; -import ErrorEmbed from "../../helpers/embeds/ErrorEmbed"; import BaseEntity from "../../contracts/BaseEntity"; export default class Lobby extends Command { @@ -13,32 +11,60 @@ export default class Lobby extends Command { super(); super.Category = "General"; + + super.CommandBuilder = new SlashCommandBuilder() + .setName('lobby') + .setDescription('Attempt to organise a lobby') + .addSubcommand(subcommand => + subcommand + .setName('config') + .setDescription('Configure the lobby command (mods only)') + .addStringOption(option => + option + .setName('action') + .setDescription('Add or remove a channel to the lobby list') + .addChoices( + { name: 'add', value: 'add' }, + { name: 'remove', value: 'remove' }, + )) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel')) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role to ping on request')) + .addNumberOption(option => + option + .setName('cooldown') + .setDescription('The cooldown in minutes')) + .addStringOption(option => + option + .setName('name') + .setDescription('The game name'))); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - switch (context.args[0]) { + switch (interaction.options.getSubcommand()) { case "config": - await this.UseConfig(context); + await this.Configure(interaction); + break; + case "request": + await this.Request(interaction); break; - default: - await this.UseDefault(context); } } - // ======= - // Default - // ======= - - private async UseDefault(context: ICommandContext) { - const channel = context.message.channel as TextChannel; - const channelId = channel.id; - - const lobby = await eLobby.FetchOneByChannelId(channelId); + private async Request(interaction: CommandInteraction) { + if (!interaction.channelId) return; + + const lobby = await eLobby.FetchOneByChannelId(interaction.channelId); if (!lobby) { - this.SendDisabled(context); + await interaction.reply('This channel is disabled from using the lobby command.'); return; } @@ -48,112 +74,91 @@ export default class Lobby extends Command { // If it was less than x minutes ago if (lobby.LastUsed.getTime() > timeAgo) { - this.SendOnCooldown(context, timeLength, new Date(timeNow), lobby.LastUsed); + const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60); + + await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); return; } - await this.RequestLobby(context, lobby); - } - - private async RequestLobby(context: ICommandContext, lobby: eLobby) { lobby.MarkAsUsed(); await lobby.Save(eLobby, lobby); - context.message.channel.send(`${context.message.author} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`); + await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@${lobby.RoleId}>`); } - private SendOnCooldown(context: ICommandContext, timeLength: number, timeNow: Date, timeUsed: Date) { - const timeLeft = Math.ceil((timeLength - (timeNow.getTime() - timeUsed.getTime())) / 1000 / 60); + private async Configure(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!interaction.member) return; - context.message.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); - } + const moderatorRole = await SettingsHelper.GetSetting("role.moderator", interaction.guildId); - private SendDisabled(context: ICommandContext) { - context.message.reply("This channel hasn't been setup for lobbies."); - } - - // ====== - // Config - // ====== - private async UseConfig(context: ICommandContext) { - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id); - - if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) { - const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command"); - await errorEmbed.SendToCurrentChannel(); + const roleManager = interaction.member.roles as GuildMemberRoleManager; + if (!roleManager.cache.find(x => x.name == moderatorRole)) { + await interaction.reply('Sorry, you must be a moderator to be able to configure this command.'); return; } - switch (context.args[1]) { + const action = interaction.options.get('action'); + + if (!action || !action.value) { + await interaction.reply('Action is required.'); + return; + } + + switch (action.value) { case "add": - await this.AddLobbyConfig(context); + await this.AddLobbyConfig(interaction); break; case "remove": - await this.RemoveLobbyConfig(context); + await this.RemoveLobbyConfig(interaction); break; - case "help": default: - await this.SendConfigHelp(context); + await interaction.reply('Action not found.'); } } - private async SendConfigHelp(context: ICommandContext) { - const helpText = readFileSync(`${process.cwd()}/data/usage/lobby.txt`).toString(); - - const embed = new PublicEmbed(context, "Configure Lobby Command", helpText); - await embed.SendToCurrentChannel(); - } - - private async AddLobbyConfig(context: ICommandContext) { - const channel = context.message.guild!.channels.cache.find(x => x.id == context.args[2]); - const role = context.message.guild!.roles.cache.find(x => x.id == context.args[3]); - const cooldown = Number(context.args[4]) || 30; - const gameName = context.args.splice(5).join(" "); - - if (!channel) { - const errorEmbed = new ErrorEmbed(context, "The channel id you provided is invalid or channel does not exist."); - errorEmbed.SendToCurrentChannel(); + private async AddLobbyConfig(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + const role = interaction.options.get('role'); + const cooldown = interaction.options.get('cooldown'); + const gameName = interaction.options.get('name'); + if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) { + await interaction.reply('Fields are required.'); return; } - if (!role) { - const errorEmbed = new ErrorEmbed(context, "The role id you provided is invalid or role does not exist."); - errorEmbed.SendToCurrentChannel(); - - return; - } - - const lobby = await eLobby.FetchOneByChannelId(channel.id); + const lobby = await eLobby.FetchOneByChannelId(channel.channel.id); if (lobby) { - const errorEmbed = new ErrorEmbed(context, "This channel has already been setup."); - errorEmbed.SendToCurrentChannel(); - + await interaction.reply('This channel has already been setup.'); return; } - const entity = new eLobby(channel.id, role.id, cooldown, gameName); + const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string); await entity.Save(eLobby, entity); - const embed = new PublicEmbed(context, "", `Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes\` and will ping \`${role.name}\` on use`); - await embed.SendToCurrentChannel(); + await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes \` and will ping \`${role.name}\` on use`); } - private async RemoveLobbyConfig(context: ICommandContext) { - const entity = await eLobby.FetchOneByChannelId(context.args[2]); + private async RemoveLobbyConfig(interaraction: CommandInteraction) { + const channel = interaraction.options.get('channel'); + + if (!channel || !channel.channel) { + await interaraction.reply('Channel is required.'); + return; + } + + const entity = await eLobby.FetchOneByChannelId(channel.channel.id); if (!entity) { - const errorEmbed = new ErrorEmbed(context, "The channel id you provided has not been setup as a lobby, unable to remove."); - await errorEmbed.SendToCurrentChannel(); - + await interaraction.reply('Channel not found.'); return; } await BaseEntity.Remove(eLobby, entity); - const embed = new PublicEmbed(context, "", `Removed <#${context.args[2]}> from the list of lobby channels`); - await embed.SendToCurrentChannel(); + await interaraction.reply(`Removed <#${channel.channel.name}> from the list of lobby channels`); } } \ No newline at end of file diff --git a/src/commands/about.ts b/src/commands/about.ts index 0bcbeb9..b743d05 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -1,23 +1,39 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, Interaction, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; import { ICommandContext } from "../contracts/ICommandContext"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; export default class About extends Command { constructor() { super(); super.Category = "General"; + + super.CommandBuilder = new SlashCommandBuilder() + .setName('about') + .setDescription('About VylBot'); } - public override async execute(context: ICommandContext) { + public override async execute(interaction: CommandInteraction) { const fundingLink = process.env.ABOUT_FUNDING; const repoLink = process.env.ABOUT_REPO; - const embed = new PublicEmbed(context, "About", "Discord Bot made by Vylpes"); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("About") + .setDescription("Discord Bot made by Vylpes"); - embed.AddField("Version", process.env.BOT_VER!, true); - embed.AddField("Author", process.env.BOT_AUTHOR!, true); - embed.AddField("Date", process.env.BOT_DATE!, true); + embed.addFields([ + { + name: "Version", + value: process.env.BOT_VER!, + inline: true, + }, + { + name: "Author", + value: process.env.BOT_AUTHOR!, + inline: true, + }, + ]) const row = new ActionRowBuilder(); @@ -36,7 +52,7 @@ export default class About extends Command { .setLabel("Funding") .setStyle(ButtonStyle.Link)); } - - await embed.SendToCurrentChannel({ components: [ row ] }); + + await interaction.reply({ embeds: [ embed ]}); } } \ No newline at end of file diff --git a/src/commands/audits.ts b/src/commands/audits.ts index cbdd110..264f3d9 100644 --- a/src/commands/audits.ts +++ b/src/commands/audits.ts @@ -1,10 +1,11 @@ import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; import AuditTools from "../helpers/AuditTools"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; import SettingsHelper from "../helpers/SettingsHelper"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; export default class Audits extends Command { constructor() { @@ -14,131 +15,203 @@ export default class Audits extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("audits") + .setDescription("View audits of a particular user in the server") + .addSubcommand(subcommand => + subcommand + .setName('user') + .setDescription('View all audits done against a user') + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('view') + .setDescription('View a particular audit') + .addStringOption(option => + option + .setName('auditid') + .setDescription('The audit id in caps') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('clear') + .setDescription('Clears an audit from a user') + .addStringOption(option => + option + .setName('auditid') + .setDescription('The audit id in caps') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('add') + .setDescription('Manually add an audit') + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('type') + .setDescription('The type of audit') + .setRequired(true) + .addChoices( + { name: 'General', value: AuditType.General.toString() }, + { name: 'Warn', value: AuditType.Warn.toString() }, + { name: 'Mute', value: AuditType.Mute.toString() }, + { name: 'Kick', value: AuditType.Kick.toString() }, + { name: 'Ban', value: AuditType.Ban.toString() }, + )) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason'))); + } - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - switch (context.args[0]) { + switch (interaction.options.getSubcommand()) { case "user": - await this.SendAuditForUser(context); + await this.SendAuditForUser(interaction); break; case "view": - await this.SendAudit(context); + await this.SendAudit(interaction); break; case "clear": - await this.ClearAudit(context); + await this.ClearAudit(interaction); break; case "add": - await this.AddAudit(context); + await this.AddAudit(interaction); break; default: - await this.SendUsage(context); + await interaction.reply("Subcommand doesn't exist."); } } - private async SendUsage(context: ICommandContext) { - const prefix = await SettingsHelper.GetServerPrefix(context.message.guild!.id); + private async SendAuditForUser(interaction: CommandInteraction) { + if (!interaction.guildId) return; - const description = [ - `\`${prefix}audits user \` - Send the audits for this user`, - `\`${prefix}audits view \` - Send information about an audit`, - `\`${prefix}audits clear \` - Clears an audit for a user by audit id`, - `\`${prefix}audits add [reason]\` - Manually add an audit for a user`, - ] + const user = interaction.options.getUser('target'); - const publicEmbed = new PublicEmbed(context, "Usage", description.join("\n")); - await publicEmbed.SendToCurrentChannel(); - } + if (!user) { + await interaction.reply("User not found."); + return; + } - private async SendAuditForUser(context: ICommandContext) { - const userId = context.args[1]; - - const audits = await Audit.FetchAuditsByUserId(userId, context.message.guild!.id); + const audits = await Audit.FetchAuditsByUserId(user.id, interaction.guildId); if (!audits || audits.length == 0) { - const publicEmbed = new PublicEmbed(context, "Audits", "There are no audits logged for this user."); - await publicEmbed.SendToCurrentChannel(); - + await interaction.reply("There are no audits for this user."); return; } - const publicEmbed = new PublicEmbed(context, "Audit Log", `Audits: ${audits.length}`); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Audits") + .setDescription(`Audits: ${audits.length}`); for (let audit of audits) { - publicEmbed.AddField(`${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`, audit.WhenCreated.toString()); + embed.addFields([ + { + name: `${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`, + value: audit.WhenCreated.toString(), + } + ]); } - await publicEmbed.SendToCurrentChannel(); + await interaction.reply({ embeds: [ embed ]}); } - private async SendAudit(context: ICommandContext) { - const auditId = context.args[1]; + private async SendAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; - if (!auditId) { - await this.SendUsage(context); + const auditId = interaction.options.get('auditid'); + + if (!auditId || !auditId.value) { + await interaction.reply("AuditId not found."); return; } - const audit = await Audit.FetchAuditByAuditId(auditId.toUpperCase(), context.message.guild!.id); + const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId); if (!audit) { - const errorEmbed = new ErrorEmbed(context, "This audit can not be found."); - await errorEmbed.SendToCurrentChannel(); - + await interaction.reply("Audit not found."); return; } - const publicEmbed = new PublicEmbed(context, "Audit", audit.AuditId.toUpperCase()); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Audit") + .setDescription(audit.AuditId.toUpperCase()) + .addFields([ + { + name: "Reason", + value: audit.Reason || "*none*", + inline: true, + }, + { + name: "Type", + value: AuditTools.TypeToFriendlyText(audit.AuditType), + inline: true, + }, + { + name: "Moderator", + value: `<@${audit.ModeratorId}>`, + inline: true, + }, + ]); - publicEmbed.AddField("Reason", audit.Reason || "*none*", true); - publicEmbed.AddField("Type", AuditTools.TypeToFriendlyText(audit.AuditType), true); - publicEmbed.AddField("Moderator", `<@${audit.ModeratorId}>`, true); - - await publicEmbed.SendToCurrentChannel(); + await interaction.reply({ embeds: [ embed ]}); } - private async ClearAudit(context: ICommandContext) { - const auditId = context.args[1]; + private async ClearAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; - if (!auditId) { - await this.SendUsage(context); + const auditId = interaction.options.get('auditid'); + + if (!auditId || !auditId.value) { + await interaction.reply("AuditId not found."); return; } - const audit = await Audit.FetchAuditByAuditId(auditId.toUpperCase(), context.message.guild!.id); + const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId); if (!audit) { - const errorEmbed = new ErrorEmbed(context, "This audit can not be found."); - await errorEmbed.SendToCurrentChannel(); - + await interaction.reply("Audit not found."); return; } await Audit.Remove(Audit, audit); - const publicEmbed = new PublicEmbed(context, "Audit", "Audit cleared"); - await publicEmbed.SendToCurrentChannel(); + await interaction.reply("Audit cleared."); } - private async AddAudit(context: ICommandContext) { - const userId = context.args[1]; - const typeString = context.args[2]; - const reason = context.args.splice(3) - .join(" "); + private async AddAudit(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const user = interaction.options.getUser('target'); + const auditType = interaction.options.get('type'); + const reasonInput = interaction.options.get('reason'); - if (!userId || !typeString) { - await this.SendUsage(context); + if (!user || !auditType || !auditType.value) { + await interaction.reply("Invalid input."); return; } - const type = AuditTools.StringToType(typeString); + const type = auditType.value as AuditType; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : ""; - const audit = new Audit(userId, type, reason, context.message.author.id, context.message.guild!.id); + const audit = new Audit(user.id, type, reason, interaction.user.id, interaction.guildId); await audit.Save(Audit, audit); - - const publicEmbed = new PublicEmbed(context, "Audit", `Created new audit with ID \`${audit.AuditId}\``); - await publicEmbed.SendToCurrentChannel(); + + await interaction.reply(`Created new audit with ID \`${audit.AuditId}\``); } } \ No newline at end of file diff --git a/src/commands/ban.ts b/src/commands/ban.ts index 6c1b496..07e4f48 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -1,11 +1,11 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import ErrorMessages from "../constants/ErrorMessages"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; import { AuditType } from "../constants/AuditType"; +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; +import SettingsHelper from "../helpers/SettingsHelper"; export default class Ban extends Command { constructor() { @@ -15,57 +15,67 @@ export default class Ban extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("ban") + .setDescription("Ban a member from the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext) { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; + if (!interaction.guild) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - await embed.SendToCurrentChannel(); + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply("User not found."); return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const member = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - await embed.SendToCurrentChannel(); + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Banned") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); - return; + await member.ban(); + await interaction.reply(`\`${targetUser.user.tag}\` has been banned.`); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - const reasonArgs = context.args; - reasonArgs.splice(0, 1) - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) return; - - if (!targetMember.bannable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - await embed.SendToCurrentChannel(); - - return; - } - - const logEmbed = new LogEmbed(context, "Member Banned"); - logEmbed.AddUser("User", targetUser, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been banned`); - - await targetMember.ban({ reason: `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}` }); - - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - if (context.message.guild) { - const audit = new Audit(targetUser.id, AuditType.Ban, reason, context.message.author.id, context.message.guild.id); - - await audit.Save(Audit, audit); - } + const audit = new Audit(targetUser.user.id, AuditType.Ban, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); } } \ No newline at end of file diff --git a/src/commands/bunny.ts b/src/commands/bunny.ts index eb0952e..76f6b79 100644 --- a/src/commands/bunny.ts +++ b/src/commands/bunny.ts @@ -1,17 +1,23 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; import { ICommandContext } from "../contracts/ICommandContext"; import randomBunny from "random-bunny"; +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; export default class Bunny extends Command { constructor() { super(); super.Category = "Fun"; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("bunny") + .setDescription("Get a random picture of a rabbit."); } - public override async execute(context: ICommandContext) { + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + const subreddits = [ 'rabbits', 'bunnieswithhats', @@ -26,16 +32,17 @@ export default class Bunny extends Command { const result = await randomBunny(selectedSubreddit, 'hot'); if (result.IsSuccess) { - const embed = new PublicEmbed(context, result.Result!.Title, ""); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(result.Result!.Title) + .setDescription(result.Result!.Permalink) + .setImage(result.Result!.Url) + .setURL(`https://reddit.com${result.Result!.Permalink}`) + .setFooter({ text: `r/${selectedSubreddit} · ${result.Result!.Ups} upvotes`}); - embed.SetImage(result.Result!.Url) - embed.SetURL(`https://reddit.com${result.Result!.Permalink}`) - embed.SetFooter(`r/${selectedSubreddit} · ${result.Result!.Ups} upvotes`); - - await embed.SendToCurrentChannel(); + await interaction.reply({ embeds: [ embed ]}); } else { - const errorEmbed = new ErrorEmbed(context, "There was an error using this command."); - await errorEmbed.SendToCurrentChannel(); + await interaction.reply("There was an error running this command."); } } } \ No newline at end of file diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 5cf855b..4412d44 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -1,6 +1,4 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import { TextChannel } from "discord.js"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import { CommandInteraction, SlashCommandBuilder, TextChannel } from "discord.js"; import { Command } from "../type/command"; import { ICommandContext } from "../contracts/ICommandContext"; @@ -12,28 +10,31 @@ export default class Clear extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("clear") + .setDescription("Clears the channel of messages") + .addNumberOption(option => + option + .setName('count') + .setDescription('The amount to delete') + .setMinValue(1) + .setMaxValue(100)); } - public override async execute(context: ICommandContext) { - if (context.args.length == 0) { - const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); - await errorEmbed.SendToCurrentChannel(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - return; - } - - const totalToClear = Number.parseInt(context.args[0]); + const totalToClear = interaction.options.getNumber('count'); if (!totalToClear || totalToClear <= 0 || totalToClear > 100) { - const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); - await errorEmbed.SendToCurrentChannel(); - + await interaction.reply('Please specify an amount between 1 and 100.'); return; } - await (context.message.channel as TextChannel).bulkDelete(totalToClear); + const channel = interaction.channel as TextChannel; + await channel.bulkDelete(totalToClear); - const embed = new PublicEmbed(context, "", `${totalToClear} message(s) were removed`); - await embed.SendToCurrentChannel(); + await interaction.reply(`${totalToClear} message(s) were removed.`); } } \ No newline at end of file diff --git a/src/commands/code.ts b/src/commands/code.ts index cdfac96..52f6090 100644 --- a/src/commands/code.ts +++ b/src/commands/code.ts @@ -1,7 +1,6 @@ +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder, SlashCommandSubcommandBuilder } from "discord.js"; import { CommandResponse } from "../constants/CommandResponse"; import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import SettingsHelper from "../helpers/SettingsHelper"; import StringTools from "../helpers/StringTools"; import { Command } from "../type/command"; @@ -14,14 +13,25 @@ export default class Code extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName('code') + .setDescription('Manage the verification code of the server') + .addSubcommand(subcommand => + subcommand + .setName('randomise') + .setDescription('Regenerates the verification code for this server')) + .addSubcommand(subcommand => + subcommand + .setName('embed') + .setDescription('Sends the embed with the current code to the current channel')); } - public override async precheckAsync(context: ICommandContext): Promise { - if (!context.message.guild){ - return CommandResponse.NotInServer; - } + public override async precheckAsync(interaction: CommandInteraction): Promise { + if (!interaction.isChatInputCommand()) return CommandResponse.NotInServer; + if (!interaction.guild || !interaction.guildId) return CommandResponse.NotInServer; - const isEnabled = await SettingsHelper.GetSetting("verification.enabled", context.message.guild?.id); + const isEnabled = await SettingsHelper.GetSetting("verification.enabled", interaction.guildId); if (!isEnabled) { return CommandResponse.FeatureDisabled; @@ -34,61 +44,44 @@ export default class Code extends Command { return CommandResponse.Ok; } - public override async execute(context: ICommandContext) { - const action = context.args[0]; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - switch (action) { + switch (interaction.options.getSubcommand()) { case "randomise": - await this.Randomise(context); + await this.Randomise(interaction); break; case "embed": - await this.SendEmbed(context); + await this.SendEmbed(interaction); break; - default: - await this.SendUsage(context); } } - private async SendUsage(context: ICommandContext) { - const description = [ - "USAGE: ", - "", - "randomise: Sets the server's entry code to a random code", - "embed: Sends an embed with the server's entry code" - ].join("\n"); - - const embed = new PublicEmbed(context, "", description); - await embed.SendToCurrentChannel(); - } - - private async Randomise(context: ICommandContext) { - if (!context.message.guild) { - return; - } + private async Randomise(interaction: CommandInteraction) { + if (!interaction.guildId) return; const randomCode = StringTools.RandomString(5); - await SettingsHelper.SetSetting("verification.code", context.message.guild.id, randomCode); + await SettingsHelper.SetSetting("verification.code", interaction.guildId, randomCode); - const embed = new PublicEmbed(context, "Code", `Entry code has been set to \`${randomCode}\``); - await embed.SendToCurrentChannel(); + await interaction.reply(`Entry code has been set to \`${randomCode}\``); } - private async SendEmbed(context: ICommandContext) { - if (!context.message.guild) { - return; - } + private async SendEmbed(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!interaction.channel) return; - const code = await SettingsHelper.GetSetting("verification.code", context.message.guild.id); + const code = await SettingsHelper.GetSetting("verification.code", interaction.guildId); if (!code || code == "") { - const errorEmbed = new ErrorEmbed(context, "There is no code for this server setup."); - errorEmbed.SendToCurrentChannel(); - + await interaction.reply("There is no code for this server setup."); return; } - const embed = new PublicEmbed(context, "Entry Code", code!); - await embed.SendToCurrentChannel(); + const embed = new EmbedBuilder() + .setTitle("Entry Code") + .setDescription(code); + + await interaction.channel.send({ embeds: [ embed ]}); } } \ No newline at end of file diff --git a/src/commands/config.ts b/src/commands/config.ts index f3710e3..bdec5a5 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,11 +1,11 @@ +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import { readFileSync } from "fs"; import { CommandResponse } from "../constants/CommandResponse"; import DefaultValues from "../constants/DefaultValues"; +import EmbedColours from "../constants/EmbedColours"; import { ICommandContext } from "../contracts/ICommandContext"; import Server from "../entity/Server"; import Setting from "../entity/Setting"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; export default class Config extends Command { @@ -15,14 +15,48 @@ export default class Config extends Command { super.Roles = [ "administrator" ] + + super.CommandBuilder = new SlashCommandBuilder() + .setName('config') + .setDescription('Configure the current server') + .addSubcommand(subcommand => + subcommand + .setName('reset') + .setDescription('Reset a setting to the default') + .addStringOption(option => + option + .setName('key') + .setDescription('The key'))) + .addSubcommand(subcommand => + subcommand + .setName('get') + .setDescription('Gets a setting for the server') + .addStringOption(option => + option + .setName('key') + .setDescription('The key'))) + .addSubcommand(subcommand => + subcommand + .setName('set') + .setDescription('Sets a setting to a specified value') + .addStringOption(option => + option + .setName('key') + .setDescription('The key')) + .addStringOption(option => + option + .setName('value') + .setDescription('The value'))) + .addSubcommand(subcommand => + subcommand + .setName('list') + .setDescription('Lists all settings')) } - public override async precheckAsync(context: ICommandContext): Promise { - if (!context.message.guild) { - return CommandResponse.ServerNotSetup; - } + public override async precheckAsync(interaction: CommandInteraction): Promise { + if (!interaction.guildId) return CommandResponse.ServerNotSetup; - const server = await Server.FetchOneById(Server, context.message.guild?.id, [ + const server = await Server.FetchOneById(Server, interaction.guildId, [ "Settings", ]); @@ -33,94 +67,125 @@ export default class Config extends Command { return CommandResponse.Ok; } - public override async execute(context: ICommandContext) { - if (!context.message.guild) { + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + switch (interaction.options.getSubcommand()) { + case 'list': + await this.SendHelpText(interaction); + break; + case 'reset': + await this.ResetValue(interaction); + break; + case 'get': + await this.GetValue(interaction); + break; + case 'set': + await this.SetValue(interaction); + break; + default: + await interaction.reply('Subcommand not found.'); + } + } + + private async SendHelpText(interaction: CommandInteraction) { + const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString(); + + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Config") + .setDescription(description); + + await interaction.reply({ embeds: [ embed ]}); + } + + private async GetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const key = interaction.options.get('key'); + + if (!key || !key.value) { + await interaction.reply('Fields are required.'); return; } - const server = await Server.FetchOneById(Server, context.message.guild?.id, [ + const server = await Server.FetchOneById(Server, interaction.guildId, [ "Settings", ]); if (!server) { + await interaction.reply('Server not found.'); return; } - const key = context.args[0]; - const action = context.args[1]; - const value = context.args.splice(2).join(" "); - - if (!key) { - this.SendHelpText(context); - } else if (!action) { - this.GetValue(context, server, key); - } else { - switch(action) { - case 'reset': - this.ResetValue(context, server, key); - break; - case 'set': - if (!value) { - const errorEmbed = new ErrorEmbed(context, "Value is required when setting"); - errorEmbed.SendToCurrentChannel(); - return; - } - - this.SetValue(context, server, key, value); - break; - default: - const errorEmbed = new ErrorEmbed(context, "Action must be either set or reset"); - errorEmbed.SendToCurrentChannel(); - return; - } - } - } - - private async SendHelpText(context: ICommandContext) { - const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString(); - - const embed = new PublicEmbed(context, "Config", description); - - await embed.SendToCurrentChannel(); - } - - private async GetValue(context: ICommandContext, server: Server, key: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; + const setting = server.Settings.filter(x => x.Key == key.value)[0]; if (setting) { - const embed = new PublicEmbed(context, "", `${key}: ${setting.Value}`); - await embed.SendToCurrentChannel(); + await interaction.reply(`\`${key}\`: \`${setting.Value}\``); } else { - const embed = new PublicEmbed(context, "", `${key}: ${DefaultValues.GetValue(key)} `); - await embed.SendToCurrentChannel(); + await interaction.reply(`\`${key}\`: \`${DefaultValues.GetValue(key.value.toString())}\` `); } } - private async ResetValue(context: ICommandContext, server: Server, key: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; + private async ResetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const key = interaction.options.get('key'); + + if (!key || !key.value) { + await interaction.reply('Fields are required.'); + return; + } + + const server = await Server.FetchOneById(Server, interaction.guildId, [ + "Settings", + ]); + + if (!server) { + await interaction.reply('Server not found.'); + return; + } + + const setting = server.Settings.filter(x => x.Key == key.value)[0]; if (!setting) { - const embed = new PublicEmbed(context, "", "Setting has been reset"); - await embed.SendToCurrentChannel(); - + await interaction.reply('Setting not found.'); return; } await Setting.Remove(Setting, setting); - const embed = new PublicEmbed(context, "", "Setting has been reset"); - await embed.SendToCurrentChannel(); + await interaction.reply('The setting has been reset to the default.'); } - private async SetValue(context: ICommandContext, server: Server, key: string, value: string) { - const setting = server.Settings.filter(x => x.Key == key)[0]; + private async SetValue(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const key = interaction.options.get('key'); + const value = interaction.options.get('value'); + + if (!key || !key.value || !value || !value.value) { + await interaction.reply('Fields are required.'); + return; + } + + const server = await Server.FetchOneById(Server, interaction.guildId, [ + "Settings", + ]); + + if (!server) { + await interaction.reply('Server not found.'); + return; + } + + const setting = server.Settings.filter(x => x.Key == key.value)[0]; if (setting) { - setting.UpdateBasicDetails(key, value); + setting.UpdateBasicDetails(key.value.toString(), value.value.toString()); await setting.Save(Setting, setting); } else { - const newSetting = new Setting(key, value); + const newSetting = new Setting(key.value.toString(), value.value.toString()); await newSetting.Save(Setting, newSetting); @@ -129,7 +194,6 @@ export default class Config extends Command { await server.Save(Server, server); } - const embed = new PublicEmbed(context, "", "Setting has been set"); - await embed.SendToCurrentChannel(); + await interaction.reply('Setting has been set.'); } } \ No newline at end of file diff --git a/src/commands/disable.ts b/src/commands/disable.ts index 390da51..07c4cf6 100644 --- a/src/commands/disable.ts +++ b/src/commands/disable.ts @@ -1,5 +1,5 @@ +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; import { ICommandContext } from "../contracts/ICommandContext"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; @@ -11,83 +11,86 @@ export default class Disable extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName('disable') + .setDescription('Disables a command') + .addSubcommand(subcommand => + subcommand + .setName('add') + .setDescription('Disables a command for the server') + .addStringOption(option => + option + .setName('name') + .setDescription('The name of the command') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('remove') + .setDescription('Enables a command for the server') + .addStringOption(option => + option + .setName('name') + .setDescription('The name of the command') + .setRequired(true))); } - public override async execute(context: ICommandContext) { - const action = context.args[0]; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - switch (action) { + switch (interaction.options.getSubcommand()) { case "add": - await this.Add(context); + await this.Add(interaction); break; case "remove": - await this.Remove(context); + await this.Remove(interaction); break; default: - await this.SendUsage(context); + await interaction.reply('Subcommand not found.'); } } - private async SendUsage(context: ICommandContext) { - const description = [ - "USAGE: ", - "", - "add: Adds the command name to the server's disabled command string", - "remove: Removes the command name from the server's disabled command string", - "name: The name of the command to enable/disable" - ].join("\n"); - - const embed = new PublicEmbed(context, "", description); - await embed.SendToCurrentChannel(); - } + private async Add(interaction: CommandInteraction) { + if (!interaction.guildId) return; - private async Add(context: ICommandContext) { - if (!context.message.guild) { + const commandName = interaction.options.get('name'); + + if (!commandName || !commandName.value) { + await interaction.reply('Fields are required.'); return; } - const commandName = context.args[1]; - - if (!commandName) { - this.SendUsage(context); - return; - } - - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; - disabledCommands?.push(commandName); + disabledCommands?.push(commandName.value.toString()); - await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); + await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(",")); - const embed = new PublicEmbed(context, "", `Disabled command: ${commandName}`); - await embed.SendToCurrentChannel(); + await interaction.reply(`Disabled command ${commandName.value}`); } - private async Remove(context: ICommandContext) { - if (!context.message.guild) { + private async Remove(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const commandName = interaction.options.get('name'); + + if (!commandName || !commandName.value) { + await interaction.reply('Fields are required.'); return; } - const commandName = context.args[1]; - - if (!commandName) { - this.SendUsage(context); - return; - } - - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; - const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName); + const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName.value!.toString()); if (disabledCommandsInstance! > -1) { disabledCommands?.splice(disabledCommandsInstance!, 1); } - await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); + await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(",")); - const embed = new PublicEmbed(context, "", `Enabled command: ${commandName}`); - await embed.SendToCurrentChannel(); + await interaction.reply(`Enabled command ${commandName.value}`); } } \ No newline at end of file diff --git a/src/commands/help.ts b/src/commands/help.ts deleted file mode 100644 index 48908f3..0000000 --- a/src/commands/help.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { CoreClient } from "../client/client"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import StringTools from "../helpers/StringTools"; -import { Command } from "../type/command"; - -export interface ICommandData { - Exists: boolean; - Name?: string; - Category?: string; - Roles?: string[]; -} - -export default class Help extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override async execute(context: ICommandContext) { - if (context.args.length == 0) { - await this.SendAll(context); - } else { - await this.SendSingle(context); - } - } - - public async SendAll(context: ICommandContext) { - const allCommands = CoreClient.commandItems - .filter(x => !x.ServerId || x.ServerId == context.message.guild?.id); - const cateogries = [...new Set(allCommands.map(x => x.Command.Category))]; - - const embed = new PublicEmbed(context, "Commands", `Commands: ${allCommands.length}`); - - cateogries.forEach(category => { - let filtered = allCommands.filter(x => x.Command.Category == category); - - embed.AddField(StringTools.Capitalise(category || "Uncategorised"), StringTools.CapitaliseArray(filtered.flatMap(x => x.Name)).join(", ")); - }); - - await embed.SendToCurrentChannel(); - } - - public async SendSingle(context: ICommandContext) { - const command = CoreClient.commandItems.find(x => x.Name == context.args[0] && !x.ServerId); - const exclusiveCommand = CoreClient.commandItems.find(x => x.Name == context.args[0] && x.ServerId == context.message.guild?.id); - - if (exclusiveCommand) { - const embed = new PublicEmbed(context, StringTools.Capitalise(exclusiveCommand.Name), "Coming Soon"); - embed.AddField("Category", StringTools.Capitalise(exclusiveCommand.Command.Category || "Uncategorised")); - embed.AddField("Required Roles", StringTools.Capitalise(exclusiveCommand.Command.Roles.join(", ")) || "Everyone"); - - await embed.SendToCurrentChannel(); - } else if (command) { - const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name), "Coming Soon"); - embed.AddField("Category", StringTools.Capitalise(command.Command.Category || "Uncategorised")); - embed.AddField("Required Roles", StringTools.Capitalise(command.Command.Roles.join(", ")) || "Everyone"); - - await embed.SendToCurrentChannel(); - } else { - const errorEmbed = new ErrorEmbed(context, "Command does not exist"); - await errorEmbed.SendToCurrentChannel(); - } - } -} diff --git a/src/commands/ignore.ts b/src/commands/ignore.ts index 2bd3297..7fe87d8 100644 --- a/src/commands/ignore.ts +++ b/src/commands/ignore.ts @@ -1,6 +1,6 @@ +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; import { ICommandContext } from "../contracts/ICommandContext"; import IgnoredChannel from "../entity/IgnoredChannel"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; export default class Ignore extends Command { @@ -11,27 +11,29 @@ export default class Ignore extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName('ignore') + .setDescription('Ignore events in this channel'); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; - const isChannelIgnored = await IgnoredChannel.IsChannelIgnored(context.message.channel.id); + const isChannelIgnored = await IgnoredChannel.IsChannelIgnored(interaction.guildId); if (isChannelIgnored) { - const entity = await IgnoredChannel.FetchOneById(IgnoredChannel, context.message.channel.id); + const entity = await IgnoredChannel.FetchOneById(IgnoredChannel, interaction.guildId); await IgnoredChannel.Remove(IgnoredChannel, entity); - const embed = new PublicEmbed(context, "Success", "This channel will start being logged again."); - await embed.SendToCurrentChannel(); + await interaction.reply('This channel will start being logged again.'); } else { - const entity = new IgnoredChannel(context.message.channel.id); + const entity = new IgnoredChannel(interaction.guildId); await entity.Save(IgnoredChannel, entity); - const embed = new PublicEmbed(context, "Success", "This channel will now be ignored from logging."); - await embed.SendToCurrentChannel(); + await interaction.reply('This channel will now be ignored from logging.'); } } } \ No newline at end of file diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 81afe71..7eb50ec 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -1,71 +1,81 @@ -import { AuditType } from "../constants/AuditType"; import ErrorMessages from "../constants/ErrorMessages"; +import { Command } from "../type/command"; import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import { Command } from "../type/command"; +import { AuditType } from "../constants/AuditType"; +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import EmbedColours from "../constants/EmbedColours"; +import SettingsHelper from "../helpers/SettingsHelper"; export default class Kick extends Command { constructor() { super(); - + super.Category = "Moderation"; super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("kick") + .setDescription("Kick a member from the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext) { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; + if (!interaction.guild) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - await embed.SendToCurrentChannel(); + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply("User not found."); return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const member = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - await embed.SendToCurrentChannel(); - - return; + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Kicked") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); + + await member.kick(); + await interaction.reply(`\`${targetUser.user.tag}\` has been kicked.`); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - const reasonArgs = context.args; - reasonArgs.splice(0, 1) - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) return; - - if (!targetMember.kickable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - await embed.SendToCurrentChannel(); - - return; - } - - const logEmbed = new LogEmbed(context, "Member Kicked"); - logEmbed.AddUser("User", targetUser, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been kicked`); - - await targetMember.kick(`Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); - - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - if (context.message.guild) { - const audit = new Audit(targetUser.id, AuditType.Kick, reason, context.message.author.id, context.message.guild.id); - - await audit.Save(Audit, audit); - } + const audit = new Audit(targetUser.user.id, AuditType.Kick, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); } } \ No newline at end of file diff --git a/src/commands/mute.ts b/src/commands/mute.ts index 438441f..751b656 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -1,10 +1,10 @@ +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; import ErrorMessages from "../constants/ErrorMessages"; import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; export default class Mute extends Command { @@ -15,67 +15,71 @@ export default class Mute extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("mute") + .setDescription("Mute a member in the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext) { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild || !interaction.guildId) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - await embed.SendToCurrentChannel(); + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const targetMember = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - await embed.SendToCurrentChannel(); - - return; - } + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Muted") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); - const reasonArgs = context.args; - reasonArgs.splice(0, 1); - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) return; - - if (!targetMember.manageable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - await embed.SendToCurrentChannel(); - - return; - } - - const logEmbed = new LogEmbed(context, "Member Muted"); - logEmbed.AddUser("User", targetUser, true) - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been muted`); - publicEmbed.AddReason(reason); - - const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); + const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); if (!mutedRole) { - const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound); - await embed.SendToCurrentChannel(); - + await interaction.reply('Muted role not found.'); return; } - await targetMember.roles.add(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); + await targetMember.roles.add(mutedRole); - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - if (context.message.guild) { - const audit = new Audit(targetUser.id, AuditType.Mute, reason, context.message.author.id, context.message.guild.id); + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); - await audit.Save(Audit, audit); + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } + + const audit = new Audit(targetUser.user.id, AuditType.Mute, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); } } \ No newline at end of file diff --git a/src/commands/poll.ts b/src/commands/poll.ts deleted file mode 100644 index bbd501a..0000000 --- a/src/commands/poll.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import { Command } from "../type/command"; - -export default class Poll extends Command { - constructor() { - super(); - - super.Category = "General"; - } - - public override async execute(context: ICommandContext) { - const argsJoined = context.args.join(" "); - const argsSplit = argsJoined.split(";"); - - if (argsSplit.length < 3 || argsSplit.length > 10) { - const errorEmbed = new ErrorEmbed(context, "Usage: ;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options"); - await errorEmbed.SendToCurrentChannel(); - - return { - commandContext: context, - embeds: [errorEmbed] - }; - } - - const title = argsSplit[0]; - - const arrayOfNumbers = [ - ':one:', - ':two:', - ':three:', - ':four:', - ':five:', - ':six:', - ':seven:', - ':eight:', - ':nine:' - ]; - - const reactionEmojis = ["1️⃣", "2️⃣", "3️⃣", "4️⃣", "5️⃣", "6️⃣", "7️⃣", "8️⃣", "9️⃣"]; - - const description = arrayOfNumbers.splice(0, argsSplit.length - 1); - - description.forEach((value, index) => { - description[index] = `${value} ${argsSplit[index + 1]}`; - }); - - const embed = new PublicEmbed(context, title, description.join("\n")); - - const message = await embed.SendToCurrentChannel(); - - if (!message) return; - - description.forEach(async (value, index) => { - await message.react(reactionEmojis[index]); - }); - - if (context.message.deletable) { - await context.message.delete(); - } - - return { - commandContext: context, - embeds: [embed] - }; - } -} \ No newline at end of file diff --git a/src/commands/role.ts b/src/commands/role.ts index a632cd4..4ed1bae 100644 --- a/src/commands/role.ts +++ b/src/commands/role.ts @@ -1,205 +1,164 @@ -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; -import { Role as DiscordRole } from "discord.js"; +import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, Role as DiscordRole, SlashCommandBuilder } from "discord.js"; import { Command } from "../type/command"; import { ICommandContext } from "../contracts/ICommandContext"; import SettingsHelper from "../helpers/SettingsHelper"; import { readFileSync } from "fs"; import { default as eRole } from "../entity/Role"; import Server from "../entity/Server"; +import EmbedColours from "../constants/EmbedColours"; export default class Role extends Command { constructor() { super(); super.Category = "General"; + + super.CommandBuilder = new SlashCommandBuilder() + .setName('role') + .setDescription('Toggle your roles') + .addSubcommand(subcommand => + subcommand + .setName('config') + .setDescription('Configure the roles') + .addStringOption(option => + option + .setName('role') + .setDescription('The role name') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('toggle') + .setDescription('Toggle your role') + .addStringOption(option => + option + .setName('role') + .setDescription('The role name') + .setRequired(true))) + .addSubcommand(subcommand => + subcommand + .setName('list') + .setDescription('List togglable roles')); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) return; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; - switch (context.args[0]) { - case "config": - await this.UseConfig(context); + switch (interaction.options.getSubcommand()) { + case 'config': + await this.Configure(interaction); + break; + case 'toggle': + await this.ToggleRole(interaction); + break; + case 'list': + await this.SendRolesList(interaction); break; default: - await this.UseDefault(context); + await interaction.reply('Subcommand not found.'); } } - // ======= - // Default - // ======= + private async SendRolesList(interaction: CommandInteraction) { + const roles = await this.GetRolesList(interaction); - private async UseDefault(context: ICommandContext) { - if (context.args.length == 0) { - await this.SendRolesList(context, context.message.guild!.id); - } else { - await this.ToggleRole(context); - } + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Roles") + .setDescription(`Roles: ${roles.length}\n\n${roles.join("\n")}`); + + await interaction.reply({ embeds: [ embed ]}); } - public async GetRolesList(context: ICommandContext): Promise<string[]> { - const rolesArray = await eRole.FetchAllByServerId(context.message.guild!.id); + private async ToggleRole(interaction: CommandInteraction) { + if (!interaction.guild) return; + if (!interaction.member) return; - const stringArray: string[] = []; - - for (let i = 0; i < rolesArray.length; i++) { - const serverRole = context.message.guild!.roles.cache.find(x => x.id == rolesArray[i].RoleId); - - if (serverRole) { - stringArray.push(serverRole.name); - } - } - - return stringArray; - } - - public async SendRolesList(context: ICommandContext, serverId: string) { - const roles = await this.GetRolesList(context); - - const botPrefix = await SettingsHelper.GetServerPrefix(serverId); - const description = roles.length == 0 ? "*no roles*" : `Do ${botPrefix}role <role> to get the role!\n\n${roles.join('\n')}`; - - const embed = new PublicEmbed(context, "Roles", description); - await embed.SendToCurrentChannel(); - } - - public async ToggleRole(context: ICommandContext) { - const roles = await this.GetRolesList(context); - const requestedRole = context.args.join(" "); - - if (!roles.includes(requestedRole)) { - const errorEmbed = new ErrorEmbed(context, "This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters"); - await errorEmbed.SendToCurrentChannel(); + const roles = await this.GetRolesList(interaction); + const requestedRole = interaction.options.get('role'); + if (!requestedRole || !requestedRole.value) { + await interaction.reply('Fields are required.'); return; } - const assignRole = context.message.guild?.roles.cache.find(x => x.name == requestedRole); + if (!roles.includes(requestedRole.value.toString())) { + await interaction.reply('This role isn\'t marked as assignable.'); + return; + } + + const assignRole = interaction.guild.roles.cache.find(x => x.name == requestedRole.value); if (!assignRole) { - const errorEmbed = new ErrorEmbed(context, "The current server doesn't have this role. Please contact the server's moderators"); - await errorEmbed.SendToCurrentChannel(); - + await interaction.reply('The current server doesn\'t have this role. Please contact the server\'s moderators'); return; } - const role = context.message.member?.roles.cache.find(x => x.name == requestedRole) + const roleManager = interaction.member.roles as GuildMemberRoleManager; + + const role = roleManager.cache.find(x => x.name == requestedRole.value); if (!role) { - await this.AddRole(context, assignRole); + await roleManager.add(assignRole); + await interaction.reply(`Gave role: \`${assignRole.name}\``); } else { - await this.RemoveRole(context, assignRole); + await roleManager.remove(assignRole); + await interaction.reply(`Removed role: \`${assignRole.name}\``); } } - public async AddRole(context: ICommandContext, role: DiscordRole) { - await context.message.member?.roles.add(role, "Toggled with role command"); + private async Configure(interaction: CommandInteraction) { + if (!interaction.guildId || !interaction.guild) return; + if (!interaction.member) return; - const embed = new PublicEmbed(context, "", `Gave role: \`${role.name}\``); - await embed.SendToCurrentChannel(); - } - - public async RemoveRole(context: ICommandContext, role: DiscordRole) { - await context.message.member?.roles.remove(role, "Toggled with role command"); - - const embed = new PublicEmbed(context, "", `Removed role: \`${role.name}\``); - await embed.SendToCurrentChannel(); - } - - // ====== - // Config - // ====== - - private async UseConfig(context: ICommandContext) { - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id); - - if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) { - const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command"); - await errorEmbed.SendToCurrentChannel(); + const roleName = interaction.options.get('role'); + if (!roleName || !roleName.value) { + await interaction.reply('Fields are required.'); return; } - switch (context.args[1]) { - case "add": - await this.AddRoleConfig(context); - break; - case "remove": - await this.RemoveRoleConfig(context); - break; - default: - await this.SendConfigHelp(context); + const roleManager = interaction.member.roles as GuildMemberRoleManager; + + const moderatorRole = await SettingsHelper.GetSetting("role.moderator", interaction.guildId); + + if (!roleManager.cache.find(x => x.name == moderatorRole)) { + await interaction.reply('Sorry, you must be a moderator to be able to configure this command.'); + return; } - } - private async SendConfigHelp(context: ICommandContext) { - const helpText = readFileSync(`${process.cwd()}/data/usage/role.txt`).toString(); - - const embed = new PublicEmbed(context, "Configure Role Command", helpText); - await embed.SendToCurrentChannel(); - } - - private async AddRoleConfig(context: ICommandContext) { - const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]); + const role = interaction.guild.roles.cache.find(x => x.name == roleName.value); if (!role) { - this.SendConfigHelp(context); + await interaction.reply('Unable to find role.'); return; } const existingRole = await eRole.FetchOneByRoleId(role.id); if (existingRole) { - const errorEmbed = new ErrorEmbed(context, "This role has already been setup"); - await errorEmbed.SendToCurrentChannel(); - - return; + await eRole.Remove(eRole, existingRole); + await interaction.reply('Removed role from configuration.'); + } else { + const newRole = new eRole(role.id); + await newRole.Save(eRole, newRole); } - - const server = await Server.FetchOneById(Server, context.message.guild!.id, [ - "Roles", - ]); - - if (!server) { - const errorEmbed = new ErrorEmbed(context, "Server not setup, please request the server owner runs the setup command."); - await errorEmbed.SendToCurrentChannel(); - - return; - } - - const roleSetting = new eRole(role.id); - - await roleSetting.Save(eRole, roleSetting); - - server.AddRoleToServer(roleSetting); - await server.Save(Server, server); - - const embed = new PublicEmbed(context, "", `Added \`${role.name}\` as a new assignable role`); - await embed.SendToCurrentChannel(); } - private async RemoveRoleConfig(context: ICommandContext) { - const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]); + private async GetRolesList(interaction: CommandInteraction): Promise<string[]> { + if (!interaction.guildId || !interaction.guild) return []; - if (!role) { - this.SendConfigHelp(context); - return; + const rolesArray = await eRole.FetchAllByServerId(interaction.guildId); + + const roles: string[] = []; + + for (let i = 0; i < rolesArray.length; i++) { + const serverRole = interaction.guild.roles.cache.find(x => x.id == rolesArray[i].RoleId); + + if (serverRole) { + roles.push(serverRole.name); + } } - const existingRole = await eRole.FetchOneByRoleId(role.id); - - if (!existingRole) { - const errorEmbed = new ErrorEmbed(context, "Unable to find this role"); - errorEmbed.SendToCurrentChannel(); - - return; - } - - await eRole.Remove(eRole, existingRole); - - const embed = new PublicEmbed(context, "", `Removed \`${role.name}\` from the list of assignable roles`); - await embed.SendToCurrentChannel(); + return roles; } } diff --git a/src/commands/rules.ts b/src/commands/rules.ts index 23d709b..02291cf 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -1,7 +1,7 @@ +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import { existsSync, readFileSync } from "fs"; +import EmbedColours from "../constants/EmbedColours"; import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; interface IRules { @@ -19,34 +19,48 @@ export default class Rules extends Command { super.Roles = [ "administrator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("rules") + .setDescription("Send the rules embeds for this server"); } - public override async execute(context: ICommandContext) { - if (!existsSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`)) { - const errorEmbed = new ErrorEmbed(context, "Rules file doesn't exist"); - await errorEmbed.SendToCurrentChannel(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; + if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) { + await interaction.reply('Rules file doesn\'t exist.'); return; } - const rulesFile = readFileSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`).toString(); + const rulesFile = readFileSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`).toString(); const rules = JSON.parse(rulesFile) as IRules[]; - const embeds: PublicEmbed[] = []; + const embeds: EmbedBuilder[] = []; rules.forEach(rule => { - const embed = new PublicEmbed(context, rule.title || "", rule.description?.join("\n") || ""); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle(rule.title || "Rules") + .setDescription(rule.description ? rule.description.join("\n") : "*none*"); + + if (rule.image) { + embed.setImage(rule.image); + } - embed.SetImage(rule.image || ""); - embed.SetFooter(rule.footer || ""); + if (rule.footer) { + embed.setFooter({ text: rule.footer }); + } embeds.push(embed); }); - for (let i = 0; i < embeds.length; i++) { - const embed = embeds[i]; + const channel = interaction.channel; - await embed.SendToCurrentChannel(); + if (!channel) { + return; } + + await channel.send({ embeds: embeds }); } } \ No newline at end of file diff --git a/src/commands/say.ts b/src/commands/say.ts deleted file mode 100644 index 433b2b8..0000000 --- a/src/commands/say.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import { Command } from "../type/command"; - -export default class Say extends Command { - constructor() { - super(); - super.Category = "Misc"; - super.Roles = [ - "moderator" - ]; - } - - public override async execute(context: ICommandContext) { - const input = context.args.join(" "); - - if (input.length == 0) { - const errorEmbed = new ErrorEmbed(context, "You must supply a message."); - - await errorEmbed.SendToCurrentChannel(); - return; - } - - context.message.channel.send(input); - } -} \ No newline at end of file diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 8267ff1..f788de2 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -1,7 +1,6 @@ +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; import { ICommandContext } from "../contracts/ICommandContext"; import Server from "../entity/Server"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; import { Command } from "../type/command"; export default class Setup extends Command { @@ -11,27 +10,26 @@ export default class Setup extends Command { super.Roles = [ "moderator" ] + + super.CommandBuilder = new SlashCommandBuilder() + .setName('setup') + .setDescription('Makes the server ready to be configured'); } - public override async execute(context: ICommandContext) { - if (!context.message.guild) { - return; - } + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId) return; - const server = await Server.FetchOneById(Server, context.message.guild?.id); + const server = await Server.FetchOneById(Server, interaction.guildId); if (server) { - const embed = new ErrorEmbed(context, "This server has already been setup, please configure using the config command"); - await embed.SendToCurrentChannel(); - + await interaction.reply('This server has already been setup, please configure using the config command.'); return; } - const newServer = new Server(context.message.guild?.id); + const newServer = new Server(interaction.guildId); await newServer.Save(Server, newServer); - const embed = new PublicEmbed(context, "Success", "Please configure using the config command"); - await embed.SendToCurrentChannel(); + await interaction.reply('Success, please configure using the configure command.'); } } \ No newline at end of file diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts index 537919f..5fc6e0a 100644 --- a/src/commands/unmute.ts +++ b/src/commands/unmute.ts @@ -1,8 +1,10 @@ +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; +import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; import ErrorMessages from "../constants/ErrorMessages"; import { ICommandContext } from "../contracts/ICommandContext"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import Audit from "../entity/Audit"; +import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; export default class Unmute extends Command { @@ -13,61 +15,68 @@ export default class Unmute extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("unmute") + .setDescription("Unmute a member in the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext) { - const targetUser = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild || !interaction.guildId) return; - if (!targetUser) { - const embed = new ErrorEmbed(context, "User does not exist"); - await embed.SendToCurrentChannel(); - + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); return; } - const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); + const targetMember = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!targetMember) { - const embed = new ErrorEmbed(context, "User is not in this server"); - await embed.SendToCurrentChannel(); - - return; - } + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Unmuted") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); - const reasonArgs = context.args; - reasonArgs.splice(0, 1); - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) return; - - if (!targetMember.manageable) { - const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); - await embed.SendToCurrentChannel(); - - return; - } - - const logEmbed = new LogEmbed(context, "Member Unmuted"); - logEmbed.AddUser("User", targetUser, true) - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been unmuted`); - publicEmbed.AddReason(reason); - - const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); + const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); if (!mutedRole) { - const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound); - await embed.SendToCurrentChannel(); - + await interaction.reply('Muted role not found.'); return; } - await targetMember.roles.remove(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); + await targetMember.roles.remove(mutedRole); - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); + } } } \ No newline at end of file diff --git a/src/commands/warn.ts b/src/commands/warn.ts index b5eb346..8bc69c2 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -1,9 +1,10 @@ +import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { AuditType } from "../constants/AuditType"; +import EmbedColours from "../constants/EmbedColours"; +import ErrorMessages from "../constants/ErrorMessages"; import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; -import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; -import LogEmbed from "../helpers/embeds/LogEmbed"; -import PublicEmbed from "../helpers/embeds/PublicEmbed"; +import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; export default class Warn extends Command { @@ -14,49 +15,62 @@ export default class Warn extends Command { super.Roles = [ "moderator" ]; + + super.CommandBuilder = new SlashCommandBuilder() + .setName("warn") + .setDescription("Warns a member in the server with an optional reason") + .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .addUserOption(option => + option + .setName('target') + .setDescription('The user') + .setRequired(true)) + .addStringOption(option => + option + .setName('reason') + .setDescription('The reason')); } - public override async execute(context: ICommandContext) { - const user = context.message.mentions.users.first(); + public override async execute(interaction: CommandInteraction) { + if (!interaction.guild || !interaction.guildId) return; - if (!user) { - const errorEmbed = new ErrorEmbed(context, "User does not exist"); - await errorEmbed.SendToCurrentChannel(); - + const targetUser = interaction.options.get('target'); + const reasonInput = interaction.options.get('reason'); + + if (!targetUser || !targetUser.user || !targetUser.member) { + await interaction.reply('Fields are required.'); return; } - const member = context.message.guild?.members.cache.find(x => x.user.id == user.id); + const targetMember = targetUser.member as GuildMember; + const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*"; - if (!member) { - const errorEmbed = new ErrorEmbed(context, "User is not in this server"); - await errorEmbed.SendToCurrentChannel(); - - return; + const logEmbed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Member Warned") + .setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``) + .addFields([ + { + name: "Moderator", + value: `<@${interaction.user.id}>`, + }, + { + name: "Reason", + value: reason, + }, + ]); + + const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); + + if (!channelName) return; + + const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel; + + if (channel) { + await channel.send({ embeds: [ logEmbed ]}); } - const reasonArgs = context.args; - reasonArgs.splice(0, 1); - - const reason = reasonArgs.join(" "); - - if (!context.message.guild?.available) return; - - const logEmbed = new LogEmbed(context, "Member Warned"); - logEmbed.AddUser("User", user, true); - logEmbed.AddUser("Moderator", context.message.author); - logEmbed.AddReason(reason); - - const publicEmbed = new PublicEmbed(context, "", `${user} has been warned`); - publicEmbed.AddReason(reason); - - await logEmbed.SendToModLogsChannel(); - await publicEmbed.SendToCurrentChannel(); - - if (context.message.guild) { - const audit = new Audit(user.id, AuditType.Warn, reason, context.message.author.id, context.message.guild.id); - - await audit.Save(Audit, audit); - } + const audit = new Audit(targetUser.user.id, AuditType.Warn, reason, interaction.user.id, interaction.guildId); + await audit.Save(Audit, audit); } } \ No newline at end of file diff --git a/src/constants/EmbedColours.ts b/src/constants/EmbedColours.ts new file mode 100644 index 0000000..023c77a --- /dev/null +++ b/src/constants/EmbedColours.ts @@ -0,0 +1,3 @@ +export default class EmbedColours { + public static readonly Ok = 0x3050ba; +} \ No newline at end of file diff --git a/src/events/MemberEvents.ts b/src/events/MemberEvents.ts index b3f5f8d..ef935da 100644 --- a/src/events/MemberEvents.ts +++ b/src/events/MemberEvents.ts @@ -1,8 +1,8 @@ import { Event } from "../type/event"; -import { GuildMember } from "discord.js"; -import EventEmbed from "../helpers/embeds/EventEmbed"; +import { EmbedBuilder, GuildChannel, GuildMember, TextChannel } from "discord.js"; import GuildMemberUpdate from "./MemberEvents/GuildMemberUpdate"; import SettingsHelper from "../helpers/SettingsHelper"; +import EmbedColours from "../constants/EmbedColours"; export default class MemberEvents extends Event { constructor() { @@ -15,15 +15,29 @@ export default class MemberEvents extends Event { const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id); if (!enabled || enabled.toLowerCase() != "true") return; - const embed = new EventEmbed(member.client, member.guild, "Member Joined"); - embed.AddUser("User", member.user, true); - embed.AddField("Created", member.user.createdAt.toISOString()); - embed.SetFooter(`Id: ${member.user.id}`); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Member Joined') + .setDescription(`${member.user} \`${member.user.tag}\``) + .setFooter({ text: `Id: ${member.user.id}` }) + .addFields([ + { + name: 'Created', + value: member.user.createdAt.toISOString(), + } + ]); - const channel = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id); - if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return; + const channelSetting = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id); - await embed.SendToChannel(channel); + if (!channelSetting) return; + + const channel = member.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); } public override async guildMemberRemove(member: GuildMember) { @@ -32,15 +46,29 @@ export default class MemberEvents extends Event { const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id); if (!enabled || enabled.toLowerCase() != "true") return; - const embed = new EventEmbed(member.client, member.guild, "Member Left"); - embed.AddUser("User", member.user, true); - embed.AddField("Joined", member.joinedAt?.toISOString() || "n/a"); - embed.SetFooter(`Id: ${member.user.id}`); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Member Left') + .setDescription(`${member.user} \`${member.user.tag}\``) + .setFooter({ text: `Id: ${member.user.id}` }) + .addFields([ + { + name: 'Joined', + value: member.joinedAt ? member.joinedAt.toISOString() : "*none*", + } + ]); - const channel = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id); - if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return; - - await embed.SendToChannel(channel); + const channelSetting = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id); + + if (!channelSetting) return; + + const channel = member.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); } public override async guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) { diff --git a/src/events/MemberEvents/GuildMemberUpdate.ts b/src/events/MemberEvents/GuildMemberUpdate.ts index 6ba3a0b..6652b96 100644 --- a/src/events/MemberEvents/GuildMemberUpdate.ts +++ b/src/events/MemberEvents/GuildMemberUpdate.ts @@ -1,5 +1,5 @@ -import { GuildMember } from "discord.js"; -import EventEmbed from "../../helpers/embeds/EventEmbed"; +import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; +import EmbedColours from "../../constants/EmbedColours"; import SettingsHelper from "../../helpers/SettingsHelper"; export default class GuildMemberUpdate { @@ -18,15 +18,32 @@ export default class GuildMemberUpdate { const oldNickname = this.oldMember.nickname || "*none*"; const newNickname = this.newMember.nickname || "*none*"; - const embed = new EventEmbed(this.oldMember.client, this.newMember.guild, "Nickname Changed"); - embed.AddUser("User", this.newMember.user, true); - embed.AddField("Before", oldNickname, true); - embed.AddField("After", newNickname, true); - embed.SetFooter(`Id: ${this.newMember.user.id}`); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle('Nickname Changed') + .setDescription(`${this.newMember.user} \`${this.newMember.user.tag}\``) + .setFooter({ text: `Id: ${this.newMember.user.id}` }) + .addFields([ + { + name: 'Before', + value: oldNickname, + }, + { + name: 'After', + value: newNickname, + }, + ]); - const channel = await SettingsHelper.GetSetting("event.member.update.channel", this.newMember.guild.id); - if (!channel || channel.toLowerCase() != "true") return; + const channelSetting = await SettingsHelper.GetSetting("event.member.update.channel", this.newMember.guild.id); - await embed.SendToChannel(channel); + if (!channelSetting) return; + + const channel = this.newMember.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [embed ]}); } } \ No newline at end of file diff --git a/src/events/MessageEvents.ts b/src/events/MessageEvents.ts index e769456..714f203 100644 --- a/src/events/MessageEvents.ts +++ b/src/events/MessageEvents.ts @@ -1,9 +1,9 @@ import { Event } from "../type/event"; -import { Message } from "discord.js"; -import EventEmbed from "../helpers/embeds/EventEmbed"; +import { EmbedBuilder, Message, TextChannel } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import OnMessage from "./MessageEvents/OnMessage"; import IgnoredChannel from "../entity/IgnoredChannel"; +import EmbedColours from "../constants/EmbedColours"; export default class MessageEvents extends Event { constructor() { @@ -20,19 +20,42 @@ export default class MessageEvents extends Event { const ignored = await IgnoredChannel.IsChannelIgnored(message.channel.id); if (ignored) return; - const embed = new EventEmbed(message.client, message.guild, "Message Deleted"); - embed.AddUser("User", message.author, true); - embed.AddField("Channel", message.channel.toString(), true); - embed.AddField("Content", `\`\`\`${message.content || "*none*"}\`\`\``); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Message Deleted") + .setDescription(`${message.author} \`${message.author.tag}\``) + .addFields([ + { + name: "Channel", + value: message.channel.toString(), + inline: true, + }, + { + name: "Content", + value: `\`\`\`${message.content || "*none*"}\`\`\``, + } + ]); if (message.attachments.size > 0) { - embed.AddField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``); + embed.addFields([ + { + name: "Attachments", + value: `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\`` + } + ]); } - const channel = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id); - if (!channel || !message.guild.channels.cache.find(x => x.name == channel)) return; + const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id); - await embed.SendToChannel(channel); + if (!channelSetting) return; + + const channel = message.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [ embed ]}); } public override async messageUpdate(oldMessage: Message, newMessage: Message) { @@ -46,16 +69,37 @@ export default class MessageEvents extends Event { const ignored = await IgnoredChannel.IsChannelIgnored(newMessage.channel.id); if (ignored) return; - const embed = new EventEmbed(newMessage.client, newMessage.guild, "Message Edited"); - embed.AddUser("User", newMessage.author, true); - embed.AddField("Channel", newMessage.channel.toString(), true); - embed.AddField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``); - embed.AddField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``); + const embed = new EmbedBuilder() + .setColor(EmbedColours.Ok) + .setTitle("Message Deleted") + .setDescription(`${newMessage.author} \`${newMessage.author.tag}\``) + .addFields([ + { + name: "Channel", + value: newMessage.channel.toString(), + inline: true, + }, + { + name: "Before", + value: `\`\`\`${oldMessage.content || "*none*"}\`\`\``, + }, + { + name: "After", + value: `\`\`\`${newMessage.content || "*none*"}\`\`\``, + } + ]); - const channel = await SettingsHelper.GetSetting("event.message.update.channel", newMessage.guild.id); - if (!channel || !newMessage.guild.channels.cache.find(x => x.name == channel)) return; + const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", newMessage.guild.id); - await embed.SendToChannel(channel); + if (!channelSetting) return; + + const channel = newMessage.guild.channels.cache.find(x => x.name == channelSetting); + + if (!channel) return; + + const guildChannel = channel as TextChannel; + + await guildChannel.send({ embeds: [ embed ]}); } public override async messageCreate(message: Message) { diff --git a/src/helpers/embeds/ErrorEmbed.ts b/src/helpers/embeds/ErrorEmbed.ts deleted file mode 100644 index 8ac2754..0000000 --- a/src/helpers/embeds/ErrorEmbed.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { EmbedBuilder, Message, PermissionsBitField, TextChannel } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; - -export default class ErrorEmbed { - public context: ICommandContext; - - private _embedBuilder: EmbedBuilder; - - constructor(context: ICommandContext, message: string) { - this._embedBuilder = new EmbedBuilder() - .setColor(0xd52803) - .setDescription(message); - - this.context = context; - } - - // Detail Methods - public AddField(name: string, value: string, inline: boolean = false) { - this._embedBuilder.addFields([ - { - name, - value, - inline - } - ]) - } - - public SetFooter(text: string) { - this._embedBuilder.setFooter({ - text - }); - } - - public SetImage(imageUrl: string) { - this._embedBuilder.setImage(imageUrl); - } - - public SetURL(url: string) { - this._embedBuilder.setURL(url); - } - - // Send Methods - public async SendToCurrentChannel(): Promise<Message | undefined> { - const channel = this.context.message.channel as TextChannel; - const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); - - if (!botMember) return; - - const permissions = channel.permissionsFor(botMember); - - if (!permissions.has(PermissionsBitField.Flags.SendMessages)) return; - - return this.context.message.channel.send({ embeds: [ this._embedBuilder ]}); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/EventEmbed.ts b/src/helpers/embeds/EventEmbed.ts deleted file mode 100644 index 92f66ca..0000000 --- a/src/helpers/embeds/EventEmbed.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { EmbedBuilder, TextChannel, User, Guild, Client, PermissionsBitField, Message } from "discord.js"; -import SettingsHelper from "../SettingsHelper"; - -export default class EventEmbed { - public guild: Guild; - - private client: Client; - private _embedBuilder: EmbedBuilder; - - constructor(client: Client, guild: Guild, title: string) { - this._embedBuilder = new EmbedBuilder() - .setColor(0x3050ba) - .setTitle(title); - - this.guild = guild; - this.client = client; - } - - // Detail methods - public AddField(name: string, value: string, inline: boolean = false) { - this._embedBuilder.addFields([ - { - name, - value, - inline - } - ]) - } - - public SetFooter(text: string) { - this._embedBuilder.setFooter({ - text - }); - } - - public SetImage(imageUrl: string) { - this._embedBuilder.setImage(imageUrl); - } - - public AddUser(title: string, user: User, setThumbnail: boolean = false) { - this._embedBuilder.addFields([ - { - name: title, - value: `${user} \`${user.tag}\``, - inline: true, - } - ]) - - if (setThumbnail) { - this._embedBuilder.setThumbnail(user.displayAvatarURL()); - } - } - - public AddReason(message: string) { - this._embedBuilder.addFields([ - { - name: "Reason", - value: message || "*none*", - } - ]) - } - - public SetURL(url: string) { - this._embedBuilder.setURL(url); - } - - // Send methods - public async SendToChannel(name: string): Promise<Message | undefined> { - const channel = this.guild.channels.cache - .find(channel => channel.name == name) as TextChannel; - - if (!channel) { - console.error(`Unable to find channel ${name}`); - return; - } - - const botMember = await this.guild.members.fetch({ user: this.client.user! }); - - if (!botMember) return; - - const permissions = channel.permissionsFor(botMember); - - if (!permissions.has(PermissionsBitField.Flags.SendMessages)) return; - - return channel.send({embeds: [ this._embedBuilder ]}); - } - - public async SendToMessageLogsChannel(): Promise<Message | undefined> { - const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.guild.id); - - if (!channelName) { - return; - } - - return this.SendToChannel(channelName); - } - - public async SendToMemberLogsChannel(): Promise<Message | undefined> { - const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.guild.id); - - if (!channelName) { - return; - } - - return this.SendToChannel(channelName); - } - - public async SendToModLogsChannel(): Promise<Message | undefined> { - const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.guild.id); - - if (!channelName) { - return; - } - - return this.SendToChannel(channelName); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/LogEmbed.ts b/src/helpers/embeds/LogEmbed.ts deleted file mode 100644 index 7c41260..0000000 --- a/src/helpers/embeds/LogEmbed.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { TextChannel, User, EmbedBuilder, PermissionsBitField, Message } from "discord.js"; -import ErrorMessages from "../../constants/ErrorMessages"; -import { ICommandContext } from "../../contracts/ICommandContext"; -import SettingsHelper from "../SettingsHelper"; -import ErrorEmbed from "./ErrorEmbed"; - -export default class LogEmbed { - public context: ICommandContext; - - private _embedBuilder: EmbedBuilder; - - constructor(context: ICommandContext, title: string) { - this._embedBuilder = new EmbedBuilder() - .setColor(0x3050ba) - .setTitle(title); - - this.context = context; - } - - // Detail methods - public AddField(name: string, value: string, inline: boolean = false) { - this._embedBuilder.addFields([ - { - name, - value, - inline - } - ]) - } - - public SetFooter(text: string) { - this._embedBuilder.setFooter({ - text - }); - } - - public SetImage(imageUrl: string) { - this._embedBuilder.setImage(imageUrl); - } - - public AddUser(title: string, user: User, setThumbnail: boolean = false) { - this._embedBuilder.addFields([ - { - name: title, - value: `${user} \`${user.tag}\``, - inline: true, - } - ]); - - if (setThumbnail) { - this._embedBuilder.setThumbnail(user.displayAvatarURL()); - } - } - - public AddReason(message: string) { - this._embedBuilder.addFields([ - { - name: "Reason", - value: message || "*none*", - } - ]) - } - - public SetURL(url: string) { - this._embedBuilder.setURL(url); - } - - // Send methods - public async SendToCurrentChannel(): Promise<Message | undefined> { - const channel = this.context.message.channel as TextChannel; - const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); - - if (!botMember) return; - - const permissions = channel.permissionsFor(botMember); - - if (!permissions.has(PermissionsBitField.Flags.SendMessages)) return; - - return this.context.message.channel.send({ embeds: [ this._embedBuilder ]}); - } - - public async SendToChannel(name: string): Promise<Message | undefined> { - const channel = this.context.message.guild?.channels.cache - .find(channel => channel.name == name) as TextChannel; - - if (!channel) { - const errorEmbed = new ErrorEmbed(this.context, ErrorMessages.ChannelNotFound); - errorEmbed.SendToCurrentChannel(); - - return; - } - - return await channel.send({ embeds: [ this._embedBuilder ]}); - } - - public async SendToMessageLogsChannel(): Promise<Message | undefined> { - const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - return this.SendToChannel(channelName); - } - - public async SendToMemberLogsChannel(): Promise<Message | undefined> { - const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - return this.SendToChannel(channelName); - } - - public async SendToModLogsChannel(): Promise<Message | undefined> { - const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.context.message.guild?.id!); - - if (!channelName) { - return; - } - - return this.SendToChannel(channelName); - } -} \ No newline at end of file diff --git a/src/helpers/embeds/PublicEmbed.ts b/src/helpers/embeds/PublicEmbed.ts deleted file mode 100644 index 55ed5e4..0000000 --- a/src/helpers/embeds/PublicEmbed.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { EmbedBuilder, Message, MessageOptions, PermissionsBitField, TextChannel } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; - -export default class PublicEmbed { - public context: ICommandContext; - - private _embedBuilder: EmbedBuilder; - - constructor(context: ICommandContext, title: string, description: string) { - this._embedBuilder = new EmbedBuilder() - .setColor(0x3050ba) - .setTitle(title) - .setDescription(description); - - this.context = context; - } - - // Detail methods - public AddField(name: string, value: string, inline: boolean = false) { - this._embedBuilder.addFields([ - { - name, - value, - inline - } - ]) - } - - public SetFooter(text: string) { - this._embedBuilder.setFooter({ - text - }); - } - - public SetImage(imageUrl: string) { - this._embedBuilder.setImage(imageUrl); - } - - public AddReason(message: string) { - this._embedBuilder.addFields([ - { - name: "Reason", - value: message || "*none*", - } - ]) - } - - public SetURL(url: string) { - this._embedBuilder.setURL(url); - } - - // Send methods - public async SendToCurrentChannel(options?: MessageOptions): Promise<Message | undefined> { - const channel = this.context.message.channel as TextChannel; - const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! }); - - if (!botMember) return; - - const permissions = channel.permissionsFor(botMember); - - if (!permissions.has(PermissionsBitField.Flags.SendMessages)) return; - - return this.context.message.channel.send({ embeds: [ this._embedBuilder ], ...options}); - } -} \ No newline at end of file diff --git a/src/registry.ts b/src/registry.ts index c34c098..0671523 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -8,14 +8,11 @@ import Clear from "./commands/clear"; import Code from "./commands/code"; import Config from "./commands/config"; import Disable from "./commands/disable"; -import Help from "./commands/help"; import Ignore from "./commands/ignore"; import Kick from "./commands/kick"; import Mute from "./commands/mute"; -import Poll from "./commands/poll"; import Role from "./commands/role"; import Rules from "./commands/rules"; -import Say from "./commands/say"; import Setup from "./commands/setup"; import Unmute from "./commands/unmute"; import Warn from "./commands/warn"; @@ -38,17 +35,14 @@ export default class Registry { CoreClient.RegisterCommand("code", new Code()); CoreClient.RegisterCommand("config", new Config()); CoreClient.RegisterCommand("disable", new Disable()); - CoreClient.RegisterCommand("help", new Help()); CoreClient.RegisterCommand("ignore", new Ignore()); CoreClient.RegisterCommand("kick", new Kick()); CoreClient.RegisterCommand("mute", new Mute()); - CoreClient.RegisterCommand("poll", new Poll()); CoreClient.RegisterCommand("role", new Role()); CoreClient.RegisterCommand("rules", new Rules()); CoreClient.RegisterCommand("unmute", new Unmute()); CoreClient.RegisterCommand("warn", new Warn()); CoreClient.RegisterCommand("setup", new Setup()); - CoreClient.RegisterCommand("say", new Say()); CoreClient.RegisterCommand("audits", new Audits()); // Exclusive Commands: MankBot diff --git a/src/type/command.ts b/src/type/command.ts index 2ffb39a..ad99790 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,9 +1,9 @@ import { CommandResponse } from "../constants/CommandResponse"; import { ICommandContext } from "../contracts/ICommandContext"; -import { SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, Interaction, SlashCommandBuilder } from "discord.js"; export class Command { - public SlashCommandBuilder: SlashCommandBuilder; + public CommandBuilder: any; public Roles: string[]; public Category?: string; @@ -12,15 +12,15 @@ export class Command { this.Roles = []; } - public precheck(context: ICommandContext): CommandResponse { + public precheck(interation: CommandInteraction): CommandResponse { return CommandResponse.Ok; } - public async precheckAsync(context: ICommandContext): Promise<CommandResponse> { + public async precheckAsync(interation: CommandInteraction): Promise<CommandResponse> { return CommandResponse.Ok; } - - public execute(context: ICommandContext) { + + public execute(interaction: CommandInteraction) { } } diff --git a/src/vylbot.ts b/src/vylbot.ts index 33fc026..16589c2 100644 --- a/src/vylbot.ts +++ b/src/vylbot.ts @@ -9,8 +9,8 @@ const requiredConfigs: string[] = [ "BOT_TOKEN", "BOT_VER", "BOT_AUTHOR", - "BOT_DATE", "BOT_OWNERID", + "BOT_CLIENTID", ]; requiredConfigs.forEach(config => { -- 2.43.4 From 8ee8bf5e9f0ddb104495021346189f15f5fcbd9f Mon Sep 17 00:00:00 2001 From: Ethan Lane <ethan@vylpes.com> Date: Sat, 17 Sep 2022 15:05:54 +0100 Subject: [PATCH 3/6] Clean up imports --- src/client/client.ts | 1 - src/client/events.ts | 9 +-------- src/client/util.ts | 10 +--------- src/commands/501231711271780357/entry.ts | 1 - src/commands/501231711271780357/lobby.ts | 4 +--- src/commands/about.ts | 3 +-- src/commands/audits.ts | 2 -- src/commands/ban.ts | 2 -- src/commands/bunny.ts | 1 - src/commands/clear.ts | 1 - src/commands/code.ts | 3 +-- src/commands/config.ts | 1 - src/commands/disable.ts | 1 - src/commands/ignore.ts | 1 - src/commands/kick.ts | 2 -- src/commands/mute.ts | 2 -- src/commands/role.ts | 5 +---- src/commands/rules.ts | 1 - src/commands/setup.ts | 1 - src/commands/unmute.ts | 4 ---- src/commands/warn.ts | 2 -- src/contracts/ICommandContext.ts | 7 ------- src/entity/Audit.ts | 2 +- src/entity/Role.ts | 2 +- src/events/MemberEvents.ts | 2 +- src/type/command.ts | 3 +-- 26 files changed, 10 insertions(+), 63 deletions(-) delete mode 100644 src/contracts/ICommandContext.ts diff --git a/src/client/client.ts b/src/client/client.ts index 0eb2c81..4031f24 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -1,7 +1,6 @@ import { Client } from "discord.js"; import * as dotenv from "dotenv"; import { createConnection } from "typeorm"; -import DefaultValues from "../constants/DefaultValues"; import ICommandItem from "../contracts/ICommandItem"; import IEventItem from "../contracts/IEventItem"; import { Command } from "../type/command"; diff --git a/src/client/events.ts b/src/client/events.ts index 79f618f..bfc61c0 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,19 +1,12 @@ -import { CommandInteraction, GuildMemberRoleManager, Interaction, Message, messageLink } from "discord.js"; +import { GuildMemberRoleManager, Interaction } from "discord.js"; import { CommandResponse } from "../constants/CommandResponse"; import ErrorMessages from "../constants/ErrorMessages"; import ICommandItem from "../contracts/ICommandItem"; import SettingsHelper from "../helpers/SettingsHelper"; import StringTools from "../helpers/StringTools"; import { CoreClient } from "./client"; -import { Util } from "./util"; export class Events { - private _util: Util; - - constructor() { - this._util = new Util(); - } - public async onInteractionCreate(interaction: Interaction) { if (!interaction.isChatInputCommand()) return; if (!interaction.guild) return; diff --git a/src/client/util.ts b/src/client/util.ts index 832a84b..7a23542 100644 --- a/src/client/util.ts +++ b/src/client/util.ts @@ -1,15 +1,7 @@ -// Required Components -import { Client, Message, REST, Routes, SlashCommandBuilder } from "discord.js"; -import { ICommandContext } from "../contracts/ICommandContext"; -import ICommandItem from "../contracts/ICommandItem"; +import { Client, REST, Routes, SlashCommandBuilder } from "discord.js"; import IEventItem from "../contracts/IEventItem"; -import SettingsHelper from "../helpers/SettingsHelper"; -import StringTools from "../helpers/StringTools"; -import { CommandResponse } from "../constants/CommandResponse"; -import ErrorMessages from "../constants/ErrorMessages"; import { CoreClient } from "./client"; -// Util Class export class Util { public loadSlashCommands(client: Client) { const registeredCommands = CoreClient.commandItems; diff --git a/src/commands/501231711271780357/entry.ts b/src/commands/501231711271780357/entry.ts index f4e206d..9f8c92e 100644 --- a/src/commands/501231711271780357/entry.ts +++ b/src/commands/501231711271780357/entry.ts @@ -1,6 +1,5 @@ import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import EmbedColours from "../../constants/EmbedColours"; -import { ICommandContext } from "../../contracts/ICommandContext"; import SettingsHelper from "../../helpers/SettingsHelper"; import { Command } from "../../type/command"; diff --git a/src/commands/501231711271780357/lobby.ts b/src/commands/501231711271780357/lobby.ts index 023cfa3..d03ba53 100644 --- a/src/commands/501231711271780357/lobby.ts +++ b/src/commands/501231711271780357/lobby.ts @@ -1,9 +1,7 @@ -import { CommandInteraction, GuildMemberRoleManager, SlashCommandBuilder, TextChannel } from "discord.js"; -import { ICommandContext } from "../../contracts/ICommandContext"; +import { CommandInteraction, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; import { Command } from "../../type/command"; import { default as eLobby } from "../../entity/501231711271780357/Lobby"; import SettingsHelper from "../../helpers/SettingsHelper"; -import { readFileSync } from "fs"; import BaseEntity from "../../contracts/BaseEntity"; export default class Lobby extends Command { diff --git a/src/commands/about.ts b/src/commands/about.ts index b743d05..30fb6ad 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -1,6 +1,5 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, Interaction, SlashCommandBuilder } from "discord.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import EmbedColours from "../constants/EmbedColours"; -import { ICommandContext } from "../contracts/ICommandContext"; import { Command } from "../type/command"; export default class About extends Command { diff --git a/src/commands/audits.ts b/src/commands/audits.ts index 264f3d9..a88f32f 100644 --- a/src/commands/audits.ts +++ b/src/commands/audits.ts @@ -1,8 +1,6 @@ -import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; import AuditTools from "../helpers/AuditTools"; import { Command } from "../type/command"; -import SettingsHelper from "../helpers/SettingsHelper"; import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import { AuditType } from "../constants/AuditType"; import EmbedColours from "../constants/EmbedColours"; diff --git a/src/commands/ban.ts b/src/commands/ban.ts index 07e4f48..37ca2a6 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -1,6 +1,4 @@ -import ErrorMessages from "../constants/ErrorMessages"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; import { AuditType } from "../constants/AuditType"; import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; diff --git a/src/commands/bunny.ts b/src/commands/bunny.ts index 76f6b79..cf28dff 100644 --- a/src/commands/bunny.ts +++ b/src/commands/bunny.ts @@ -1,5 +1,4 @@ import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; import randomBunny from "random-bunny"; import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import EmbedColours from "../constants/EmbedColours"; diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 4412d44..160ec07 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -1,6 +1,5 @@ import { CommandInteraction, SlashCommandBuilder, TextChannel } from "discord.js"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; export default class Clear extends Command { constructor() { diff --git a/src/commands/code.ts b/src/commands/code.ts index 52f6090..f3fed94 100644 --- a/src/commands/code.ts +++ b/src/commands/code.ts @@ -1,6 +1,5 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder, SlashCommandSubcommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import { CommandResponse } from "../constants/CommandResponse"; -import { ICommandContext } from "../contracts/ICommandContext"; import SettingsHelper from "../helpers/SettingsHelper"; import StringTools from "../helpers/StringTools"; import { Command } from "../type/command"; diff --git a/src/commands/config.ts b/src/commands/config.ts index bdec5a5..60cd73d 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -3,7 +3,6 @@ import { readFileSync } from "fs"; import { CommandResponse } from "../constants/CommandResponse"; import DefaultValues from "../constants/DefaultValues"; import EmbedColours from "../constants/EmbedColours"; -import { ICommandContext } from "../contracts/ICommandContext"; import Server from "../entity/Server"; import Setting from "../entity/Setting"; import { Command } from "../type/command"; diff --git a/src/commands/disable.ts b/src/commands/disable.ts index 07c4cf6..4ad4af8 100644 --- a/src/commands/disable.ts +++ b/src/commands/disable.ts @@ -1,5 +1,4 @@ import { CommandInteraction, SlashCommandBuilder } from "discord.js"; -import { ICommandContext } from "../contracts/ICommandContext"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; diff --git a/src/commands/ignore.ts b/src/commands/ignore.ts index 7fe87d8..bb5124e 100644 --- a/src/commands/ignore.ts +++ b/src/commands/ignore.ts @@ -1,5 +1,4 @@ import { CommandInteraction, SlashCommandBuilder } from "discord.js"; -import { ICommandContext } from "../contracts/ICommandContext"; import IgnoredChannel from "../entity/IgnoredChannel"; import { Command } from "../type/command"; diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 7eb50ec..8a520d6 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -1,6 +1,4 @@ -import ErrorMessages from "../constants/ErrorMessages"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; import { AuditType } from "../constants/AuditType"; import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; diff --git a/src/commands/mute.ts b/src/commands/mute.ts index 751b656..312eff3 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -1,8 +1,6 @@ import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { AuditType } from "../constants/AuditType"; import EmbedColours from "../constants/EmbedColours"; -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; diff --git a/src/commands/role.ts b/src/commands/role.ts index 4ed1bae..8e0eaaa 100644 --- a/src/commands/role.ts +++ b/src/commands/role.ts @@ -1,10 +1,7 @@ -import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, Role as DiscordRole, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; import { Command } from "../type/command"; -import { ICommandContext } from "../contracts/ICommandContext"; import SettingsHelper from "../helpers/SettingsHelper"; -import { readFileSync } from "fs"; import { default as eRole } from "../entity/Role"; -import Server from "../entity/Server"; import EmbedColours from "../constants/EmbedColours"; export default class Role extends Command { diff --git a/src/commands/rules.ts b/src/commands/rules.ts index 02291cf..180f779 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -1,7 +1,6 @@ import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; import { existsSync, readFileSync } from "fs"; import EmbedColours from "../constants/EmbedColours"; -import { ICommandContext } from "../contracts/ICommandContext"; import { Command } from "../type/command"; interface IRules { diff --git a/src/commands/setup.ts b/src/commands/setup.ts index f788de2..63a0fc8 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -1,5 +1,4 @@ import { CommandInteraction, SlashCommandBuilder } from "discord.js"; -import { ICommandContext } from "../contracts/ICommandContext"; import Server from "../entity/Server"; import { Command } from "../type/command"; diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts index 5fc6e0a..c40360c 100644 --- a/src/commands/unmute.ts +++ b/src/commands/unmute.ts @@ -1,9 +1,5 @@ import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; -import { AuditType } from "../constants/AuditType"; import EmbedColours from "../constants/EmbedColours"; -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; -import Audit from "../entity/Audit"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; diff --git a/src/commands/warn.ts b/src/commands/warn.ts index 8bc69c2..1280983 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -1,8 +1,6 @@ import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { AuditType } from "../constants/AuditType"; import EmbedColours from "../constants/EmbedColours"; -import ErrorMessages from "../constants/ErrorMessages"; -import { ICommandContext } from "../contracts/ICommandContext"; import Audit from "../entity/Audit"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; diff --git a/src/contracts/ICommandContext.ts b/src/contracts/ICommandContext.ts deleted file mode 100644 index 78f0d17..0000000 --- a/src/contracts/ICommandContext.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Message } from "discord.js"; - -export interface ICommandContext { - name: string; - args: string[]; - message: Message; -} \ No newline at end of file diff --git a/src/entity/Audit.ts b/src/entity/Audit.ts index 0ec1ea7..448913d 100644 --- a/src/entity/Audit.ts +++ b/src/entity/Audit.ts @@ -1,4 +1,4 @@ -import { Column, Entity, getConnection, ManyToOne } from "typeorm"; +import { Column, Entity, getConnection } from "typeorm"; import { AuditType } from "../constants/AuditType"; import BaseEntity from "../contracts/BaseEntity"; import StringTools from "../helpers/StringTools"; diff --git a/src/entity/Role.ts b/src/entity/Role.ts index 5b534ad..e4b2da7 100644 --- a/src/entity/Role.ts +++ b/src/entity/Role.ts @@ -1,4 +1,4 @@ -import { Column, Entity, EntityTarget, getConnection, ManyToOne } from "typeorm"; +import { Column, Entity, getConnection, ManyToOne } from "typeorm"; import BaseEntity from "../contracts/BaseEntity" import Server from "./Server"; diff --git a/src/events/MemberEvents.ts b/src/events/MemberEvents.ts index ef935da..fabf0b9 100644 --- a/src/events/MemberEvents.ts +++ b/src/events/MemberEvents.ts @@ -1,5 +1,5 @@ import { Event } from "../type/event"; -import { EmbedBuilder, GuildChannel, GuildMember, TextChannel } from "discord.js"; +import { EmbedBuilder, GuildMember, TextChannel } from "discord.js"; import GuildMemberUpdate from "./MemberEvents/GuildMemberUpdate"; import SettingsHelper from "../helpers/SettingsHelper"; import EmbedColours from "../constants/EmbedColours"; diff --git a/src/type/command.ts b/src/type/command.ts index ad99790..6fea8c3 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,6 +1,5 @@ import { CommandResponse } from "../constants/CommandResponse"; -import { ICommandContext } from "../contracts/ICommandContext"; -import { CommandInteraction, Interaction, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction } from "discord.js"; export class Command { public CommandBuilder: any; -- 2.43.4 From e62ee7e491cd67dc855825582f1e44476bb741be Mon Sep 17 00:00:00 2001 From: Ethan Lane <ethan@vylpes.com> Date: Sat, 17 Sep 2022 16:21:00 +0100 Subject: [PATCH 4/6] Update permissions --- src/client/events.ts | 47 +---- src/commands/501231711271780357/Lobby/add.ts | 54 ++++++ .../501231711271780357/Lobby/lobby.ts | 41 +++++ .../501231711271780357/Lobby/remove.ts | 39 +++++ src/commands/501231711271780357/entry.ts | 10 +- src/commands/501231711271780357/lobby.ts | 162 ------------------ src/commands/Role/config.ts | 54 ++++++ src/commands/{ => Role}/role.ts | 85 ++------- src/commands/about.ts | 1 - src/commands/audits.ts | 8 +- src/commands/ban.ts | 10 +- src/commands/bunny.ts | 2 - src/commands/clear.ts | 15 +- src/commands/code.ts | 26 +-- src/commands/config.ts | 22 +-- src/commands/disable.ts | 8 +- src/commands/ignore.ts | 10 +- src/commands/kick.ts | 10 +- src/commands/mute.ts | 12 +- src/commands/rules.ts | 10 +- src/commands/setup.ts | 9 +- src/commands/unmute.ts | 12 +- src/commands/warn.ts | 7 +- src/constants/CommandResponse.ts | 7 - src/constants/ErrorMessages.ts | 27 --- src/entity/Role.ts | 4 + src/registry.ts | 15 +- src/type/command.ts | 16 -- 28 files changed, 283 insertions(+), 440 deletions(-) create mode 100644 src/commands/501231711271780357/Lobby/add.ts create mode 100644 src/commands/501231711271780357/Lobby/lobby.ts create mode 100644 src/commands/501231711271780357/Lobby/remove.ts delete mode 100644 src/commands/501231711271780357/lobby.ts create mode 100644 src/commands/Role/config.ts rename src/commands/{ => Role}/role.ts (54%) delete mode 100644 src/constants/CommandResponse.ts delete mode 100644 src/constants/ErrorMessages.ts diff --git a/src/client/events.ts b/src/client/events.ts index bfc61c0..5970f47 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,22 +1,18 @@ -import { GuildMemberRoleManager, Interaction } from "discord.js"; -import { CommandResponse } from "../constants/CommandResponse"; -import ErrorMessages from "../constants/ErrorMessages"; +import { Interaction } from "discord.js"; import ICommandItem from "../contracts/ICommandItem"; import SettingsHelper from "../helpers/SettingsHelper"; -import StringTools from "../helpers/StringTools"; import { CoreClient } from "./client"; export class Events { public async onInteractionCreate(interaction: Interaction) { if (!interaction.isChatInputCommand()) return; - if (!interaction.guild) return; - if (!interaction.member) return; + if (!interaction.guildId) return; - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guild.id); + const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString?.split(","); if (disabledCommands?.find(x => x == interaction.commandName)) { - interaction.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); + await interaction.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); return; } @@ -28,7 +24,7 @@ export class Events { if (!itemForServer) { if (!item) { - interaction.reply('Command not found'); + await interaction.reply('Command not found'); return; } @@ -37,39 +33,6 @@ export class Events { itemToUse = itemForServer; } - const requiredRoles = itemToUse.Command.Roles; - - if (interaction.member.user.id != process.env.BOT_OWNERID && interaction.member.user.id != interaction.guild.ownerId) { - for (const i in requiredRoles) { - const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, interaction.guildId!); - - if (!setting) { - interaction.reply("Unable to verify if you have this role, please contact your bot administrator"); - return; - } - - const roles = interaction.member.roles as GuildMemberRoleManager; - - if (!roles.cache.find(role => role.name == setting)) { - interaction.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`); - return; - } - } - } - - const precheckResponse = itemToUse.Command.precheck(interaction); - const precheckAsyncResponse = await itemToUse.Command.precheckAsync(interaction); - - if (precheckResponse != CommandResponse.Ok) { - interaction.reply(ErrorMessages.GetErrorMessage(precheckResponse)); - return; - } - - if (precheckAsyncResponse != CommandResponse.Ok) { - interaction.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse)); - return; - } - itemToUse.Command.execute(interaction); } diff --git a/src/commands/501231711271780357/Lobby/add.ts b/src/commands/501231711271780357/Lobby/add.ts new file mode 100644 index 0000000..105844c --- /dev/null +++ b/src/commands/501231711271780357/Lobby/add.ts @@ -0,0 +1,54 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../entity/501231711271780357/Lobby"; + +export default class AddRole extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('addlobby') + .setDescription('Add lobby channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel')) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role to ping on request')) + .addNumberOption(option => + option + .setName('cooldown') + .setDescription('The cooldown in minutes')) + .addStringOption(option => + option + .setName('name') + .setDescription('The game name')); + } + + public override async execute(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + const role = interaction.options.get('role'); + const cooldown = interaction.options.get('cooldown'); + const gameName = interaction.options.get('name'); + + if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) { + await interaction.reply('Fields are required.'); + return; + } + + const lobby = await eLobby.FetchOneByChannelId(channel.channel.id); + + if (lobby) { + await interaction.reply('This channel has already been setup.'); + return; + } + + const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string); + await entity.Save(eLobby, entity); + + await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes \` and will ping \`${role.name}\` on use`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/lobby.ts b/src/commands/501231711271780357/Lobby/lobby.ts new file mode 100644 index 0000000..5be32ad --- /dev/null +++ b/src/commands/501231711271780357/Lobby/lobby.ts @@ -0,0 +1,41 @@ +import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../entity/501231711271780357/Lobby"; + +export default class Lobby extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('lobby') + .setDescription('Attempt to organise a lobby'); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.channelId) return; + + const lobby = await eLobby.FetchOneByChannelId(interaction.channelId); + + if (!lobby) { + await interaction.reply('This channel is disabled from using the lobby command.'); + return; + } + + const timeNow = Date.now(); + const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms + const timeAgo = timeNow - timeLength; + + // If it was less than x minutes ago + if (lobby.LastUsed.getTime() > timeAgo) { + const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60); + + await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); + return; + } + + lobby.MarkAsUsed(); + await lobby.Save(eLobby, lobby); + + await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@${lobby.RoleId}>`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/remove.ts b/src/commands/501231711271780357/Lobby/remove.ts new file mode 100644 index 0000000..9dac70b --- /dev/null +++ b/src/commands/501231711271780357/Lobby/remove.ts @@ -0,0 +1,39 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../../type/command"; +import { default as eLobby } from "../../../entity/501231711271780357/Lobby"; +import BaseEntity from "../../../contracts/BaseEntity"; + +export default class RemoveLobby extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('removelobby') + .setDescription('Remove a lobby channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) + .addChannelOption(option => + option + .setName('channel') + .setDescription('The channel')); + } + + public override async execute(interaction: CommandInteraction) { + const channel = interaction.options.get('channel'); + + if (!channel || !channel.channel) { + await interaction.reply('Channel is required.'); + return; + } + + const entity = await eLobby.FetchOneByChannelId(channel.channel.id); + + if (!entity) { + await interaction.reply('Channel not found.'); + return; + } + + await BaseEntity.Remove<eLobby>(eLobby, entity); + + await interaction.reply(`Removed <#${channel.channel.name}> from the list of lobby channels`); + } +} \ No newline at end of file diff --git a/src/commands/501231711271780357/entry.ts b/src/commands/501231711271780357/entry.ts index 9f8c92e..fd246ca 100644 --- a/src/commands/501231711271780357/entry.ts +++ b/src/commands/501231711271780357/entry.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import EmbedColours from "../../constants/EmbedColours"; import SettingsHelper from "../../helpers/SettingsHelper"; import { Command } from "../../type/command"; @@ -7,14 +7,10 @@ export default class Entry extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName('entry') - .setDescription('Sends the entry embed'); + .setDescription('Sends the entry embed') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers); } public override async execute(interaction: CommandInteraction) { diff --git a/src/commands/501231711271780357/lobby.ts b/src/commands/501231711271780357/lobby.ts deleted file mode 100644 index d03ba53..0000000 --- a/src/commands/501231711271780357/lobby.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { CommandInteraction, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; -import { Command } from "../../type/command"; -import { default as eLobby } from "../../entity/501231711271780357/Lobby"; -import SettingsHelper from "../../helpers/SettingsHelper"; -import BaseEntity from "../../contracts/BaseEntity"; - -export default class Lobby extends Command { - constructor() { - super(); - - super.Category = "General"; - - super.CommandBuilder = new SlashCommandBuilder() - .setName('lobby') - .setDescription('Attempt to organise a lobby') - .addSubcommand(subcommand => - subcommand - .setName('config') - .setDescription('Configure the lobby command (mods only)') - .addStringOption(option => - option - .setName('action') - .setDescription('Add or remove a channel to the lobby list') - .addChoices( - { name: 'add', value: 'add' }, - { name: 'remove', value: 'remove' }, - )) - .addChannelOption(option => - option - .setName('channel') - .setDescription('The channel')) - .addRoleOption(option => - option - .setName('role') - .setDescription('The role to ping on request')) - .addNumberOption(option => - option - .setName('cooldown') - .setDescription('The cooldown in minutes')) - .addStringOption(option => - option - .setName('name') - .setDescription('The game name'))); - } - - public override async execute(interaction: CommandInteraction) { - if (!interaction.isChatInputCommand()) return; - - switch (interaction.options.getSubcommand()) { - case "config": - await this.Configure(interaction); - break; - case "request": - await this.Request(interaction); - break; - } - } - - private async Request(interaction: CommandInteraction) { - if (!interaction.channelId) return; - - const lobby = await eLobby.FetchOneByChannelId(interaction.channelId); - - if (!lobby) { - await interaction.reply('This channel is disabled from using the lobby command.'); - return; - } - - const timeNow = Date.now(); - const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms - const timeAgo = timeNow - timeLength; - - // If it was less than x minutes ago - if (lobby.LastUsed.getTime() > timeAgo) { - const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60); - - await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`); - return; - } - - lobby.MarkAsUsed(); - await lobby.Save(eLobby, lobby); - - await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@${lobby.RoleId}>`); - } - - private async Configure(interaction: CommandInteraction) { - if (!interaction.guildId) return; - if (!interaction.member) return; - - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", interaction.guildId); - - const roleManager = interaction.member.roles as GuildMemberRoleManager; - - if (!roleManager.cache.find(x => x.name == moderatorRole)) { - await interaction.reply('Sorry, you must be a moderator to be able to configure this command.'); - return; - } - - const action = interaction.options.get('action'); - - if (!action || !action.value) { - await interaction.reply('Action is required.'); - return; - } - - switch (action.value) { - case "add": - await this.AddLobbyConfig(interaction); - break; - case "remove": - await this.RemoveLobbyConfig(interaction); - break; - default: - await interaction.reply('Action not found.'); - } - } - - private async AddLobbyConfig(interaction: CommandInteraction) { - const channel = interaction.options.get('channel'); - const role = interaction.options.get('role'); - const cooldown = interaction.options.get('cooldown'); - const gameName = interaction.options.get('name'); - - if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) { - await interaction.reply('Fields are required.'); - return; - } - - const lobby = await eLobby.FetchOneByChannelId(channel.channel.id); - - if (lobby) { - await interaction.reply('This channel has already been setup.'); - return; - } - - const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string); - await entity.Save(eLobby, entity); - - await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes \` and will ping \`${role.name}\` on use`); - } - - private async RemoveLobbyConfig(interaraction: CommandInteraction) { - const channel = interaraction.options.get('channel'); - - if (!channel || !channel.channel) { - await interaraction.reply('Channel is required.'); - return; - } - - const entity = await eLobby.FetchOneByChannelId(channel.channel.id); - - if (!entity) { - await interaraction.reply('Channel not found.'); - return; - } - - await BaseEntity.Remove<eLobby>(eLobby, entity); - - await interaraction.reply(`Removed <#${channel.channel.name}> from the list of lobby channels`); - } -} \ No newline at end of file diff --git a/src/commands/Role/config.ts b/src/commands/Role/config.ts new file mode 100644 index 0000000..93d6891 --- /dev/null +++ b/src/commands/Role/config.ts @@ -0,0 +1,54 @@ +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../type/command"; +import { default as eRole } from "../../entity/Role"; +import Server from "../../entity/Server"; + +export default class ConfigRole extends Command { + constructor() { + super(); + + super.CommandBuilder = new SlashCommandBuilder() + .setName('configrole') + .setDescription('Toggle your roles') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles) + .addRoleOption(option => + option + .setName('role') + .setDescription('The role name') + .setRequired(true)); + } + + public override async execute(interaction: CommandInteraction) { + if (!interaction.guildId || !interaction.guild) return; + if (!interaction.member) return; + + const role = interaction.options.get('role'); + + if (!role || !role.role) { + await interaction.reply('Fields are required.'); + return; + } + + const existingRole = await eRole.FetchOneByRoleId(role.role.id); + + if (existingRole) { + await eRole.Remove(eRole, existingRole); + + await interaction.reply('Removed role from configuration.'); + } else { + const server = await Server.FetchOneById(Server, interaction.guildId); + + if (!server) { + await interaction.reply('This server has not been setup.'); + return; + } + + const newRole = new eRole(role.role.id); + newRole.SetServer(server); + + await newRole.Save(eRole, newRole); + + await interaction.reply('Added role to configuration.'); + } + } +} diff --git a/src/commands/role.ts b/src/commands/Role/role.ts similarity index 54% rename from src/commands/role.ts rename to src/commands/Role/role.ts index 8e0eaaa..6e57653 100644 --- a/src/commands/role.ts +++ b/src/commands/Role/role.ts @@ -1,32 +1,20 @@ -import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; -import { Command } from "../type/command"; -import SettingsHelper from "../helpers/SettingsHelper"; -import { default as eRole } from "../entity/Role"; -import EmbedColours from "../constants/EmbedColours"; +import { CommandInteraction, EmbedBuilder, GuildMember, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; +import { Command } from "../../type/command"; +import { default as eRole } from "../../entity/Role"; +import EmbedColours from "../../constants/EmbedColours"; export default class Role extends Command { constructor() { super(); - super.Category = "General"; - super.CommandBuilder = new SlashCommandBuilder() .setName('role') .setDescription('Toggle your roles') - .addSubcommand(subcommand => - subcommand - .setName('config') - .setDescription('Configure the roles') - .addStringOption(option => - option - .setName('role') - .setDescription('The role name') - .setRequired(true))) .addSubcommand(subcommand => subcommand .setName('toggle') .setDescription('Toggle your role') - .addStringOption(option => + .addRoleOption(option => option .setName('role') .setDescription('The role name') @@ -41,9 +29,6 @@ export default class Role extends Command { if (!interaction.isChatInputCommand()) return; switch (interaction.options.getSubcommand()) { - case 'config': - await this.Configure(interaction); - break; case 'toggle': await this.ToggleRole(interaction); break; @@ -73,28 +58,30 @@ export default class Role extends Command { const roles = await this.GetRolesList(interaction); const requestedRole = interaction.options.get('role'); - if (!requestedRole || !requestedRole.value) { + if (!requestedRole || !requestedRole.role) { await interaction.reply('Fields are required.'); return; } - if (!roles.includes(requestedRole.value.toString())) { + if (!roles.includes(requestedRole.role.name)) { await interaction.reply('This role isn\'t marked as assignable.'); return; } - const assignRole = interaction.guild.roles.cache.find(x => x.name == requestedRole.value); + const roleManager = interaction.member.roles as GuildMemberRoleManager; + const member = interaction.member as GuildMember; - if (!assignRole) { - await interaction.reply('The current server doesn\'t have this role. Please contact the server\'s moderators'); + const userRole = roleManager.cache.find(x => x.name == requestedRole.role!.name); + const assignRole = interaction.guild.roles.cache.find(x => x.id == requestedRole.role!.id); + + if (!assignRole) return; + + if (!assignRole.editable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); return; } - const roleManager = interaction.member.roles as GuildMemberRoleManager; - - const role = roleManager.cache.find(x => x.name == requestedRole.value); - - if (!role) { + if (!userRole) { await roleManager.add(assignRole); await interaction.reply(`Gave role: \`${assignRole.name}\``); } else { @@ -103,44 +90,6 @@ export default class Role extends Command { } } - private async Configure(interaction: CommandInteraction) { - if (!interaction.guildId || !interaction.guild) return; - if (!interaction.member) return; - - const roleName = interaction.options.get('role'); - - if (!roleName || !roleName.value) { - await interaction.reply('Fields are required.'); - return; - } - - const roleManager = interaction.member.roles as GuildMemberRoleManager; - - const moderatorRole = await SettingsHelper.GetSetting("role.moderator", interaction.guildId); - - if (!roleManager.cache.find(x => x.name == moderatorRole)) { - await interaction.reply('Sorry, you must be a moderator to be able to configure this command.'); - return; - } - - const role = interaction.guild.roles.cache.find(x => x.name == roleName.value); - - if (!role) { - await interaction.reply('Unable to find role.'); - return; - } - - const existingRole = await eRole.FetchOneByRoleId(role.id); - - if (existingRole) { - await eRole.Remove(eRole, existingRole); - await interaction.reply('Removed role from configuration.'); - } else { - const newRole = new eRole(role.id); - await newRole.Save(eRole, newRole); - } - } - private async GetRolesList(interaction: CommandInteraction): Promise<string[]> { if (!interaction.guildId || !interaction.guild) return []; diff --git a/src/commands/about.ts b/src/commands/about.ts index 30fb6ad..b1ad17b 100644 --- a/src/commands/about.ts +++ b/src/commands/about.ts @@ -5,7 +5,6 @@ import { Command } from "../type/command"; export default class About extends Command { constructor() { super(); - super.Category = "General"; super.CommandBuilder = new SlashCommandBuilder() .setName('about') diff --git a/src/commands/audits.ts b/src/commands/audits.ts index a88f32f..8c87397 100644 --- a/src/commands/audits.ts +++ b/src/commands/audits.ts @@ -1,7 +1,7 @@ import Audit from "../entity/Audit"; import AuditTools from "../helpers/AuditTools"; import { Command } from "../type/command"; -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { AuditType } from "../constants/AuditType"; import EmbedColours from "../constants/EmbedColours"; @@ -9,14 +9,10 @@ export default class Audits extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("audits") .setDescription("View audits of a particular user in the server") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addSubcommand(subcommand => subcommand .setName('user') diff --git a/src/commands/ban.ts b/src/commands/ban.ts index 37ca2a6..a7f2d99 100644 --- a/src/commands/ban.ts +++ b/src/commands/ban.ts @@ -8,11 +8,6 @@ import SettingsHelper from "../helpers/SettingsHelper"; export default class Ban extends Command { constructor() { super(); - - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; super.CommandBuilder = new SlashCommandBuilder() .setName("ban") @@ -60,6 +55,11 @@ export default class Ban extends Command { }, ]); + if (!member.bannable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + await member.ban(); await interaction.reply(`\`${targetUser.user.tag}\` has been banned.`); diff --git a/src/commands/bunny.ts b/src/commands/bunny.ts index cf28dff..b735f61 100644 --- a/src/commands/bunny.ts +++ b/src/commands/bunny.ts @@ -7,8 +7,6 @@ export default class Bunny extends Command { constructor() { super(); - super.Category = "Fun"; - super.CommandBuilder = new SlashCommandBuilder() .setName("bunny") .setDescription("Get a random picture of a rabbit."); diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 160ec07..981c847 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -1,18 +1,14 @@ -import { CommandInteraction, SlashCommandBuilder, TextChannel } from "discord.js"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js"; import { Command } from "../type/command"; export default class Clear extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("clear") .setDescription("Clears the channel of messages") + .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages) .addNumberOption(option => option .setName('count') @@ -23,6 +19,7 @@ export default class Clear extends Command { public override async execute(interaction: CommandInteraction) { if (!interaction.isChatInputCommand()) return; + if (!interaction.channel) return; const totalToClear = interaction.options.getNumber('count'); @@ -32,6 +29,12 @@ export default class Clear extends Command { } const channel = interaction.channel as TextChannel; + + if (!channel.manageable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + await channel.bulkDelete(totalToClear); await interaction.reply(`${totalToClear} message(s) were removed.`); diff --git a/src/commands/code.ts b/src/commands/code.ts index f3fed94..2f0f7e2 100644 --- a/src/commands/code.ts +++ b/src/commands/code.ts @@ -1,5 +1,4 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; -import { CommandResponse } from "../constants/CommandResponse"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import StringTools from "../helpers/StringTools"; import { Command } from "../type/command"; @@ -8,14 +7,10 @@ export default class Code extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName('code') .setDescription('Manage the verification code of the server') + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addSubcommand(subcommand => subcommand .setName('randomise') @@ -26,23 +21,6 @@ export default class Code extends Command { .setDescription('Sends the embed with the current code to the current channel')); } - public override async precheckAsync(interaction: CommandInteraction): Promise<CommandResponse> { - if (!interaction.isChatInputCommand()) return CommandResponse.NotInServer; - if (!interaction.guild || !interaction.guildId) return CommandResponse.NotInServer; - - const isEnabled = await SettingsHelper.GetSetting("verification.enabled", interaction.guildId); - - if (!isEnabled) { - return CommandResponse.FeatureDisabled; - } - - if (isEnabled.toLocaleLowerCase() != 'true') { - return CommandResponse.FeatureDisabled; - } - - return CommandResponse.Ok; - } - public override async execute(interaction: CommandInteraction) { if (!interaction.isChatInputCommand()) return; diff --git a/src/commands/config.ts b/src/commands/config.ts index 60cd73d..b4887ea 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,6 +1,5 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { readFileSync } from "fs"; -import { CommandResponse } from "../constants/CommandResponse"; import DefaultValues from "../constants/DefaultValues"; import EmbedColours from "../constants/EmbedColours"; import Server from "../entity/Server"; @@ -10,14 +9,11 @@ import { Command } from "../type/command"; export default class Config extends Command { constructor() { super(); - super.Category = "Administration"; - super.Roles = [ - "administrator" - ] super.CommandBuilder = new SlashCommandBuilder() .setName('config') .setDescription('Configure the current server') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) .addSubcommand(subcommand => subcommand .setName('reset') @@ -52,23 +48,19 @@ export default class Config extends Command { .setDescription('Lists all settings')) } - public override async precheckAsync(interaction: CommandInteraction): Promise<CommandResponse> { - if (!interaction.guildId) return CommandResponse.ServerNotSetup; + public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + if (!interaction.guildId) return; const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [ "Settings", ]); if (!server) { - return CommandResponse.ServerNotSetup; + await interaction.reply('Server not setup. Please use the setup command,'); + return; } - return CommandResponse.Ok; - } - - public override async execute(interaction: CommandInteraction) { - if (!interaction.isChatInputCommand()) return; - switch (interaction.options.getSubcommand()) { case 'list': await this.SendHelpText(interaction); diff --git a/src/commands/disable.ts b/src/commands/disable.ts index 4ad4af8..4cca622 100644 --- a/src/commands/disable.ts +++ b/src/commands/disable.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import SettingsHelper from "../helpers/SettingsHelper"; import { Command } from "../type/command"; @@ -6,14 +6,10 @@ export default class Disable extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName('disable') .setDescription('Disables a command') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) .addSubcommand(subcommand => subcommand .setName('add') diff --git a/src/commands/ignore.ts b/src/commands/ignore.ts index bb5124e..2367762 100644 --- a/src/commands/ignore.ts +++ b/src/commands/ignore.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import IgnoredChannel from "../entity/IgnoredChannel"; import { Command } from "../type/command"; @@ -6,14 +6,10 @@ export default class Ignore extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName('ignore') - .setDescription('Ignore events in this channel'); + .setDescription('Ignore events in this channel') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } public override async execute(interaction: CommandInteraction) { diff --git a/src/commands/kick.ts b/src/commands/kick.ts index 8a520d6..6ddee0c 100644 --- a/src/commands/kick.ts +++ b/src/commands/kick.ts @@ -8,11 +8,6 @@ import SettingsHelper from "../helpers/SettingsHelper"; export default class Kick extends Command { constructor() { super(); - - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; super.CommandBuilder = new SlashCommandBuilder() .setName("kick") @@ -59,6 +54,11 @@ export default class Kick extends Command { value: reason, }, ]); + + if (!member.kickable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } await member.kick(); await interaction.reply(`\`${targetUser.user.tag}\` has been kicked.`); diff --git a/src/commands/mute.ts b/src/commands/mute.ts index 312eff3..d3b1493 100644 --- a/src/commands/mute.ts +++ b/src/commands/mute.ts @@ -9,15 +9,10 @@ export default class Mute extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("mute") .setDescription("Mute a member in the server with an optional reason") - .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addUserOption(option => option .setName('target') @@ -65,6 +60,11 @@ export default class Mute extends Command { return; } + if (!targetMember.manageable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + await targetMember.roles.add(mutedRole); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); diff --git a/src/commands/rules.ts b/src/commands/rules.ts index 180f779..f6a6406 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { existsSync, readFileSync } from "fs"; import EmbedColours from "../constants/EmbedColours"; import { Command } from "../type/command"; @@ -14,14 +14,10 @@ export default class Rules extends Command { constructor() { super(); - super.Category = "Admin"; - super.Roles = [ - "administrator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("rules") - .setDescription("Send the rules embeds for this server"); + .setDescription("Send the rules embeds for this server") + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } public override async execute(interaction: CommandInteraction) { diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 63a0fc8..6676ef8 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -1,18 +1,15 @@ -import { CommandInteraction, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import Server from "../entity/Server"; import { Command } from "../type/command"; export default class Setup extends Command { constructor() { super(); - super.Category = "Administration"; - super.Roles = [ - "moderator" - ] super.CommandBuilder = new SlashCommandBuilder() .setName('setup') - .setDescription('Makes the server ready to be configured'); + .setDescription('Makes the server ready to be configured') + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); } public override async execute(interaction: CommandInteraction) { diff --git a/src/commands/unmute.ts b/src/commands/unmute.ts index c40360c..59817f8 100644 --- a/src/commands/unmute.ts +++ b/src/commands/unmute.ts @@ -7,15 +7,10 @@ export default class Unmute extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("unmute") .setDescription("Unmute a member in the server with an optional reason") - .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addUserOption(option => option .setName('target') @@ -63,6 +58,11 @@ export default class Unmute extends Command { return; } + if (!targetMember.manageable) { + await interaction.reply('Insufficient permissions. Please contact a moderator.'); + return; + } + await targetMember.roles.remove(mutedRole); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId); diff --git a/src/commands/warn.ts b/src/commands/warn.ts index 1280983..5a41985 100644 --- a/src/commands/warn.ts +++ b/src/commands/warn.ts @@ -9,15 +9,10 @@ export default class Warn extends Command { constructor() { super(); - super.Category = "Moderation"; - super.Roles = [ - "moderator" - ]; - super.CommandBuilder = new SlashCommandBuilder() .setName("warn") .setDescription("Warns a member in the server with an optional reason") - .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers) + .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers) .addUserOption(option => option .setName('target') diff --git a/src/constants/CommandResponse.ts b/src/constants/CommandResponse.ts deleted file mode 100644 index b876ce8..0000000 --- a/src/constants/CommandResponse.ts +++ /dev/null @@ -1,7 +0,0 @@ -export enum CommandResponse { - Ok, - Unauthorised, - ServerNotSetup, - NotInServer, - FeatureDisabled, -} \ No newline at end of file diff --git a/src/constants/ErrorMessages.ts b/src/constants/ErrorMessages.ts deleted file mode 100644 index 588a143..0000000 --- a/src/constants/ErrorMessages.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { CommandResponse } from "./CommandResponse"; - -export default class ErrorMessages { - public static readonly InsufficientBotPermissions = "Unable to do this action, am I missing permissions?"; - public static readonly ChannelNotFound = "Unable to find channel"; - public static readonly RoleNotFound = "Unable to find role"; - - public static readonly UserUnauthorised = "You are not authorised to use this command"; - public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command"; - public static readonly NotInServer = "This command requires to be ran inside of a server"; - public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator"; - - public static GetErrorMessage(response: CommandResponse): string { - switch (response) { - case CommandResponse.Unauthorised: - return this.UserUnauthorised; - case CommandResponse.ServerNotSetup: - return this.ServerNotSetup; - case CommandResponse.NotInServer: - return this.NotInServer; - case CommandResponse.FeatureDisabled: - return this.FeatureDisabled; - default: - return ""; - } - } -} \ No newline at end of file diff --git a/src/entity/Role.ts b/src/entity/Role.ts index e4b2da7..954c208 100644 --- a/src/entity/Role.ts +++ b/src/entity/Role.ts @@ -15,6 +15,10 @@ export default class Role extends BaseEntity { @ManyToOne(() => Server, x => x.Roles) Server: Server; + + public SetServer(server: Server) { + this.Server = server; + } public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise<Role | undefined> { const connection = getConnection(); diff --git a/src/registry.ts b/src/registry.ts index 0671523..a83e7fa 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -11,7 +11,8 @@ import Disable from "./commands/disable"; import Ignore from "./commands/ignore"; import Kick from "./commands/kick"; import Mute from "./commands/mute"; -import Role from "./commands/role"; +import Role from "./commands/Role/role"; +import ConfigRole from "./commands/Role/config"; import Rules from "./commands/rules"; import Setup from "./commands/setup"; import Unmute from "./commands/unmute"; @@ -19,7 +20,9 @@ import Warn from "./commands/warn"; // Command Imports: MankBot import Entry from "./commands/501231711271780357/entry"; -import Lobby from "./commands/501231711271780357/lobby"; +import Lobby from "./commands/501231711271780357/Lobby/lobby"; +import AddLobby from "./commands/501231711271780357/Lobby/add"; +import RemoveLobby from "./commands/501231711271780357/Lobby/remove"; // Event Imports import MemberEvents from "./events/MemberEvents"; @@ -38,19 +41,25 @@ export default class Registry { CoreClient.RegisterCommand("ignore", new Ignore()); CoreClient.RegisterCommand("kick", new Kick()); CoreClient.RegisterCommand("mute", new Mute()); - CoreClient.RegisterCommand("role", new Role()); CoreClient.RegisterCommand("rules", new Rules()); CoreClient.RegisterCommand("unmute", new Unmute()); CoreClient.RegisterCommand("warn", new Warn()); CoreClient.RegisterCommand("setup", new Setup()); CoreClient.RegisterCommand("audits", new Audits()); + CoreClient.RegisterCommand("role", new Role()); + CoreClient.RegisterCommand("configrole", new ConfigRole()); + // Exclusive Commands: MankBot CoreClient.RegisterCommand("lobby", new Lobby(), "501231711271780357"); + CoreClient.RegisterCommand("lobbyAdd", new AddLobby(), "501231711271780357"); + CoreClient.RegisterCommand("lobbyRemove", new RemoveLobby(), "501231711271780357"); CoreClient.RegisterCommand("entry", new Entry(), "501231711271780357"); // Add Exclusive Commands to Test Server CoreClient.RegisterCommand("lobby", new Lobby(), "442730357897429002"); + CoreClient.RegisterCommand("addlobby", new AddLobby(), "442730357897429002"); + CoreClient.RegisterCommand("removelobby", new RemoveLobby(), "442730357897429002"); CoreClient.RegisterCommand("entry", new Entry(), "442730357897429002"); } diff --git a/src/type/command.ts b/src/type/command.ts index 6fea8c3..10d091d 100644 --- a/src/type/command.ts +++ b/src/type/command.ts @@ -1,23 +1,7 @@ -import { CommandResponse } from "../constants/CommandResponse"; import { CommandInteraction } from "discord.js"; export class Command { public CommandBuilder: any; - - public Roles: string[]; - public Category?: string; - - constructor() { - this.Roles = []; - } - - public precheck(interation: CommandInteraction): CommandResponse { - return CommandResponse.Ok; - } - - public async precheckAsync(interation: CommandInteraction): Promise<CommandResponse> { - return CommandResponse.Ok; - } public execute(interaction: CommandInteraction) { -- 2.43.4 From 09c431def7ec29f639a6b3f9461f924f88edb4f7 Mon Sep 17 00:00:00 2001 From: Ethan Lane <ethan@vylpes.com> Date: Sat, 17 Sep 2022 16:32:12 +0100 Subject: [PATCH 5/6] Fix guild-specific commands not showing up --- src/client/client.ts | 2 +- src/commands/501231711271780357/Lobby/add.ts | 14 +++++++++----- src/commands/501231711271780357/Lobby/lobby.ts | 2 +- src/commands/501231711271780357/Lobby/remove.ts | 5 +++-- src/commands/Role/role.ts | 3 +-- src/commands/audits.ts | 3 ++- src/commands/clear.ts | 1 + src/commands/config.ts | 12 ++++++++---- 8 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/client/client.ts b/src/client/client.ts index 4031f24..4b933f3 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -49,7 +49,7 @@ export class CoreClient extends Client { super.on("interactionCreate", this._events.onInteractionCreate); super.on("ready", this._events.onReady); - super.login(process.env.BOT_TOKEN); + await super.login(process.env.BOT_TOKEN); this._util.loadEvents(this, CoreClient._eventItems); this._util.loadSlashCommands(this); diff --git a/src/commands/501231711271780357/Lobby/add.ts b/src/commands/501231711271780357/Lobby/add.ts index 105844c..16a38d2 100644 --- a/src/commands/501231711271780357/Lobby/add.ts +++ b/src/commands/501231711271780357/Lobby/add.ts @@ -13,19 +13,23 @@ export default class AddRole extends Command { .addChannelOption(option => option .setName('channel') - .setDescription('The channel')) + .setDescription('The channel') + .setRequired(true)) .addRoleOption(option => option .setName('role') - .setDescription('The role to ping on request')) + .setDescription('The role to ping on request') + .setRequired(true)) .addNumberOption(option => option .setName('cooldown') - .setDescription('The cooldown in minutes')) + .setDescription('The cooldown in minutes') + .setRequired(true)) .addStringOption(option => option .setName('name') - .setDescription('The game name')); + .setDescription('The game name') + .setRequired(true)); } public override async execute(interaction: CommandInteraction) { @@ -49,6 +53,6 @@ export default class AddRole extends Command { const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string); await entity.Save(eLobby, entity); - await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes \` and will ping \`${role.name}\` on use`); + await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown.value} minutes \` and will ping \`${role.name}\` on use`); } } \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/lobby.ts b/src/commands/501231711271780357/Lobby/lobby.ts index 5be32ad..d7f0d42 100644 --- a/src/commands/501231711271780357/Lobby/lobby.ts +++ b/src/commands/501231711271780357/Lobby/lobby.ts @@ -36,6 +36,6 @@ export default class Lobby extends Command { lobby.MarkAsUsed(); await lobby.Save(eLobby, lobby); - await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@${lobby.RoleId}>`); + await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`); } } \ No newline at end of file diff --git a/src/commands/501231711271780357/Lobby/remove.ts b/src/commands/501231711271780357/Lobby/remove.ts index 9dac70b..b350316 100644 --- a/src/commands/501231711271780357/Lobby/remove.ts +++ b/src/commands/501231711271780357/Lobby/remove.ts @@ -14,7 +14,8 @@ export default class RemoveLobby extends Command { .addChannelOption(option => option .setName('channel') - .setDescription('The channel')); + .setDescription('The channel') + .setRequired(true)); } public override async execute(interaction: CommandInteraction) { @@ -34,6 +35,6 @@ export default class RemoveLobby extends Command { await BaseEntity.Remove<eLobby>(eLobby, entity); - await interaction.reply(`Removed <#${channel.channel.name}> from the list of lobby channels`); + await interaction.reply(`Removed <#${channel.channel.id}> from the list of lobby channels`); } } \ No newline at end of file diff --git a/src/commands/Role/role.ts b/src/commands/Role/role.ts index 6e57653..85b0abf 100644 --- a/src/commands/Role/role.ts +++ b/src/commands/Role/role.ts @@ -1,4 +1,4 @@ -import { CommandInteraction, EmbedBuilder, GuildMember, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; +import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js"; import { Command } from "../../type/command"; import { default as eRole } from "../../entity/Role"; import EmbedColours from "../../constants/EmbedColours"; @@ -69,7 +69,6 @@ export default class Role extends Command { } const roleManager = interaction.member.roles as GuildMemberRoleManager; - const member = interaction.member as GuildMember; const userRole = roleManager.cache.find(x => x.name == requestedRole.role!.name); const assignRole = interaction.guild.roles.cache.find(x => x.id == requestedRole.role!.id); diff --git a/src/commands/audits.ts b/src/commands/audits.ts index 8c87397..3dabe26 100644 --- a/src/commands/audits.ts +++ b/src/commands/audits.ts @@ -60,7 +60,8 @@ export default class Audits extends Command { { name: 'Mute', value: AuditType.Mute.toString() }, { name: 'Kick', value: AuditType.Kick.toString() }, { name: 'Ban', value: AuditType.Ban.toString() }, - )) + ) + .setRequired(true)) .addStringOption(option => option .setName('reason') diff --git a/src/commands/clear.ts b/src/commands/clear.ts index 981c847..a448a7b 100644 --- a/src/commands/clear.ts +++ b/src/commands/clear.ts @@ -13,6 +13,7 @@ export default class Clear extends Command { option .setName('count') .setDescription('The amount to delete') + .setRequired(true) .setMinValue(1) .setMaxValue(100)); } diff --git a/src/commands/config.ts b/src/commands/config.ts index b4887ea..a370f27 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -21,7 +21,8 @@ export default class Config extends Command { .addStringOption(option => option .setName('key') - .setDescription('The key'))) + .setDescription('The key') + .setRequired(true))) .addSubcommand(subcommand => subcommand .setName('get') @@ -29,7 +30,8 @@ export default class Config extends Command { .addStringOption(option => option .setName('key') - .setDescription('The key'))) + .setDescription('The key') + .setRequired(true))) .addSubcommand(subcommand => subcommand .setName('set') @@ -37,11 +39,13 @@ export default class Config extends Command { .addStringOption(option => option .setName('key') - .setDescription('The key')) + .setDescription('The key') + .setRequired(true)) .addStringOption(option => option .setName('value') - .setDescription('The value'))) + .setDescription('The value') + .setRequired(true))) .addSubcommand(subcommand => subcommand .setName('list') -- 2.43.4 From 7e827090f9386711ac15511267f37c4070fadd08 Mon Sep 17 00:00:00 2001 From: Ethan Lane <ethan@vylpes.com> Date: Sun, 18 Sep 2022 08:53:17 +0100 Subject: [PATCH 6/6] Fix changes requested --- .dev.env | 1 - .prod.env | 1 - .stage.env | 1 - src/client/events.ts | 5 +++-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.dev.env b/.dev.env index e4e3d28..199bb0a 100644 --- a/.dev.env +++ b/.dev.env @@ -11,7 +11,6 @@ BOT_VER=3.1 BOT_AUTHOR=Vylpes BOT_OWNERID=147392775707426816 BOT_CLIENTID=682942374040961060 -BOT_PREFIX=d! ABOUT_FUNDING=https://ko-fi.com/vylpes ABOUT_REPO=https://github.com/vylpes/vylbot-app diff --git a/.prod.env b/.prod.env index ff055a4..c4fd73a 100644 --- a/.prod.env +++ b/.prod.env @@ -11,7 +11,6 @@ BOT_VER=3.1 BOT_AUTHOR=Vylpes BOT_OWNERID=147392775707426816 BOT_CLIENTID=680083120896081954 -BOT_PREFIX=v! ABOUT_FUNDING=https://ko-fi.com/vylpes ABOUT_REPO=https://github.com/vylpes/vylbot-app diff --git a/.stage.env b/.stage.env index 6788ec2..85e3068 100644 --- a/.stage.env +++ b/.stage.env @@ -11,7 +11,6 @@ BOT_VER=3.1 BOT_AUTHOR=Vylpes BOT_OWNERID=147392775707426816 BOT_CLIENTID=1016767908740857949 -BOT_PREFIX=s! ABOUT_FUNDING=https://ko-fi.com/vylpes ABOUT_REPO=https://github.com/vylpes/vylbot-app diff --git a/src/client/events.ts b/src/client/events.ts index 5970f47..2d36001 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -11,9 +11,10 @@ export class Events { const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); const disabledCommands = disabledCommandsString?.split(","); - if (disabledCommands?.find(x => x == interaction.commandName)) { - await interaction.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); + const disabledCommandsMessage = await SettingsHelper.GetSetting("commands.disabled.message", interaction.guildId); + if (disabledCommands?.find(x => x == interaction.commandName)) { + await interaction.reply(disabledCommandsMessage || "This command is disabled."); return; } -- 2.43.4