all repos — stealth-developers @ b204a979e4c0c34610e51010d9bb5d314fef3c3f

feat(tickets): rename closed tickets
vi did:web:vt3e.cat
Fri, 08 May 2026 18:32:33 +0100
commit

b204a979e4c0c34610e51010d9bb5d314fef3c3f

parent

0d4e04bebc7503441d3444c8fad995dde0d46a4a

1 files changed, 39 insertions(+), 119 deletions(-)

jump to
M src/interactions/commands/tickets/actions.tssrc/interactions/commands/tickets/actions.ts

@@ -1,10 +1,5 @@

import { type Guild, type Ticket, db, tickets } from "@/database"; -import { - getGuild, - getManagerRoleIds, - getTicket, - getTicketById, -} from "@/database/queries"; +import { getGuild, getManagerRoleIds, getTicket, getTicketById } from "@/database/queries"; import { text } from "@/utils/discord/components"; import { hasManagerPermissions } from "@/utils/discord/permissions"; import { loggers } from "@/utils/logging";

@@ -51,13 +46,10 @@ if (

interaction instanceof ChatInputCommandInteraction && interaction.options.getSubcommand() === "create-for" ) { - const isModerator = await hasManagerPermissions( - interaction.member as GuildMember, - ); + const isModerator = await hasManagerPermissions(interaction.member as GuildMember); if (!isModerator) { await interaction.reply({ - content: - "❌ You don't have permission to create tickets for other users.", + content: "❌ You don't have permission to create tickets for other users.", }); }

@@ -108,10 +100,7 @@ ],

}, { id: client.user.id, - allow: [ - PermissionFlagsBits.ViewChannel, - PermissionFlagsBits.ManageChannels, - ], + allow: [PermissionFlagsBits.ViewChannel, PermissionFlagsBits.ManageChannels], }, ...managerRoleIds.map((roleId) => ({ id: roleId,

@@ -145,10 +134,7 @@ targetUser ? targetUser.id : interaction.user.id,

); const controls = new ActionRowBuilder<ButtonBuilder>().addComponents( - new ButtonBuilder() - .setCustomId("ticket:claim") - .setLabel("Claim") - .setStyle(ButtonStyle.Primary), + new ButtonBuilder().setCustomId("ticket:claim").setLabel("Claim").setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId("ticket:close") .setLabel("Close")

@@ -189,8 +175,7 @@ });

const canDm = await canDmUser(targetUser ?? interaction.user); if (!canDm) { - const formatCommand = (name: string) => - `</ticket ${name}:1477532047785332906>`; + const formatCommand = (name: string) => `</ticket ${name}:1477532047785332906>`; const view = formatCommand("view"); const list = formatCommand("list");

@@ -226,15 +211,11 @@ ticket: Ticket,

) { const targetUser = interaction.options.getUser("user", true); - const isStaff = await hasManagerPermissions( - interaction.member as GuildMember, - ); + const isStaff = await hasManagerPermissions(interaction.member as GuildMember); const isAuthor = interaction.user.id === ticket.authorId; if (!isStaff && !isAuthor) { - await interaction.editReply( - "❌ You don't have permission to add users to this ticket.", - ); + await interaction.editReply("❌ You don't have permission to add users to this ticket."); return; }

@@ -248,13 +229,8 @@ SendMessages: true,

AttachFiles: true, }); - const updatedUsers = [ - ...new Set([...(ticket.addedUsers || []), targetUser.id]), - ]; - await db - .update(tickets) - .set({ addedUsers: updatedUsers }) - .where(eq(tickets.id, ticket.id)); + const updatedUsers = [...new Set([...(ticket.addedUsers || []), targetUser.id])]; + await db.update(tickets).set({ addedUsers: updatedUsers }).where(eq(tickets.id, ticket.id)); await channel.send({ content: `✅ <@${targetUser.id}> has been added to the ticket by <@${interaction.user.id}>.`,

@@ -262,13 +238,8 @@ });

await interaction.editReply("✅ User added successfully."); } catch (error) { - logger.error( - error, - `Failed to add user ${targetUser.id} to ticket ${ticket.id}`, - ); - await interaction.editReply( - "❌ Failed to add the user to the channel permissions.", - ); + logger.error(error, `Failed to add user ${targetUser.id} to ticket ${ticket.id}`); + await interaction.editReply("❌ Failed to add the user to the channel permissions."); } }

@@ -295,9 +266,7 @@ await interaction.editReply("❌ This ticket is already closed.");

