From 9e930276410b6a0b8307335dfe53d3d369eb8958 Mon Sep 17 00:00:00 2001 From: Shadow Date: Wed, 4 Sep 2024 11:51:48 -0500 Subject: [PATCH] feat: make components automatically registered --- .changeset/curly-dots-burn.md | 5 +++++ apps/cloudo/src/commands/testing/button.ts | 1 - .../src/commands/testing/every_select.ts | 7 ------ apps/rocko/src/commands/testing/button.ts | 1 - .../src/commands/testing/every_select.ts | 22 +++++++------------ packages/carbon/src/abstracts/BaseCommand.ts | 12 +--------- .../carbon/src/abstracts/BaseInteraction.ts | 20 ++++++++++++++--- packages/carbon/src/classes/Row.ts | 6 ++--- .../carbon/src/internals/CommandHandler.ts | 4 ++++ .../carbon/src/internals/ComponentHandler.ts | 19 ++++++++++++---- 10 files changed, 53 insertions(+), 44 deletions(-) create mode 100644 .changeset/curly-dots-burn.md diff --git a/.changeset/curly-dots-burn.md b/.changeset/curly-dots-burn.md new file mode 100644 index 000000000..82d30b404 --- /dev/null +++ b/.changeset/curly-dots-burn.md @@ -0,0 +1,5 @@ +--- +"@buape/carbon": patch +--- + +feat: make components automatically registered diff --git a/apps/cloudo/src/commands/testing/button.ts b/apps/cloudo/src/commands/testing/button.ts index e5f6cf61d..f434617ab 100644 --- a/apps/cloudo/src/commands/testing/button.ts +++ b/apps/cloudo/src/commands/testing/button.ts @@ -12,7 +12,6 @@ export default class ButtonCommand extends Command { name = "button" description = "A simple command with a button!" defer = true - components = [new PingButton(), new Link()] async run(interaction: CommandInteraction) { await interaction.reply({ diff --git a/apps/cloudo/src/commands/testing/every_select.ts b/apps/cloudo/src/commands/testing/every_select.ts index ce0923e17..fa70fa8cd 100644 --- a/apps/cloudo/src/commands/testing/every_select.ts +++ b/apps/cloudo/src/commands/testing/every_select.ts @@ -18,13 +18,6 @@ export default class SelectCommand extends Command { name = "every_select" description = "Send every select menu" defer = true - components = [ - new StringSelect(), - new RoleSelect(), - new MentionableSelect(), - new ChannelSelect(), - new UserSelect() - ] async run(interaction: CommandInteraction) { interaction.reply({ diff --git a/apps/rocko/src/commands/testing/button.ts b/apps/rocko/src/commands/testing/button.ts index e5f6cf61d..f434617ab 100644 --- a/apps/rocko/src/commands/testing/button.ts +++ b/apps/rocko/src/commands/testing/button.ts @@ -12,7 +12,6 @@ export default class ButtonCommand extends Command { name = "button" description = "A simple command with a button!" defer = true - components = [new PingButton(), new Link()] async run(interaction: CommandInteraction) { await interaction.reply({ diff --git a/apps/rocko/src/commands/testing/every_select.ts b/apps/rocko/src/commands/testing/every_select.ts index ce0923e17..be308acd7 100644 --- a/apps/rocko/src/commands/testing/every_select.ts +++ b/apps/rocko/src/commands/testing/every_select.ts @@ -18,24 +18,18 @@ export default class SelectCommand extends Command { name = "every_select" description = "Send every select menu" defer = true - components = [ - new StringSelect(), - new RoleSelect(), - new MentionableSelect(), - new ChannelSelect(), - new UserSelect() - ] async run(interaction: CommandInteraction) { + const row = new Row() + row.addComponent(new StringSelect()) + row.addComponent(new RoleSelect()) + row.addComponent(new MentionableSelect()) + row.addComponent(new ChannelSelect()) + row.addComponent(new UserSelect()) + interaction.reply({ content: "Select menus! <:caughtIn4k:1145473115703496816>", - components: [ - new Row([new StringSelect()]), - new Row([new RoleSelect()]), - new Row([new MentionableSelect()]), - new Row([new ChannelSelect()]), - new Row([new UserSelect()]) - ] + components: [row] }) } } diff --git a/packages/carbon/src/abstracts/BaseCommand.ts b/packages/carbon/src/abstracts/BaseCommand.ts index 514102319..b23cf91b7 100644 --- a/packages/carbon/src/abstracts/BaseCommand.ts +++ b/packages/carbon/src/abstracts/BaseCommand.ts @@ -2,11 +2,7 @@ import { ApplicationCommandType, type RESTPostAPIApplicationCommandsJSONBody } from "discord-api-types/v10" -import { - ApplicationIntegrationType, - type BaseComponent, - InteractionContextType -} from "../index.js" +import { ApplicationIntegrationType, InteractionContextType } from "../index.js" /** * Represents the base data of a command that the user creates @@ -50,12 +46,6 @@ export abstract class BaseCommand { InteractionContextType.PrivateChannel ] - /** - * All the components that the command is able to use. - * You mount these here so the handler can access them - */ - components?: BaseComponent[] = [] - /** * All the paginators that the command is able to use. * You mount these here so the handler can access them diff --git a/packages/carbon/src/abstracts/BaseInteraction.ts b/packages/carbon/src/abstracts/BaseInteraction.ts index 7b7a05194..9e8b94821 100644 --- a/packages/carbon/src/abstracts/BaseInteraction.ts +++ b/packages/carbon/src/abstracts/BaseInteraction.ts @@ -36,6 +36,10 @@ export type InteractionReplyOptions = { * The files to send in the interaction */ files?: InteractionFileData[] + /** + * Whether the interaction should be ephemeral + */ + ephemeral?: boolean } /** @@ -80,12 +84,15 @@ export abstract class BaseInteraction extends Base { */ _deferred = false - constructor(client: Client, data: T) { + private defaultEphemeral = false + + constructor(client: Client, data: T, defaults: { ephemeral?: boolean } = {}) { super(client) this.rawData = data this.type = data.type this.userId = this.rawData.user?.id || this.rawData.member?.user.id || undefined + if (defaults.ephemeral) this.defaultEphemeral = defaults.ephemeral } get message(): Message | null { @@ -125,6 +132,11 @@ export abstract class BaseInteraction extends Base { data: InteractionReplyData, options: InteractionReplyOptions = {} ) { + data.components?.map((row) => { + row.components.map((component) => { + this.client.componentHandler.registerComponent(component) + }) + }) if (this._deferred) { await this.client.rest.patch( Routes.webhookMessage( @@ -148,7 +160,8 @@ export abstract class BaseInteraction extends Base { type: InteractionResponseType.ChannelMessageWithSource, data: { ...data, - components: data.components?.map((row) => row.serialize()) + components: data.components?.map((row) => row.serialize()), + ephemeral: options.ephemeral ?? this.defaultEphemeral } }, files: options.files @@ -169,7 +182,8 @@ export abstract class BaseInteraction extends Base { Routes.interactionCallback(this.rawData.id, this.rawData.token), { body: { - type: InteractionResponseType.DeferredChannelMessageWithSource + type: InteractionResponseType.DeferredChannelMessageWithSource, + ephemeral: this.defaultEphemeral } } ) diff --git a/packages/carbon/src/classes/Row.ts b/packages/carbon/src/classes/Row.ts index df9c96cef..56577d01d 100644 --- a/packages/carbon/src/classes/Row.ts +++ b/packages/carbon/src/classes/Row.ts @@ -4,10 +4,10 @@ export class Row { /** * The components in the action row */ - components: BaseComponent[] + components: BaseComponent[] = [] - constructor(components: BaseComponent[]) { - this.components = components + constructor(components?: BaseComponent[]) { + if (components) this.components = components } /** diff --git a/packages/carbon/src/internals/CommandHandler.ts b/packages/carbon/src/internals/CommandHandler.ts index 7918f9b6b..ea1abe618 100644 --- a/packages/carbon/src/internals/CommandHandler.ts +++ b/packages/carbon/src/internals/CommandHandler.ts @@ -76,6 +76,10 @@ export class CommandHandler extends Base { throw new Error("Command is not a valid command type") } + /** + * Handle a command interaction + * @internal + */ async handleCommandInteraction( rawInteraction: APIApplicationCommandInteraction ) { diff --git a/packages/carbon/src/internals/ComponentHandler.ts b/packages/carbon/src/internals/ComponentHandler.ts index 0591d8672..b1a8961e8 100644 --- a/packages/carbon/src/internals/ComponentHandler.ts +++ b/packages/carbon/src/internals/ComponentHandler.ts @@ -19,11 +19,22 @@ import { StringSelectMenuInteraction } from "./StringSelectMenuInteraction.js" import { UserSelectMenuInteraction } from "./UserSelectMenuInteraction.js" export class ComponentHandler extends Base { + components: BaseComponent[] = [] + /** + * Register a component with the handler + * @internal + */ + registerComponent(component: BaseComponent) { + if (!this.components.find((x) => x.customId === component.customId)) { + this.components.push(component) + } + } + /** + * Handle an interaction + * @internal + */ async handleInteraction(data: APIMessageComponentInteraction) { - const allComponents = this.client.commands - .filter((x) => x.components && x.components.length > 0) - .flatMap((x) => x.components) as BaseComponent[] - const component = allComponents.find( + const component = this.components.find( (x) => x.customId === data.data.custom_id && x.type === data.data.component_type