all repos — stealth-developers @ 772a28dcd81f6f2195c77ef1b6dbfd304277ca82

bot: extract ticket closing login into action
vi did:web:vt3e.cat
Tue, 23 Jun 2026 20:24:42 +0100
commit

772a28dcd81f6f2195c77ef1b6dbfd304277ca82

parent

c48217d1371dc5f4f190db6987bb480b524fff83

M apps/bot/src/commands/tickets/actions.tsapps/bot/src/commands/tickets/actions.ts

@@ -11,10 +11,12 @@ import { Result } from "@sapphire/framework";

import db, { desc, + editTicket, eq, getGuildByDiscordId, getTicketByChannelId, tickets, + type Actor, } from "@stealth-developers/db"; export * from "./config";

@@ -100,7 +102,8 @@ createdChannel = channel;

const greeting = dbGuild.ticketGreeting; if (greeting) { - const container = new ContainerBuilder() + const greetingContainer = new ContainerBuilder() + .setAccentColor(0x3a9027) .addTextDisplayComponents( new TextDisplayBuilder({ content: greeting.replaceAll("{user}", `<@${subjectUser.discordId}>`),

@@ -113,7 +116,28 @@ TicketButtonPresets.close(publicId),

), ); - channel.send({ components: [container], flags: ["IsComponentsV2"] }); + const uploadContainer = new ContainerBuilder() + .setAccentColor(0x3a9027) + .addTextDisplayComponents( + new TextDisplayBuilder({ + content: [ + "# Uploading Videos\n", + "You can either upload your video directly to this channel, or", + "you can use the button below to upload it directly to us, which", + "allows you to upload videos up to 2GB in size.", + ].join(" "), + }), + ) + .addActionRowComponents( + new ActionRowBuilder<ButtonBuilder>().addComponents( + TicketButtonPresets.upload(publicId), + ), + ); + + channel.send({ + components: [greetingContainer, uploadContainer], + flags: ["IsComponentsV2"], + }); } await tx.update(tickets).set({ channelId: channel.id }).where(eq(tickets.id, ticket.id));

@@ -151,3 +175,20 @@ if (!ticket) throw new PublicError("Failed to close ticket: Ticket not found in database");

return TicketModalPresets.close(ticket.id, Boolean(dbUser.moderator)); } + +export async function closeTicket( + user: Actor, + ticketId: string, + reasons: { public: string; private?: string | null }, +) { + const result = await editTicket(ticketId, { + closedAt: new Date(), + closeReason: reasons.public, + privateReason: reasons.private, + closedBy: user.id, + status: "closed", + }); + + if (!result) throw new PublicError("Failed to close ticket: Ticket not found in database"); + return result; +}
M apps/bot/src/components/buttons/tickets/index.tsapps/bot/src/components/buttons/tickets/index.ts

@@ -6,7 +6,7 @@ import { kitten } from "@/client";

import { errorMessage, PublicError } from "@/lib"; import * as actions from "@/commands/tickets/actions"; -export const closeTicketButton = kitten.button("close-ticket", { +export const closeTicketButton = kitten.button("b-close-ticket", { options: { ticketId: option.string({ required: true }), },

@@ -100,6 +100,11 @@ new ButtonBuilder()

.setLabel("Open a Ticket") .setStyle(ButtonStyle.Success) .setCustomId(newTicketButton.id()), + upload: (id: string) => + new ButtonBuilder() + .setLabel("Upload a video") + .setStyle(ButtonStyle.Link) + .setURL(`https://tickets.vt3e.cat/upload/${id}`), }; export default TicketButtonPresets;
M apps/bot/src/components/modals/tickets/index.tsapps/bot/src/components/modals/tickets/index.ts

@@ -2,9 +2,10 @@ import { TextInputBuilder, LabelBuilder, ModalBuilder } from "discord.js";

import { TextInputStyle } from "discord-api-types/v9"; import { option } from "@purrkit/router"; -import { editTicket, getTicket } from "@stealth-developers/db"; +import { getTicket } from "@stealth-developers/db"; import { kitten } from "@/client"; import { errorMessage, upsertUser } from "@/lib"; +import * as actions from "@/commands/tickets/actions"; export const closeTicketModal = kitten.modal("close-ticket", { options: {

@@ -26,15 +27,7 @@ public: interaction.fields.getTextInputValue("reason:public"),

private: user.moderator ? interaction.fields.getTextInputValue("reason:private") : undefined, }; - const result = await editTicket(ticketId, { - closedAt: new Date(), - closeReason: reasons.public, - privateReason: reasons.private, - closedBy: user.id, - status: "closed", - }); - - if (!result) return errorMessage(interaction, "Failed to close ticket"); + await actions.closeTicket(user, ticket.id, reasons); return interaction.followUp({ content: "Ticket closed successfully" }); }, });