return; } const closedByReporter = interaction.user.id === _ticket.authorId; - const isManager = await hasManagerPermissions( - interaction.member as GuildMember, - ); + const isManager = await hasManagerPermissions(interaction.member as GuildMember); const channel = interaction.channel; if (!channel || !("permissionOverwrites" in channel)) return;

@@ -315,10 +284,7 @@ closedBy: closedByReporter ? "reporter" : interaction.user.id,

}) .where(eq(tickets.id, _ticket.id)); - const { data: ticket, exists } = await getTicket( - interaction.guildId, - interaction.channelId, - ); + const { data: ticket, exists } = await getTicket(interaction.guildId, interaction.channelId); if (!exists || !ticket) { await interaction.editReply({

@@ -328,9 +294,7 @@ return;

} try { - const member = await interaction.guild?.members - .fetch(ticket.authorId) - .catch(() => null); + const member = await interaction.guild?.members.fetch(ticket.authorId).catch(() => null); const isAdmin = member?.permissions.has(PermissionFlagsBits.Administrator); const isOwner = interaction.guild?.ownerId === ticket.authorId;

@@ -384,10 +348,7 @@ try {

await fn(); return; } catch (error) { - logger.error( - error, - `Attempt ${attempt}/${maxRetries} failed for ${label}`, - ); + logger.error(error, `Attempt ${attempt}/${maxRetries} failed for ${label}`); if (attempt === maxRetries) { throw error; }

@@ -406,10 +367,7 @@ users: [],

}, }); } catch (error) { - if ( - error instanceof DiscordAPIError && - [50007, 50278].includes(error.code as number) - ) { + if (error instanceof DiscordAPIError && [50007, 50278].includes(error.code as number)) { channel.send(`Couldn't DM the reporter, they may have DMs disabled.`); } else { channel.send("Couldn't DM the reporter.");

@@ -429,8 +387,7 @@ `No transcript channel configured for guild ${interaction.guildId}, skipping transcript post`,

); return; } - const transcriptChannel = - await client.channels.fetch(transcriptChannelId); + const transcriptChannel = await client.channels.fetch(transcriptChannelId); if (!transcriptChannel || !("send" in transcriptChannel)) { logger.warn( `Transcript channel with ID ${transcriptChannelId} not found or is not a text channel`,

@@ -451,17 +408,13 @@ error,

`Could not send ticket transcript to transcript channel ${transcriptChannelId} for ticket ${ticket.id}`, ); await interaction.followUp({ - content: - "⚠️ Ticket closed but failed to send transcript to transcript channel.", + content: "⚠️ Ticket closed but failed to send transcript to transcript channel.", }); } }; try { - await retry( - messageAuthor, - `DM user ${ticket.authorId} for ticket ${ticket.id}`, - ); + await retry(messageAuthor, `DM user ${ticket.authorId} for ticket ${ticket.id}`); await retry( sendToStaff, `send transcript to channel ${transcriptChannelId} for ticket ${ticket.id}`,

@@ -484,10 +437,7 @@ name: `closed-${channelName}`,

}); } -export async function handleThank( - _client: Client, - interaction: ButtonInteraction, -) { +export async function handleThank(_client: Client, interaction: ButtonInteraction) { await interaction.deferReply({ flags: ["Ephemeral"] }); const id = Number(interaction.customId.split(":")[2]);

@@ -502,9 +452,7 @@ );

} if (ticket.thankedAt) { - return interaction.editReply( - "⚠️ You have already thanked the moderator for this ticket.", - ); + return interaction.editReply("⚠️ You have already thanked the moderator for this ticket."); } const { data: guild, exists: guildExists } = await getGuild(ticket.guildId);

@@ -531,20 +479,12 @@ `The reporter says thank you for handling ticket #${ticket.ticketNumber}! 💖`,

); const thankedAt = new Date(); - await db - .update(tickets) - .set({ thankedAt }) - .where(eq(tickets.id, ticket.id)); + await db.update(tickets).set({ thankedAt }).where(eq(tickets.id, ticket.id)); return interaction.editReply("A thank you message sent to the moderators!"); } catch (error) { - logger.error( - error, - `Could not send thank you message for ticket ${ticket.id}`, - ); - return interaction.editReply( - "⚠️ Failed to send thank you message to the moderator.", - ); + logger.error(error, `Could not send thank you message for ticket ${ticket.id}`); + return interaction.editReply("⚠️ Failed to send thank you message to the moderator."); } }

