From ae5e39d08d2d5a132f54b0fc3d832843e18a20fb Mon Sep 17 00:00:00 2001 From: xHyroM Date: Sat, 8 Apr 2023 14:05:40 +0200 Subject: [PATCH] feat(bot): add modals --- packages/bot/scripts/build.mjs | 1 + packages/bot/src/commands/setup.ts | 31 +++++-- packages/bot/src/components/setup.ts | 35 ++++++++ packages/bot/src/index.ts | 86 +++++++++++++------ packages/bot/src/modals/setup.ts | 8 ++ packages/bot/src/registers.ts | 6 ++ packages/bot/src/structs/Command.ts | 11 ++- packages/bot/src/structs/Component.ts | 4 + packages/bot/src/structs/Modal.ts | 23 +++++ packages/bot/src/structs/contexts/Context.ts | 32 ++++++- .../bot/src/structs/contexts/ModalContext.ts | 12 +++ 11 files changed, 216 insertions(+), 33 deletions(-) create mode 100644 packages/bot/src/components/setup.ts create mode 100644 packages/bot/src/modals/setup.ts create mode 100644 packages/bot/src/structs/Modal.ts create mode 100644 packages/bot/src/structs/contexts/ModalContext.ts diff --git a/packages/bot/scripts/build.mjs b/packages/bot/scripts/build.mjs index 297559a..4f608b4 100644 --- a/packages/bot/scripts/build.mjs +++ b/packages/bot/scripts/build.mjs @@ -25,6 +25,7 @@ Promise.all([ charset: "utf8", minify: !dev, watch: watch, + external: ["node:events"], }), ]) .catch((err) => { diff --git a/packages/bot/src/commands/setup.ts b/packages/bot/src/commands/setup.ts index 77c4ed4..e4c3212 100644 --- a/packages/bot/src/commands/setup.ts +++ b/packages/bot/src/commands/setup.ts @@ -1,17 +1,37 @@ import { Command } from "../structs/Command"; -import { ChannelType } from "discord-api-types/v10"; -import { ActionRowBuilder, ChannelSelectMenuBuilder } from "builders"; +import { MessageFlags, TextInputStyle } from "discord-api-types/v10"; +import { ActionRowBuilder, TextInputBuilder } from "builders"; new Command({ name: "setup", + flags: MessageFlags.Ephemeral, + acknowledge: false, run: async (ctx) => { - await ctx.editReply({ - content: "Setup", + return ctx.returnModal({ + title: "Setup", + custom_id: "test", + components: [ + new ActionRowBuilder() + .addComponents( + new TextInputBuilder() + .setLabel("ASDSAD") + .setCustomId("prefix") + .setPlaceholder("Prefix") + .setStyle(TextInputStyle.Paragraph) + .setRequired(true), + ) + .toJSON(), + ], + }); + + /** + * await ctx.editReply({ + content: "Select the channel to which the panel will be sent.", components: [ new ActionRowBuilder() .addComponents( new ChannelSelectMenuBuilder() - .setCustomId("channel") + .setCustomId("setup:part-channel") .addChannelTypes( ChannelType.GuildAnnouncement, ChannelType.GuildText, @@ -20,5 +40,6 @@ new Command({ .toJSON(), ], }); + */ }, }); diff --git a/packages/bot/src/components/setup.ts b/packages/bot/src/components/setup.ts new file mode 100644 index 0000000..55062cd --- /dev/null +++ b/packages/bot/src/components/setup.ts @@ -0,0 +1,35 @@ +import { ActionRowBuilder, RoleSelectMenuBuilder } from "builders"; +import { Component } from "../structs/Component"; +import { MessageFlags } from "discord-api-types/v10"; + +new Component({ + id: "setup:part-channel", + flags: MessageFlags.Ephemeral, + run: async (ctx) => { + await ctx.editReply({ + content: "Select the roles that will be available in the menu.", + components: [ + new ActionRowBuilder() + .addComponents( + new RoleSelectMenuBuilder() + .setCustomId("setup:part-roles") + .setPlaceholder("Select roles") + .setMinValues(1) + .setMaxValues(25), + ) + .toJSON(), + ], + }); + }, +}); + +new Component({ + id: "setup:part-roles", + flags: MessageFlags.Ephemeral, + run: async (ctx) => { + await ctx.editReply({ + content: "done", + components: [], + }); + }, +}); diff --git a/packages/bot/src/index.ts b/packages/bot/src/index.ts index 4a0b335..984b65c 100644 --- a/packages/bot/src/index.ts +++ b/packages/bot/src/index.ts @@ -1,18 +1,21 @@ import "./commands/setup"; +import "./components/setup"; +import "./modals/setup"; + import { APIApplicationCommandInteraction, APIMessageComponentInteraction, + APIModalSubmitInteraction, APIPingInteraction, InteractionResponseType, InteractionType, } from "discord-api-types/v10"; -import { COMMANDS, COMPONENTS } from "./registers"; +import { COMMANDS, COMPONENTS, MODALS } from "./registers"; import { verify } from "./utils/verify"; import respond from "./utils/respond"; import { CommandContext } from "./structs/contexts/CommandContext"; import { ComponentContext } from "./structs/contexts/ComponentContext"; - -console.log(COMMANDS); +import { ModalContext } from "./structs/contexts/ModalContext"; export default { fetch: async (request: Request, env: Env) => { @@ -27,6 +30,7 @@ export default { const interaction = (await request.json()) as | APIPingInteraction | APIApplicationCommandInteraction + | APIModalSubmitInteraction | APIMessageComponentInteraction; if (interaction.type === InteractionType.Ping) @@ -34,34 +38,66 @@ export default { type: InteractionResponseType.Pong, }); - if (interaction.type === InteractionType.ApplicationCommand) { - const command = COMMANDS.find( - (cmd) => cmd.name === interaction.data.name, - ); + switch (interaction.type) { + case InteractionType.ApplicationCommand: { + const command = COMMANDS.find( + (cmd) => cmd.name === interaction.data.name, + ); - if (!command) return new Response("Unknown command", { status: 404 }); + if (!command) return new Response("Unknown command", { status: 404 }); - try { - return respond({ - type: InteractionResponseType.DeferredChannelMessageWithSource, - }); - } finally { - command.run(new CommandContext(interaction, env)); + try { + if (command.acknowledge) + return respond({ + type: InteractionResponseType.DeferredChannelMessageWithSource, + data: { + flags: command.flags, + }, + }); + } finally { + if (command.acknowledge) + command.run(new CommandContext(interaction, env)); + // rome-ignore lint/correctness/noUnsafeFinally: it works, must do better typings etc... + else return command.run(new CommandContext(interaction, env)); + } + + break; } - } + case InteractionType.ModalSubmit: { + const modal = MODALS.find((md) => md.id === interaction.data.custom_id); - const component = COMPONENTS.find( - (cmp) => cmp.id === interaction.data.custom_id, - ); + if (!modal) return new Response("Unknown modal", { status: 404 }); - if (!component) return new Response("Unknown component", { status: 404 }); + try { + return respond({ + type: InteractionResponseType.DeferredChannelMessageWithSource, + data: { + flags: modal.flags, + }, + }); + } finally { + modal.run(new ModalContext(interaction, env)); + } + } + case InteractionType.MessageComponent: { + const component = COMPONENTS.find( + (cmp) => cmp.id === interaction.data.custom_id, + ); - try { - return respond({ - type: InteractionResponseType.DeferredChannelMessageWithSource, - }); - } finally { - component.run(new ComponentContext(interaction, env)); + if (!component) + return new Response("Unknown component", { status: 404 }); + + try { + return respond({ + type: InteractionResponseType.DeferredChannelMessageWithSource, + data: { + flags: component.flags, + }, + }); + } finally { + component.run(new ComponentContext(interaction, env)); + } + } } }, }; diff --git a/packages/bot/src/modals/setup.ts b/packages/bot/src/modals/setup.ts new file mode 100644 index 0000000..198e445 --- /dev/null +++ b/packages/bot/src/modals/setup.ts @@ -0,0 +1,8 @@ +import { Modal } from "../structs/Modal"; + +new Modal({ + id: "test", + run: async (ctx) => { + // TODO: finish + }, +}); diff --git a/packages/bot/src/registers.ts b/packages/bot/src/registers.ts index 59b7fe0..f80ae55 100644 --- a/packages/bot/src/registers.ts +++ b/packages/bot/src/registers.ts @@ -1,8 +1,10 @@ import { Command } from "./structs/Command"; import { Component } from "./structs/Component"; +import { Modal } from "./structs/Modal"; export const COMMANDS: Command[] = []; export const COMPONENTS: Component[] = []; +export const MODALS: Modal[] = []; export const registerCommand = (command: Command) => { COMMANDS.push(command); @@ -11,3 +13,7 @@ export const registerCommand = (command: Command) => { export const registerComponent = (component: Component) => { COMPONENTS.push(component); }; + +export const registerModal = (modal: Modal) => { + MODALS.push(modal); +}; diff --git a/packages/bot/src/structs/Command.ts b/packages/bot/src/structs/Command.ts index 3450083..03b8dde 100644 --- a/packages/bot/src/structs/Command.ts +++ b/packages/bot/src/structs/Command.ts @@ -1,17 +1,24 @@ +import { MessageFlags } from "discord-api-types/v10"; import { registerCommand } from "../registers"; import { CommandContext } from "./contexts/CommandContext"; interface CommandOptions { name: string; + acknowledge?: boolean; + flags?: MessageFlags; run: (interaction: CommandContext) => void; } -export class Command { +export class Command { public name: string; - public run: (interaction: CommandContext) => void; + public acknowledge: boolean; + public flags: MessageFlags | undefined; + public run: (interaction: CommandContext) => void | Response; constructor(options: CommandOptions) { this.name = options.name; + this.acknowledge = options.acknowledge ?? true; + this.flags = options.flags; this.run = options.run; registerCommand(this); diff --git a/packages/bot/src/structs/Component.ts b/packages/bot/src/structs/Component.ts index 8c3290e..1ee0df7 100644 --- a/packages/bot/src/structs/Component.ts +++ b/packages/bot/src/structs/Component.ts @@ -1,17 +1,21 @@ +import { MessageFlags } from "discord-api-types/v10"; import { registerComponent } from "../registers"; import { ComponentContext } from "./contexts/ComponentContext"; interface ComponentOptions { id: string; + flags?: MessageFlags; run: (interaction: ComponentContext) => void; } export class Component { public id: string; + public flags: MessageFlags | undefined; public run: (interaction: ComponentContext) => void; constructor(options: ComponentOptions) { this.id = options.id; + this.flags = options.flags; this.run = options.run; registerComponent(this); diff --git a/packages/bot/src/structs/Modal.ts b/packages/bot/src/structs/Modal.ts new file mode 100644 index 0000000..94657ad --- /dev/null +++ b/packages/bot/src/structs/Modal.ts @@ -0,0 +1,23 @@ +import { MessageFlags } from "discord-api-types/v10"; +import { registerModal } from "../registers"; +import { ModalContext } from "./contexts/ModalContext"; + +interface ModalOptions { + id: string; + flags?: MessageFlags; + run: (interaction: ModalContext) => void; +} + +export class Modal { + public id: string; + public flags: MessageFlags | undefined; + public run: (interaction: ModalContext) => void; + + constructor(options: ModalOptions) { + this.id = options.id; + this.flags = options.flags; + this.run = options.run; + + registerModal(this); + } +} diff --git a/packages/bot/src/structs/contexts/Context.ts b/packages/bot/src/structs/contexts/Context.ts index 4dd548e..52e48ea 100644 --- a/packages/bot/src/structs/contexts/Context.ts +++ b/packages/bot/src/structs/contexts/Context.ts @@ -1,9 +1,12 @@ import { APIInteraction, APIInteractionResponseCallbackData, + APIModalInteractionResponseCallbackData, + InteractionResponseType, RouteBases, Routes, } from "discord-api-types/v10"; +import respond from "../../utils/respond"; export class Context { public interaction: APIInteraction; @@ -17,7 +20,7 @@ export class Context { public async editReply(content: APIInteractionResponseCallbackData) { return await fetch( `${RouteBases.api}${Routes.webhookMessage( - this.interaction.application_id, + this.interaction.id, this.interaction.token, )}`, { @@ -30,4 +33,31 @@ export class Context { }, ); } + + public async showModal(content: APIModalInteractionResponseCallbackData) { + return await fetch( + `${RouteBases.api}${Routes.interactionCallback( + this.interaction.id, + this.interaction.token, + )}`, + { + method: "POST", + body: JSON.stringify({ + type: InteractionResponseType.Modal, + data: content, + }), + headers: { + "Content-Type": "application/json", + Authorization: `Bot ${this.env.token}`, + }, + }, + ); + } + + public async returnModal(content: APIModalInteractionResponseCallbackData) { + return respond({ + type: InteractionResponseType.Modal, + data: content, + }); + } } diff --git a/packages/bot/src/structs/contexts/ModalContext.ts b/packages/bot/src/structs/contexts/ModalContext.ts new file mode 100644 index 0000000..b5439b0 --- /dev/null +++ b/packages/bot/src/structs/contexts/ModalContext.ts @@ -0,0 +1,12 @@ +import { APIModalSubmitInteraction } from "discord-api-types/v10"; +import { Context } from "./Context"; + +export class ModalContext extends Context { + public interaction: APIModalSubmitInteraction; + + constructor(interaction: APIModalSubmitInteraction, env: Env) { + super(interaction, env); + + this.interaction = interaction; + } +}