diff --git a/src/buttonEvents/verify.ts b/src/buttonEvents/verify.ts new file mode 100644 index 0000000..c3f696c --- /dev/null +++ b/src/buttonEvents/verify.ts @@ -0,0 +1,42 @@ +import { ButtonInteraction, CacheType } from "discord.js"; +import { ButtonEvent } from "../type/buttonEvent"; +import SettingsHelper from "../helpers/SettingsHelper"; + +export default class Verify extends ButtonEvent { + public override async execute(interaction: ButtonInteraction) { + if (!interaction.guildId || !interaction.guild) return; + + const roleName = await SettingsHelper.GetSetting("verification.role", interaction.guildId); + + if (!roleName) return; + + const role = interaction.guild.roles.cache.find(x => x.name == roleName); + + if (!role) { + await interaction.reply({ + content: `Unable to find the role, ${roleName}`, + ephemeral: true, + }); + + return; + } + + const member = interaction.guild.members.cache.find(x => x.id == interaction.user.id); + + if (!member || !member.manageable) { + await interaction.reply({ + content: "Unable to give role to user", + ephemeral: true, + }); + + return; + } + + await member.roles.add(role); + + await interaction.reply({ + content: "Given role", + ephemeral: true, + }); + } +} \ No newline at end of file diff --git a/src/client/client.ts b/src/client/client.ts index 9e8f4ef..415d645 100644 --- a/src/client/client.ts +++ b/src/client/client.ts @@ -9,10 +9,13 @@ import { Command } from "../type/command"; import { Events } from "./events"; import { Util } from "./util"; import AppDataSource from "../database/dataSources/appDataSource"; +import ButtonEventItem from "../contracts/ButtonEventItem"; +import { ButtonEvent } from "../type/buttonEvent"; export class CoreClient extends Client { private static _commandItems: ICommandItem[]; private static _eventItems: IEventItem[]; + private static _buttonEvents: ButtonEventItem[]; private _events: Events; private _util: Util; @@ -25,12 +28,17 @@ export class CoreClient extends Client { return this._eventItems; } + public static get buttonEvents(): ButtonEventItem[] { + return this._buttonEvents; + } + constructor(intents: number[], partials: Partials[]) { super({ intents: intents, partials: partials }); dotenv.config(); CoreClient._commandItems = []; CoreClient._eventItems = []; + CoreClient._buttonEvents = []; this._events = new Events(); this._util = new Util(); @@ -73,4 +81,13 @@ export class CoreClient extends Client { CoreClient._eventItems.push(item); } + + public static RegisterButtonEvent(buttonId: string, event: ButtonEvent) { + const item: ButtonEventItem = { + ButtonId: buttonId, + Event: event, + }; + + CoreClient._buttonEvents.push(item); + } } diff --git a/src/client/events.ts b/src/client/events.ts index 2d36001..97d8cc7 100644 --- a/src/client/events.ts +++ b/src/client/events.ts @@ -1,40 +1,18 @@ import { Interaction } from "discord.js"; -import ICommandItem from "../contracts/ICommandItem"; -import SettingsHelper from "../helpers/SettingsHelper"; -import { CoreClient } from "./client"; +import ChatInputCommand from "./interactionCreate/chatInputCommand"; +import Button from "./interactionCreate/button"; export class Events { public async onInteractionCreate(interaction: Interaction) { - if (!interaction.isChatInputCommand()) return; if (!interaction.guildId) return; - const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId); - const disabledCommands = disabledCommandsString?.split(","); - - 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; + if (interaction.isChatInputCommand()) { + ChatInputCommand.onChatInput(interaction); } - 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) { - await interaction.reply('Command not found'); - return; - } - - itemToUse = item; - } else { - itemToUse = itemForServer; + if (interaction.isButton()) { + Button.onButtonClicked(interaction); } - - itemToUse.Command.execute(interaction); } // Emit when bot is logged in and ready to use diff --git a/src/client/interactionCreate/button.ts b/src/client/interactionCreate/button.ts new file mode 100644 index 0000000..f64f6c7 --- /dev/null +++ b/src/client/interactionCreate/button.ts @@ -0,0 +1,17 @@ +import { ButtonInteraction } from "discord.js"; +import { CoreClient } from "../client"; + +export default class Button { + public static async onButtonClicked(interaction: ButtonInteraction) { + if (!interaction.isButton) return; + + const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(" ")[0]); + + if (!item) { + await interaction.reply("Event not found."); + return; + } + + item.Event.execute(interaction); + } +} \ No newline at end of file diff --git a/src/client/interactionCreate/chatInputCommand.ts b/src/client/interactionCreate/chatInputCommand.ts new file mode 100644 index 0000000..8690994 --- /dev/null +++ b/src/client/interactionCreate/chatInputCommand.ts @@ -0,0 +1,27 @@ +import { Interaction } from "discord.js"; +import { CoreClient } from "../client"; +import ICommandItem from "../../contracts/ICommandItem"; + +export default class ChatInputCommand { + public static async onChatInput(interaction: Interaction) { + if (!interaction.isChatInputCommand()) 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) { + await interaction.reply("Command not found."); + return; + } + + itemToUse = item; + } else { + itemToUse = itemForServer; + } + + itemToUse.Command.execute(interaction); + } +} \ No newline at end of file diff --git a/src/commands/rules.ts b/src/commands/rules.ts index 930138c..c50cb36 100644 --- a/src/commands/rules.ts +++ b/src/commands/rules.ts @@ -1,7 +1,8 @@ -import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js"; import { existsSync, readFileSync } from "fs"; import EmbedColours from "../constants/EmbedColours"; import { Command } from "../type/command"; +import SettingsHelper from "../helpers/SettingsHelper"; interface IRules { title?: string; @@ -14,13 +15,36 @@ export default class Rules extends Command { constructor() { super(); - super.CommandBuilder = new SlashCommandBuilder() - .setName("rules") - .setDescription("Send the rules embeds for this server") - .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator); + this.CommandBuilder = new SlashCommandBuilder() + .setName('rules') + .setDescription("Rules-related commands") + .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator) + .addSubcommand(x => + x + .setName('embeds') + .setDescription('Send the rules embeds for this server')) + .addSubcommand(x => + x + .setName('access') + .setDescription('Send the server verification embed button')); } public override async execute(interaction: CommandInteraction) { + if (!interaction.isChatInputCommand()) return; + + switch (interaction.options.getSubcommand()) { + case "embeds": + await this.SendEmbeds(interaction); + break; + case "access": + await this.SendAccessButton(interaction); + break; + default: + await interaction.reply("Subcommand doesn't exist."); + } + } + + private async SendEmbeds(interaction: CommandInteraction) { if (!interaction.guildId) return; if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) { @@ -71,4 +95,27 @@ export default class Rules extends Command { await interaction.reply({ embeds: [ successEmbed ], ephemeral: true }); } + + private async SendAccessButton(interaction: CommandInteraction) { + if (!interaction.guildId) return; + + const buttonLabel = await SettingsHelper.GetSetting("rules.access.label", interaction.guildId); + + const row = new ActionRowBuilder() + .addComponents([ + new ButtonBuilder() + .setCustomId('verify') + .setStyle(ButtonStyle.Primary) + .setLabel(buttonLabel || "Verify") + ]); + + await interaction.channel?.send({ + components: [ row ] + }); + + await interaction.reply({ + content: "Success", + ephemeral: true, + }); + } } \ No newline at end of file diff --git a/src/constants/DefaultValues.ts b/src/constants/DefaultValues.ts index 8608fbb..177ddee 100644 --- a/src/constants/DefaultValues.ts +++ b/src/constants/DefaultValues.ts @@ -31,6 +31,7 @@ export default class DefaultValues { // Rules (Command) this.values.push({ Key: "rules.file", Value: "data/rules/rules" }); + this.values.push({ Key: "rules.access.label", Value: "Verify" }); // Channels this.values.push({ Key: "channels.logs.message", Value: "message-logs" }); diff --git a/src/contracts/ButtonEventItem.ts b/src/contracts/ButtonEventItem.ts new file mode 100644 index 0000000..4a4c368 --- /dev/null +++ b/src/contracts/ButtonEventItem.ts @@ -0,0 +1,8 @@ +import { ButtonEvent } from "../type/buttonEvent"; + +interface ButtonEventItem { + ButtonId: string, + Event: ButtonEvent, +} + +export default ButtonEventItem; \ No newline at end of file diff --git a/src/registry.ts b/src/registry.ts index 1e03201..ee3efed 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -84,4 +84,8 @@ export default class Registry { CoreClient.RegisterEvent(EventType.MessageUpdate, MessageUpdate); CoreClient.RegisterEvent(EventType.MessageCreate, MessageCreate); } + + public static RegisterButtonEvents() { + + } } \ No newline at end of file diff --git a/src/type/buttonEvent.ts b/src/type/buttonEvent.ts new file mode 100644 index 0000000..8a45bd0 --- /dev/null +++ b/src/type/buttonEvent.ts @@ -0,0 +1,5 @@ +import { ButtonInteraction } from "discord.js"; + +export abstract class ButtonEvent { + abstract execute(interaction: ButtonInteraction): Promise; +} \ No newline at end of file