@@ -587,11 +527,8 @@ } catch (error) {

logger.error(error, `could not restore permissions for ${ticket.authorId}`); } - const controls = new ActionRowBuilder<ButtonBuilder>().addComponents( - new ButtonBuilder() - .setCustomId("ticket:claim") - .setLabel("Claim") - .setStyle(ButtonStyle.Primary), + const controls = new ActionRowBuilder<ButtonBuilder>().addComponents( + new ButtonBuilder().setCustomId("ticket:claim").setLabel("Claim").setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId("ticket:close") .setLabel("Close")

@@ -612,9 +549,7 @@ _client: Client,

interaction: ButtonInteraction | ChatInputCommandInteraction, ticket: Ticket, ) { - const isStaff = await hasManagerPermissions( - interaction.member as GuildMember, - ); + const isStaff = await hasManagerPermissions(interaction.member as GuildMember); if (!isStaff) { await interaction.editReply( "❌ You don't have permission to delete this ticket, you can only close it.",

@@ -622,14 +557,9 @@ );

return; } - await db - .update(tickets) - .set({ status: "archived" }) - .where(eq(tickets.id, ticket.id)); + await db.update(tickets).set({ status: "archived" }).where(eq(tickets.id, ticket.id)); - await interaction.editReply( - "✅ Ticket deleted. Channel will be deleted shortly.", - ); + await interaction.editReply("✅ Ticket deleted. Channel will be deleted shortly."); const channel = interaction.channel; if (channel && "delete" in channel) {

@@ -639,9 +569,7 @@ }

logger.error( `Channel with ID ${interaction.channelId} not found or cannot be deleted for ticket ${ticket.id}`, ); - interaction.editReply( - "Ticket was marked as deleted but couldn't delete the channel.", - ); + interaction.editReply("Ticket was marked as deleted but couldn't delete the channel."); } export async function handleTranscript(

@@ -649,9 +577,7 @@ _client: Client,

interaction: ButtonInteraction | ChatInputCommandInteraction, ticket: Ticket, ) { - const isModerator = await hasManagerPermissions( - interaction.member as GuildMember, - ); + const isModerator = await hasManagerPermissions(interaction.member as GuildMember); const isReporter = interaction.user.id === ticket.authorId; if (!isModerator && !isReporter) { await interaction.editReply(

@@ -680,9 +606,7 @@ _client: Client,

interaction: ButtonInteraction | ChatInputCommandInteraction, ticket: Ticket, ) { - const isStaff = await hasManagerPermissions( - interaction.member as GuildMember, - ); + const isStaff = await hasManagerPermissions(interaction.member as GuildMember); if (!isStaff) { await interaction.editReply({ content: "Only staff can claim tickets.",

@@ -717,14 +641,15 @@ .setStyle(ButtonStyle.Danger)

.setEmoji("🔒"), ); - if (interaction.channel?.isSendable()) { - const moderator = getModerator(interaction.user.id) - const pronoun = capitalise(moderator.subjective) + if (interaction.channel?.isSendable() && interaction.channel.type === ChannelType.GuildText) { + const moderator = getModerator(interaction.user.id); + const pronoun = capitalise(moderator.subjective); await interaction.channel.send({ content: `This ticket has been claimed by <@${interaction.user.id}>! ${pronoun} will be with you shortly.`, components: [controls], }); + interaction.channel.setName(`claimed-${ticket.ticketNumber}`).catch(() => {}); } await interaction.editReply("✅ Ticket claimed.");

@@ -735,9 +660,7 @@ _client: Client,

interaction: ButtonInteraction | ChatInputCommandInteraction, ticket: Ticket, ) { - const isStaff = await hasManagerPermissions( - interaction.member as GuildMember, - ); + const isStaff = await hasManagerPermissions(interaction.member as GuildMember); if (!isStaff) { await interaction.editReply({ content: "❌ Only staff can unclaim tickets.",

@@ -761,10 +684,7 @@ })

.where(eq(tickets.id, ticket.id)); const controls = new ActionRowBuilder<ButtonBuilder>().addComponents( - new ButtonBuilder() - .setCustomId("ticket:claim") - .setLabel("Claim") - .setStyle(ButtonStyle.Primary), + new ButtonBuilder().setCustomId("ticket:claim").setLabel("Claim").setStyle(ButtonStyle.Primary), new ButtonBuilder() .setCustomId("ticket:close") .setLabel("Close")