From e11fb83b4414a836f4d9ff97cec3c349da56e2a2 Mon Sep 17 00:00:00 2001 From: CarelessInternet Date: Mon, 29 Dec 2025 18:48:00 +0000 Subject: [PATCH] refactor(bot): use modal where possible for select menus --- .../commands/staff/bot-profile/component.ts | 8 +-- .../src/commands/staff/bot-profile/modal.ts | 66 +++++-------------- .../command.ts | 11 +--- .../component.ts | 8 +-- .../helpers.ts | 34 ++++++---- .../configuration-automatic-threads/modal.ts | 16 ++--- .../subcommand.ts | 3 +- .../configuration-user-forums/command.ts | 11 +--- .../configuration-user-forums/component.ts | 12 ++-- .../configuration-user-forums/helpers.ts | 23 +++++-- .../staff/configuration-user-forums/modal.ts | 18 ++--- .../configuration-user-forums/subcommand.ts | 3 +- .../src/commands/thread-ticketing/panel.ts | 43 ++++++------ 13 files changed, 104 insertions(+), 152 deletions(-) diff --git a/apps/bot/src/commands/staff/bot-profile/component.ts b/apps/bot/src/commands/staff/bot-profile/component.ts index 3756ad4..8dd17c4 100644 --- a/apps/bot/src/commands/staff/bot-profile/component.ts +++ b/apps/bot/src/commands/staff/bot-profile/component.ts @@ -19,7 +19,7 @@ export default class extends Component.Interaction { case 'name': { return interaction.showModal( new ModalBuilder() - .setCustomId(customId('bot_profile_menu', 'name')) + .setCustomId(customId('bot_profile_menu_name')) .setTitle(guildTranslations.name.input.title()) .setLabelComponents( new LabelBuilder() @@ -40,7 +40,7 @@ export default class extends Component.Interaction { case 'bio': { return interaction.showModal( new ModalBuilder() - .setCustomId(customId('bot_profile_menu', 'bio')) + .setCustomId(customId('bot_profile_menu_bio')) .setTitle(guildTranslations.bio.input.title()) .setLabelComponents( new LabelBuilder() @@ -60,7 +60,7 @@ export default class extends Component.Interaction { case 'avatar': { return interaction.showModal( new ModalBuilder() - .setCustomId(customId('bot_profile_menu', 'avatar')) + .setCustomId(customId('bot_profile_menu_avatar')) .setTitle(guildTranslations.avatar.input.title()) .setLabelComponents( new LabelBuilder() @@ -79,7 +79,7 @@ export default class extends Component.Interaction { case 'banner': { return interaction.showModal( new ModalBuilder() - .setCustomId(customId('bot_profile_menu', 'banner')) + .setCustomId(customId('bot_profile_menu_banner')) .setTitle(guildTranslations.banner.input.title()) .setLabelComponents( new LabelBuilder() diff --git a/apps/bot/src/commands/staff/bot-profile/modal.ts b/apps/bot/src/commands/staff/bot-profile/modal.ts index 8f2a482..b8056bf 100644 --- a/apps/bot/src/commands/staff/bot-profile/modal.ts +++ b/apps/bot/src/commands/staff/bot-profile/modal.ts @@ -1,11 +1,4 @@ -import { - container, - DeferReply, - dynamicCustomId, - extractCustomId, - Modal, - userEmbedError, -} from '@ticketer/djs-framework'; +import { container, customId, DeferReply, Modal, userEmbedError } from '@ticketer/djs-framework'; import { ContainerBuilder, codeBlock, @@ -22,44 +15,9 @@ import { prettifyError, z } from 'zod'; import { translate } from '@/i18n'; export default class extends Modal.Interaction { - public readonly customIds = [dynamicCustomId('bot_profile_menu')]; - - public async execute(context: Modal.Context) { - const { dynamicValue } = extractCustomId(context.interaction.customId, true); + public readonly customIds = [customId('bot_profile_menu_name')]; - switch (dynamicValue) { - case 'name': { - return this.name(context); - } - case 'bio': { - return this.bio(context); - } - case 'avatar': { - return this.avatar(context); - } - case 'banner': { - return this.banner(context); - } - default: { - const translations = translate(context.interaction.locale).commands['bot-profile'].command.modals.errors - .customId; - - return context.interaction.reply({ - embeds: [ - userEmbedError({ - client: context.interaction.client, - description: translations.description(), - member: context.interaction.member, - title: translations.title(), - }), - ], - flags: [MessageFlags.Ephemeral], - }); - } - } - } - - private async name({ interaction }: Modal.Context) { + public async execute({ interaction }: Modal.Context) { const translations = translate(interaction.locale).commands['bot-profile'].command.modals.name.response.errors; const { data: nick, @@ -126,8 +84,12 @@ export default class extends Modal.Interaction { flags: [MessageFlags.IsComponentsV2], }); } +} + +export class Bio extends Modal.Interaction { + public readonly customIds = [customId('bot_profile_menu_bio')]; - private async bio({ interaction }: Modal.Context) { + public async execute({ interaction }: Modal.Context) { const translations = translate(interaction.locale).commands['bot-profile'].command.modals.bio.response.errors; const { data: bio, @@ -175,9 +137,13 @@ export default class extends Modal.Interaction { flags: [MessageFlags.IsComponentsV2], }); } +} + +export class Avatar extends Modal.Interaction { + public readonly customIds = [customId('bot_profile_menu_avatar')]; @DeferReply() - private async avatar({ interaction }: Modal.Context) { + public async execute({ interaction }: Modal.Context) { const avatar = interaction.fields.getUploadedFiles('avatar', false)?.at(0)?.url ?? ''; let me = await interaction.guild.members.fetchMe(); const oldAvatar = me.displayAvatarURL(); @@ -226,9 +192,13 @@ export default class extends Modal.Interaction { flags: [MessageFlags.IsComponentsV2], }); } +} + +export class Banner extends Modal.Interaction { + public readonly customIds = [customId('bot_profile_menu_banner')]; @DeferReply() - private async banner({ interaction }: Modal.Context) { + public async execute({ interaction }: Modal.Context) { const banner = interaction.fields.getUploadedFiles('banner', false)?.at(0)?.url ?? ''; // Force fetch due to caching issues. let me = await interaction.guild.members.fetchMe({ force: true }); diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/command.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/command.ts index 658f9fa..1d09892 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/command.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/command.ts @@ -11,16 +11,7 @@ export default class extends Command.Interaction { subcommand.setName('overview').setDescription('View the current configurations for automatic threads.'), ) .addSubcommand((subcommand) => - subcommand - .setName('create') - .setDescription('Create a new configuration for automatic threads.') - .addChannelOption((option) => - option - .setName('channel') - .setDescription('The text channel where the bot creates a thread for the user.') - .addChannelTypes(ChannelType.GuildText) - .setRequired(true), - ), + subcommand.setName('create').setDescription('Create a new configuration for automatic threads.'), ) .addSubcommand((subcommand) => subcommand diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts index 9adc76d..30eac60 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts @@ -57,7 +57,7 @@ export default class extends Component.Interaction { private async openingMessage(context: Component.Context<'string'>) { const { dynamicValue } = extractCustomId(context.interaction.customId, true); const { - data: id, + data: channelId, error, success, } = automaticThreadsConfigurationsSelectSchema.shape.channelId.safeParse(dynamicValue); @@ -85,7 +85,7 @@ export default class extends Component.Interaction { .from(automaticThreadsConfigurations) .where( and( - eq(automaticThreadsConfigurations.channelId, id), + eq(automaticThreadsConfigurations.channelId, channelId), eq(automaticThreadsConfigurations.guildId, context.interaction.guildId), ), ); @@ -105,9 +105,7 @@ export default class extends Component.Interaction { .catch(() => false); } - const { description, title } = row; - - void openingMessageModal(context, { description, id, title }); + void openingMessageModal(context, { channelId, ...row }); } } diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/helpers.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/helpers.ts index 6847363..5388e34 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/helpers.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/helpers.ts @@ -1,7 +1,8 @@ import { automaticThreadsConfigurations, database, desc, eq } from '@ticketer/database'; -import { type Command, type Component, container, customId, userEmbedError } from '@ticketer/djs-framework'; +import { type Component, container, customId, type Subcommand, userEmbedError } from '@ticketer/djs-framework'; import { bold, + ChannelSelectMenuBuilder, ChannelType, channelMention, HeadingLevel, @@ -21,7 +22,7 @@ import { automaticThreadsContainer, messageWithPagination, withPagination } from export function IsTextChannel(_: object, __: string, descriptor: PropertyDescriptor) { const original = descriptor.value as () => void; - descriptor.value = function (this: Command.Interaction, { interaction }: Command.Context<'chat'>) { + descriptor.value = function (this: Subcommand.Interaction, { interaction }: Subcommand.Context) { const { type } = interaction.options.getChannel('channel', true); if (type !== ChannelType.GuildText) { @@ -43,10 +44,7 @@ export function IsTextChannel(_: object, __: string, descriptor: PropertyDescrip return descriptor; } -export async function getConfigurations( - { interaction }: Command.Context<'chat'> | Component.Context<'button'>, - page = 0, -) { +export async function getConfigurations({ interaction }: Subcommand.Context | Component.Context<'button'>, page = 0) { const PAGE_SIZE = 3; const configurations = await withPagination({ page, @@ -96,14 +94,26 @@ export async function getConfigurations( } export async function openingMessageModal( - { interaction }: Command.Context<'chat'> | Component.Context<'string'>, - options: { id: string; title?: string; description?: string }, + { interaction }: Subcommand.Context | Component.Context<'string'>, + { channelId, description, title }: { channelId?: string; title?: string; description?: string }, ) { + const channelInput = new LabelBuilder() + .setLabel('Channel') + .setDescription("The text channel where the bot creates a thread from the user's message.") + .setChannelSelectMenuComponent( + // TODO: make this disabled in presence of a preset channel when Discord allows so. + (channelId ? new ChannelSelectMenuBuilder().setDefaultChannels(channelId) : new ChannelSelectMenuBuilder()) + .setCustomId(customId('channel')) + .setRequired(true) + .setMinValues(1) + .setMaxValues(1) + .setChannelTypes(ChannelType.GuildText), + ); const titleInput = new LabelBuilder() .setLabel('Message Title') .setDescription('Write "{member}" to mention the user.') .setTextInputComponent( - (options.title ? new TextInputBuilder().setValue(options.title) : new TextInputBuilder()) + (title ? new TextInputBuilder().setValue(title) : new TextInputBuilder()) .setCustomId(customId('title')) .setRequired(true) .setMinLength(1) @@ -115,7 +125,7 @@ export async function openingMessageModal( .setLabel('Message Description') .setDescription('Write "{member}" to mention the user.') .setTextInputComponent( - (options.description ? new TextInputBuilder().setValue(options.description) : new TextInputBuilder()) + (description ? new TextInputBuilder().setValue(description) : new TextInputBuilder()) .setCustomId(customId('description')) .setRequired(true) .setMinLength(1) @@ -124,9 +134,9 @@ export async function openingMessageModal( ); const modal = new ModalBuilder() - .setCustomId(customId('ticket_automatic_threads_configuration_opening_message', options.id)) + .setCustomId(customId('ticket_automatic_threads_configuration_opening_message')) .setTitle('Opening Message Title & Description') - .setLabelComponents(titleInput, descriptionInput); + .setLabelComponents([channelInput, titleInput, descriptionInput].filter((input) => !!input)); return interaction.showModal(modal).catch(() => false); } diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts index b03ccba..23d1849 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts @@ -3,25 +3,17 @@ import { automaticThreadsConfigurationsInsertSchema, database, } from '@ticketer/database'; -import { - container, - DeferReply, - dynamicCustomId, - extractCustomId, - Modal, - userEmbedError, -} from '@ticketer/djs-framework'; +import { container, customId, DeferReply, Modal, userEmbedError } from '@ticketer/djs-framework'; import { ChannelType, HeadingLevel, heading, MessageFlags, TextDisplayBuilder } from 'discord.js'; import { prettifyError } from 'zod'; -import { automaticThreadsContainer, fetchChannel } from '@/utils'; +import { automaticThreadsContainer } from '@/utils'; export class ModalInteraction extends Modal.Interaction { - public readonly customIds = [dynamicCustomId('ticket_automatic_threads_configuration_opening_message')]; + public readonly customIds = [customId('ticket_automatic_threads_configuration_opening_message')]; @DeferReply() public async execute({ interaction }: Modal.Context) { - const { dynamicValue } = extractCustomId(interaction.customId, true); - const channel = await fetchChannel(interaction.guild, dynamicValue); + const channel = interaction.fields.getSelectedChannels('channel', true).at(0); if (channel?.type !== ChannelType.GuildText) { return interaction.editReply({ diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/subcommand.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/subcommand.ts index 5837ac3..ee3b041 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/subcommand.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/subcommand.ts @@ -21,9 +21,8 @@ export class Create extends Subcommand.Interaction { subcommandNames: ['create'], }); - @IsTextChannel public execute(context: Subcommand.Context) { - void openingMessageModal(context, { id: context.interaction.options.getChannel('channel', true).id }); + void openingMessageModal(context, {}); } } diff --git a/apps/bot/src/commands/staff/configuration-user-forums/command.ts b/apps/bot/src/commands/staff/configuration-user-forums/command.ts index 50d11ed..441f1a9 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/command.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/command.ts @@ -11,16 +11,7 @@ export default class extends Command.Interaction { subcommand.setName('overview').setDescription('View the current configurations for user forum threads.'), ) .addSubcommand((subcommand) => - subcommand - .setName('create') - .setDescription('Create a new configuration for user forums assisted by the bot.') - .addChannelOption((option) => - option - .setName('channel') - .setDescription('The forum channel where the bot assists with support for the user.') - .addChannelTypes(ChannelType.GuildForum) - .setRequired(true), - ), + subcommand.setName('create').setDescription('Create a new configuration for user forums assisted by the bot.'), ) .addSubcommand((subcommand) => subcommand diff --git a/apps/bot/src/commands/staff/configuration-user-forums/component.ts b/apps/bot/src/commands/staff/configuration-user-forums/component.ts index 3dbd2c1..b81a764 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/component.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/component.ts @@ -51,7 +51,11 @@ export default class extends Component.Interaction { private async openingMessage(context: Component.Context<'string'>) { const { dynamicValue } = extractCustomId(context.interaction.customId, true); - const { data: id, error, success } = userForumsConfigurationsSelectSchema.shape.channelId.safeParse(dynamicValue); + const { + data: channelId, + error, + success, + } = userForumsConfigurationsSelectSchema.shape.channelId.safeParse(dynamicValue); if (!success) { return context.interaction @@ -76,7 +80,7 @@ export default class extends Component.Interaction { .from(userForumsConfigurations) .where( and( - eq(userForumsConfigurations.channelId, id), + eq(userForumsConfigurations.channelId, channelId), eq(userForumsConfigurations.guildId, context.interaction.guildId), ), ); @@ -96,9 +100,7 @@ export default class extends Component.Interaction { .catch(() => false); } - const { description, title } = row; - - void openingMessageModal(context, { description, id, title }); + void openingMessageModal(context, { channelId, ...row }); } } diff --git a/apps/bot/src/commands/staff/configuration-user-forums/helpers.ts b/apps/bot/src/commands/staff/configuration-user-forums/helpers.ts index 311a08f..ccb8e89 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/helpers.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/helpers.ts @@ -2,6 +2,7 @@ import { database, desc, eq, userForumsConfigurations } from '@ticketer/database import { type Command, type Component, container, customId, userEmbedError } from '@ticketer/djs-framework'; import { bold, + ChannelSelectMenuBuilder, ChannelType, channelMention, HeadingLevel, @@ -97,13 +98,25 @@ export async function getConfigurations( export async function openingMessageModal( { interaction }: Command.Context<'chat'> | Component.Context<'string'>, - options: { id: string; title?: string; description?: string }, + { channelId, description, title }: { channelId?: string; title?: string; description?: string }, ) { + const channelInput = new LabelBuilder() + .setLabel('Channel') + .setDescription('The forum channel where the bot sends the message below to the user.') + .setChannelSelectMenuComponent( + // TODO: make this disabled in presence of a preset channel when Discord allows so. + (channelId ? new ChannelSelectMenuBuilder().setDefaultChannels(channelId) : new ChannelSelectMenuBuilder()) + .setCustomId(customId('channel')) + .setRequired(true) + .setMinValues(1) + .setMaxValues(1) + .setChannelTypes(ChannelType.GuildForum), + ); const titleInput = new LabelBuilder() .setLabel('Message Title') .setDescription('Write "{member}" to mention the user.') .setTextInputComponent( - (options.title ? new TextInputBuilder().setValue(options.title) : new TextInputBuilder()) + (title ? new TextInputBuilder().setValue(title) : new TextInputBuilder()) .setCustomId(customId('title')) .setRequired(true) .setMinLength(1) @@ -114,7 +127,7 @@ export async function openingMessageModal( .setLabel('Message Description') .setDescription('Write "{member}" to mention the user.') .setTextInputComponent( - (options.description ? new TextInputBuilder().setValue(options.description) : new TextInputBuilder()) + (description ? new TextInputBuilder().setValue(description) : new TextInputBuilder()) .setCustomId(customId('description')) .setRequired(true) .setMinLength(1) @@ -123,9 +136,9 @@ export async function openingMessageModal( ); const modal = new ModalBuilder() - .setCustomId(customId('ticket_user_forums_configuration_opening_message', options.id)) + .setCustomId(customId('ticket_user_forums_configuration_opening_message')) .setTitle('Opening Message Title & Description') - .setLabelComponents(titleInput, descriptionInput); + .setLabelComponents([channelInput, titleInput, descriptionInput].filter((input) => !!input)); return interaction.showModal(modal).catch(() => false); } diff --git a/apps/bot/src/commands/staff/configuration-user-forums/modal.ts b/apps/bot/src/commands/staff/configuration-user-forums/modal.ts index e0b6c05..6180a90 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/modal.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/modal.ts @@ -1,23 +1,15 @@ import { database, userForumsConfigurations, userForumsConfigurationsInsertSchema } from '@ticketer/database'; -import { - container, - DeferReply, - dynamicCustomId, - extractCustomId, - Modal, - userEmbedError, -} from '@ticketer/djs-framework'; +import { container, customId, DeferReply, Modal, userEmbedError } from '@ticketer/djs-framework'; import { ChannelType, HeadingLevel, heading, MessageFlags, TextDisplayBuilder } from 'discord.js'; import { prettifyError } from 'zod'; -import { fetchChannel, userForumsContainer } from '@/utils'; +import { userForumsContainer } from '@/utils'; export class ModalInteraction extends Modal.Interaction { - public readonly customIds = [dynamicCustomId('ticket_user_forums_configuration_opening_message')]; + public readonly customIds = [customId('ticket_user_forums_configuration_opening_message')]; @DeferReply() public async execute({ interaction }: Modal.Context) { - const { dynamicValue } = extractCustomId(interaction.customId, true); - const channel = await fetchChannel(interaction.guild, dynamicValue); + const channel = interaction.fields.getSelectedChannels('channel', true).at(0); if (channel?.type !== ChannelType.GuildForum) { return interaction.editReply({ @@ -49,7 +41,7 @@ export class ModalInteraction extends Modal.Interaction { await database .insert(userForumsConfigurations) .values({ - channelId: dynamicValue, + channelId: channel.id, guildId: interaction.guildId, openingMessageTitle: data.openingMessageTitle, openingMessageDescription: data.openingMessageDescription, diff --git a/apps/bot/src/commands/staff/configuration-user-forums/subcommand.ts b/apps/bot/src/commands/staff/configuration-user-forums/subcommand.ts index d28f9f7..4b75ea2 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/subcommand.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/subcommand.ts @@ -21,9 +21,8 @@ export class Create extends Subcommand.Interaction { subcommandNames: ['create'], }); - @IsForumChannel public execute(context: Subcommand.Context) { - void openingMessageModal(context, { id: context.interaction.options.getChannel('channel', true).id }); + void openingMessageModal(context, {}); } } diff --git a/apps/bot/src/commands/thread-ticketing/panel.ts b/apps/bot/src/commands/thread-ticketing/panel.ts index fbfd1eb..c29fd21 100644 --- a/apps/bot/src/commands/thread-ticketing/panel.ts +++ b/apps/bot/src/commands/thread-ticketing/panel.ts @@ -1,17 +1,8 @@ -import { - Command, - container, - customId, - DeferReply, - dynamicCustomId, - extractCustomId, - Modal, - userEmbed, - userEmbedError, -} from '@ticketer/djs-framework'; +import { Command, container, customId, DeferReply, Modal, userEmbed, userEmbedError } from '@ticketer/djs-framework'; import { ButtonBuilder, ButtonStyle, + ChannelSelectMenuBuilder, Colors, HeadingLevel, heading, @@ -25,19 +16,24 @@ import { TextInputStyle, } from 'discord.js'; import { prettifyError, z } from 'zod'; -import { extractEmoji, fetchChannel } from '@/utils'; +import { extractEmoji } from '@/utils'; export default class extends Command.Interaction { public readonly data = super.SlashBuilder.setName('panel') .setDescription('Create a ticket panel in a specific channel.') - .setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels | PermissionFlagsBits.ManageThreads) - .addChannelOption((option) => - option.setName('channel').setDescription('Choose the channel where the panel goes to.').setRequired(true), - ); + .setDefaultMemberPermissions(PermissionFlagsBits.ManageChannels | PermissionFlagsBits.ManageThreads); public execute({ interaction }: Command.Context<'chat'>) { - const channel = interaction.options.getChannel('channel', true); - + const channelInput = new LabelBuilder() + .setLabel('Channel') + .setDescription('Choose the channel where the panel will be displayed.') + .setChannelSelectMenuComponent( + new ChannelSelectMenuBuilder() + .setCustomId(customId('channel')) + .setRequired(true) + .setMinValues(1) + .setMaxValues(1), + ); const titleInput = new LabelBuilder() .setLabel('Title') .setDescription('Write a title to be used in the ticket panel.') @@ -84,20 +80,20 @@ export default class extends Command.Interaction { ); const modal = new ModalBuilder() - .setCustomId(customId('ticket_threads_categories_create_panel', channel.id)) + .setCustomId(customId('ticket_threads_categories_create_panel')) .setTitle('Ticket Panel Details') - .setLabelComponents(titleInput, descriptonInput, buttonEmojiInput, buttonLabelInput); + .setLabelComponents(channelInput, titleInput, descriptonInput, buttonEmojiInput, buttonLabelInput); return interaction.showModal(modal); } } export class ModalInteraction extends Modal.Interaction { - public readonly customIds = [dynamicCustomId('ticket_threads_categories_create_panel')]; + public readonly customIds = [customId('ticket_threads_categories_create_panel')]; @DeferReply() public async execute({ interaction }: Modal.Context) { - const { customId: interactionId, fields, guild } = interaction; + const { fields, guild } = interaction; const rawButtonEmoji = fields.getTextInputValue('button_emoji'); const buttonEmoji = extractEmoji(rawButtonEmoji) ?? '🎫'; @@ -127,8 +123,7 @@ export class ModalInteraction extends Modal.Interaction { }); } - const { dynamicValue: channelId } = extractCustomId(interactionId, true); - const channel = await fetchChannel(guild, channelId); + const channel = fields.getSelectedChannels('channel', true).at(0); if (!channel?.isTextBased()) { return interaction.editReply({