import { ActionRowBuilder, ButtonBuilder, ButtonStyle, type ChatInputCommandInteraction, type Client, ContainerBuilder, type GuildMember, LabelBuilder, ModalBuilder, type ModalSubmitInteraction, SlashCommandBuilder, TextDisplayBuilder, TextInputBuilder, TextInputStyle, } from "discord.js"; import { makeComponentId } from "@/utils/components"; import { formatMessage } from "@/utils/formatting"; import { loggers } from "@/utils/logging"; import { hasManagerPermissions } from "@/utils/permissions"; import { getGuild } from "@/utils/queries"; const logger = loggers.interactions.child({ name: "messageCommand" }); const commandData = new SlashCommandBuilder() .setName("ticket-message") .setDescription("Send the ticket message in the designated channel") .addChannelOption((c) => c .setName("channel") .setDescription("The channel to send the ticket message in") .setRequired(true), ); const MODAL_ID = makeComponentId(commandData, "message"); const INPUT_ID = "message_content"; async function execute( _client: Client, interaction: ChatInputCommandInteraction, ) { if (!interaction.guild || !interaction.member) { await interaction.reply({ content: "❌ This command can only be used in a server.", flags: ["Ephemeral"], }); return; } const hasPerms = await hasManagerPermissions( interaction.member as GuildMember, ); if (!hasPerms) { await interaction.reply({ content: "❌ You don't have permission to use this command.", flags: ["Ephemeral"], }); return; } const targetChannel = interaction.options.getChannel("channel", true); // construct & show the modal const textInput = new LabelBuilder() .setLabel("Message Content") .setTextInputComponent( new TextInputBuilder() .setCustomId(INPUT_ID) .setStyle(TextInputStyle.Paragraph) .setRequired(true), ); await interaction.showModal( new ModalBuilder() .setCustomId(`${MODAL_ID}:${targetChannel.id}`) .setTitle("Ticket Message") .setLabelComponents(textInput), ); } async function modalExecute( _client: Client, interaction: ModalSubmitInteraction, ) { if (!interaction.guild || !interaction.member) return; const channelId = interaction.customId.split(":")[2]; const { data: guildData } = await getGuild(interaction.guild.id); const channel = interaction.guild.channels.cache.get(channelId); if (!channel) { await interaction.reply({ content: "❌ The channel specified is invalid.", flags: ["Ephemeral"], }); return; } if (!channel.isSendable()) { await interaction.reply({ content: "❌ I'm not able to send messages in the specified channel.", flags: ["Ephemeral"], }); return; } const message = interaction.fields.getTextInputValue(INPUT_ID); const formatted = formatMessage(message, interaction); const controls = new ActionRowBuilder().addComponents( new ButtonBuilder() .setCustomId("ticket:create") .setLabel("Open a Ticket") .setEmoji("📩") .setStyle(ButtonStyle.Success), ); const container = new ContainerBuilder() .addTextDisplayComponents(new TextDisplayBuilder().setContent(formatted)) .addActionRowComponents(controls); const sent = await channel .send({ components: [container], flags: ["IsComponentsV2"] }) .catch((err) => { logger.error({ err, channelId }, "Failed to send ticket message"); return null; }); if (!sent) { await interaction.reply({ content: "❌ Failed to send the message in the specified channel.", flags: ["Ephemeral"], }); return; } await interaction.reply({ content: `✅ Message sent successfully! ${sent.url}`, flags: ["Ephemeral"], allowedMentions: { users: [], }, }); } export default { data: commandData, execute, modalExecute, };