import { ButtonBuilder, ButtonStyle } from "discord.js"; import { Result } from "@sapphire/framework"; import { option } from "@purrkit/router"; import { kitten } from "@/client"; import { errorMessage, handleError, PublicError } from "@/lib"; import * as actions from "./actions"; import { useGetTicketData } from "@/middleware"; import { TicketModalPresets } from "./modals"; export const newTicketButton = kitten.button("b-open-ticket", { run: async (interaction) => { if (!interaction.guild) return errorMessage(interaction, "You must run this command in a guild"); const func = actions.createTicket(interaction.member ?? interaction.user, interaction.guild); const ticketResult = await Result.fromAsync(async () => func); if (ticketResult.isErr()) { const error = ticketResult.unwrapErr(); if (error instanceof PublicError) { return errorMessage(interaction, error.message); } else { console.error(error); return errorMessage(interaction, "An unexpected error occurred while creating the ticket."); } } const { channelId } = ticketResult.unwrap(); return interaction.reply({ content: `Opened ticket <#${channelId}>.`, flags: ["Ephemeral"], }); }, }); // export const closeTicketButton = useGetTicketData.button("b-close-ticket", { options: { ticketId: option.string({ required: true }), }, async run(interaction) { if (!interaction.guild) return errorMessage(interaction, "You must run this command in a guild"); const modal = await actions.triggerCloseTicket( interaction.user, interaction.guild, interaction.channelId, ); await interaction.showModal(modal); }, }); export const reopenTicketButton = useGetTicketData.button("b-reopen-ticket", { options: { ticketId: option.string({ required: true }), }, async run(interaction, _, { actor, ticket, ticketChannel, guild }) { if (!ticket || !ticketChannel || !actor || !guild) return errorMessage(interaction, "Failed to find ticket"); const result = await Result.fromAsync( actions.reopenTicket({ ticket, ticketChannel, actor, guild }), ); if (result.isErr()) return handleError(interaction, result.unwrapErr()); await interaction.reply({ content: `Ticket has been reopened.`, flags: ["Ephemeral"], }); }, }); // export const claimTicketButton = useGetTicketData.button("b-claim-ticket", { options: { ticketId: option.string({ required: true }), }, async run(interaction, _, ctx) { const { actor, guild, ticket, ticketChannel, user } = ctx; if (!actor || !guild || !ticket || !ticketChannel || !user) { await errorMessage(interaction, "Invalid ticket"); return; } const result = await Result.fromAsync( actions.claimTicket({ ticket, ticketChannel, actor, guild }), ); if (result.isErr()) return handleError(interaction, result.unwrapErr()); await interaction.reply({ content: "Ticket claimed!", flags: ["Ephemeral"], }); }, }); export const unclaimTicketButton = useGetTicketData.button("b-unclaim-ticket", { options: { ticketId: option.string({ required: true }), }, async run(interaction, _, { actor, ticket, ticketChannel, guild }) { if (!ticket || !ticketChannel || !actor || !guild) return errorMessage(interaction, "Failed to find ticket"); const result = await Result.fromAsync( actions.unclaimTicket({ ticket, ticketChannel, actor, guild }), ); if (result.isErr()) return handleError(interaction, result.unwrapErr()); await interaction.reply({ content: `You have unclaimed ticket #${ticket.localId}.`, flags: ["Ephemeral"], }); }, }); // export const commentOnTicketButton = useGetTicketData.button("b-comment-ticket", { options: { ticketId: option.string({ required: true }), }, async run(interaction, _, { actor, ticket, ticketChannel, guild }) { if (!ticket || !ticketChannel || !actor || !guild) return errorMessage(interaction, "Failed to find ticket"); if (!actor.moderator) return errorMessage(interaction, "You must be a moderator to use this command"); await interaction.showModal(TicketModalPresets.comment(ticket.id)); }, }); export const viewCommentsButton = kitten.button("b-comment-ticket", { options: { ticketId: option.string({ required: true }), }, async run() {}, }); // export const editTicketButton = useGetTicketData.button("b-edit-ticket", { options: { ticketId: option.string({ required: true }), }, async run(interaction, _, { actor, ticket }) { if (!actor) { return errorMessage(interaction, "This command can only be used in a server."); } else if (!actor.moderator) { return errorMessage(interaction, "You do not have permission to use this command."); } else if (!ticket) { return errorMessage(interaction, "Ticket not found."); } await interaction.showModal( TicketModalPresets.edit(ticket.id, { public: ticket.closeReason, private: ticket.privateReason, }), ); }, }); export const cleanTicketButton = kitten.button("b-delete-ticket", { options: { ticketId: option.string({ required: true }), }, async run() {}, }); export const thankButton = kitten.button("b-thank-ticket", { options: { ticketId: option.string({ required: true }), }, async run() {}, }); export const TicketButtonsMap = { newTicketButton, closeTicketButton, reopenTicketButton, claimTicketButton, unclaimTicketButton, commentOnTicketButton, viewCommentsButton, editTicketButton, cleanTicketButton, thankButton, } as const; export const TicketButtons = Object.values(TicketButtonsMap); export type TicketButtonKeys = keyof typeof TicketButtonsMap | "transcriptsLink" | "uploadLink"; export const TicketButtonPresets: Record ButtonBuilder> = { newTicketButton: () => new ButtonBuilder() .setLabel("Open a Ticket") .setStyle(ButtonStyle.Success) .setCustomId(newTicketButton.id()), // closeTicketButton: (id: string) => new ButtonBuilder() .setCustomId(closeTicketButton.id({ ticketId: id })) .setLabel("Close Ticket") .setStyle(ButtonStyle.Danger), reopenTicketButton: (id: string) => new ButtonBuilder() .setCustomId(reopenTicketButton.id({ ticketId: id })) .setLabel("Reopen Ticket") .setStyle(ButtonStyle.Primary), // claimTicketButton: (id: string) => new ButtonBuilder() .setCustomId(claimTicketButton.id({ ticketId: id })) .setLabel("Claim Ticket") .setStyle(ButtonStyle.Primary), unclaimTicketButton: (id: string) => new ButtonBuilder() .setCustomId(unclaimTicketButton.id({ ticketId: id })) .setLabel("Unclaim Ticket") .setStyle(ButtonStyle.Secondary), // commentOnTicketButton: (id: string) => new ButtonBuilder() .setCustomId(commentOnTicketButton.id({ ticketId: id })) .setLabel(`Add Comment`) .setStyle(ButtonStyle.Secondary), viewCommentsButton: (id: string, quantity: number) => new ButtonBuilder() .setCustomId(viewCommentsButton.id({ ticketId: id })) .setLabel(`View Comments (≥${quantity})`) .setStyle(ButtonStyle.Secondary), // editTicketButton: (id: string) => new ButtonBuilder() .setCustomId(editTicketButton.id({ ticketId: id })) .setLabel("Edit Ticket") .setStyle(ButtonStyle.Primary), // cleanTicketButton: (id: string) => new ButtonBuilder() .setCustomId(cleanTicketButton.id({ ticketId: id })) .setLabel("Delete Channel") .setStyle(ButtonStyle.Danger), // transcriptsLink: (id: string) => new ButtonBuilder() .setLabel("View on Web") .setURL(`https://tickets.vt3e.cat/tickets/${id}`) .setStyle(ButtonStyle.Link), uploadLink: (id: string) => new ButtonBuilder() .setLabel("Upload a video") .setStyle(ButtonStyle.Link) .setURL(`https://tickets.vt3e.cat/upload/${id}`), thankButton: (id: string) => new ButtonBuilder() .setCustomId(thankButton.id({ ticketId: id })) .setLabel("Thank your moderator") .setEmoji("💖") .setStyle(ButtonStyle.Success), }; export default TicketButtonPresets;