import { type ButtonInteraction, type ChatInputCommandInteraction, type Client, type ModalSubmitInteraction, SlashCommandBuilder, SlashCommandSubcommandBuilder, } from "discord.js"; import { parseComponentId } from "@/utils/components"; import { getGuild, getTicket } from "@/utils/queries"; import { getTicketReasonModal } from "./_shared"; import { handleClose, handleCreate, handleDelete, handleReopen, handleThank, handleTranscript, } from "./actions"; import { handleExport, handleList, handleListPagination, handleViewTicket, } from "./gdpr"; import { handleReasonModal } from "./modals"; const commandData = new SlashCommandBuilder() .setName("ticket") .setDescription("Manage and create new tickets") .addSubcommand( new SlashCommandSubcommandBuilder() .setName("create") .setDescription("Create a new ticket"), ) .addSubcommand( new SlashCommandSubcommandBuilder() .setName("close") .setDescription("Close the ticket in the current channel") .addStringOption((option) => option .setName("reason") .setDescription("Reason for closing the ticket, shown to the user") .setRequired(false), ) .addStringOption((option) => option .setName("private_reason") .setDescription( "Private reason for closing the ticket, shown only to staff", ) .setRequired(false), ), ) .addSubcommand( new SlashCommandSubcommandBuilder() .setName("reopen") .setDescription("Reopen the ticket in the current channel"), ) .addSubcommand( new SlashCommandSubcommandBuilder() .setName("delete") .setDescription("Delete the ticket in the current channel"), ) .addSubcommand( new SlashCommandSubcommandBuilder() .setName("transcript") .setDescription( "Get the transcript of the ticket in the current channel", ), ) .addSubcommand( new SlashCommandSubcommandBuilder() .setName("list") .setDescription("Lists all of your current and past tickets"), ) .addSubcommand( new SlashCommandSubcommandBuilder() .setName("view") .setDescription("View details about a specific ticket") .addIntegerOption((option) => option .setName("ticket_id") .setDescription("The ID of the ticket you want to view") .setRequired(true), ), ) .addSubcommand( new SlashCommandSubcommandBuilder() .setName("export") .setDescription( "Export all of your ticket data in a machine readable format", ), ); async function execute( client: Client, interaction: ChatInputCommandInteraction, ) { const action = interaction.options.getSubcommand(); if (!interaction.channelId || !interaction.guildId) { await interaction.reply({ content: "This command can only be used in a server.", flags: ["Ephemeral"], }); return; } const actions = { create: handleCreate, list: handleList, export: handleExport, view: handleViewTicket, }; if (action in actions) { await actions[action as keyof typeof actions](client, interaction); return; } const { data: guild } = await getGuild(interaction.guildId); const { data: ticket } = await getTicket( interaction.guildId, interaction.channelId, ); if (!ticket) { await interaction.reply({ content: "❌ This channel is not a valid ticket.", }); return; } if (action === "close") { const publicReason = interaction.options.getString("reason") ?? undefined; const privateReason = interaction.options.getString("private_reason") ?? undefined; if (publicReason || privateReason) { await handleClose(client, interaction, ticket, guild, { publicReason, privateReason, }); return; } return await interaction.showModal( getTicketReasonModal(interaction.user.id !== ticket.authorId), ); } await interaction.deferReply({ flags: ["Ephemeral"] }); if (action === "reopen") await handleReopen(client, interaction, ticket); else if (action === "delete") await handleDelete(client, interaction, ticket); else if (action === "transcript") await handleTranscript(client, interaction, ticket); else await interaction.editReply("❌ Invalid or unhandled action."); } async function buttonExecute(client: Client, interaction: ButtonInteraction) { const [action] = interaction.customId.split(":").slice(1); if (action === "thank") { await handleThank(client, interaction); return; } if (!interaction.channelId || !interaction.guildId) { await interaction.reply({ content: "This command can only be used in a server.", }); return; } if (action === "list") { await handleListPagination(client, interaction); return; } if (action === "create") { await handleCreate(client, interaction); return; } const { data: ticket } = await getTicket( interaction.guildId, interaction.channelId, ); if (!ticket) { await interaction.reply({ content: "❌ This channel is not a valid ticket.", }); return; } if (action === "close") return await interaction.showModal( getTicketReasonModal(interaction.user.id !== ticket.authorId), ); await interaction.deferReply({ flags: ["Ephemeral"] }); if (action === "reopen") await handleReopen(client, interaction, ticket); else if (action === "delete") await handleDelete(client, interaction, ticket); else if (action === "transcript") await handleTranscript(client, interaction, ticket); else await interaction.editReply("❌ Invalid or unhandled action."); } async function modalExecute( client: Client, interaction: ModalSubmitInteraction, ) { const components = parseComponentId(interaction.customId); const action = components.parts[0]; if (action === "reason") await handleReasonModal(client, interaction); } export default { data: commandData, execute, buttonExecute, modalExecute, };