all repos — stealth-developers @ ffa2c91027d77bb42b875d24dd139172e61aa2c3

feat(tickets): log when transcripts are viewed
vi v@vt3e.cat
Thu, 05 Mar 2026 03:25:29 +0000
commit

ffa2c91027d77bb42b875d24dd139172e61aa2c3

parent

930b53d1f2ecf63e281211eb4521c0cba2675320

2 files changed, 44 insertions(+), 2 deletions(-)

jump to
M src/database/schema/tickets.tssrc/database/schema/tickets.ts

@@ -61,18 +61,46 @@ },

(t) => [index("ticket_msgs_ticket_idx").on(t.ticketId)], ); +export const ticketAccessLogs = sqliteTable( + "ticket_access_logs", + { + id: integer("id").primaryKey({ autoIncrement: true }), + ticketId: integer("ticket_id") + .references(() => tickets.id, { onDelete: "cascade" }) + .notNull(), + + userId: text("user_id").notNull(), + action: text("action", { enum: ["view_transcript"] }).notNull(), + + createdAt: integer("created_at", { mode: "timestamp" }) + .default(sql`(unixepoch())`) + .notNull(), + }, + (t) => [index("ticket_access_logs_ticket_idx").on(t.ticketId)], +); + export const TicketRelations = defineRelations( - { tickets, ticketMessages }, + { tickets, ticketMessages, ticketAccessLogs }, (r) => ({ tickets: { messages: r.many.ticketMessages({ from: r.tickets.id, to: r.ticketMessages.ticketId, }), + accessLogs: r.many.ticketAccessLogs({ + from: r.tickets.id, + to: r.ticketAccessLogs.ticketId, + }), }, ticketMessages: { ticket: r.one.tickets({ from: r.ticketMessages.ticketId, + to: r.tickets.id, + }), + }, + ticketAccessLogs: { + ticket: r.one.tickets({ + from: r.ticketAccessLogs.ticketId, to: r.tickets.id, }), },
M src/interactions/buttons/transcript-btn.tssrc/interactions/buttons/transcript-btn.ts

@@ -1,7 +1,10 @@

-import type { ButtonInteraction, Client } from "discord.js"; +import type { ButtonInteraction, Client, GuildMember } from "discord.js"; +import { hasManagerPermissions } from "@/utils/permissions"; import { getTicketById } from "@/utils/queries"; import { generateTranscript } from "@/utils/transcripts"; + +import { db, ticketAccessLogs } from "@/database"; export default { buttonExecute: async (_client: Client, interaction: ButtonInteraction) => {

@@ -14,6 +17,17 @@ content: "Ticket not found.",

ephemeral: true, }); return; + } + + if ( + interaction.member && + (await hasManagerPermissions(interaction.member as GuildMember)) + ) { + await db.insert(ticketAccessLogs).values({ + ticketId: ticket.id, + userId: interaction.user.id, + action: "view_transcript", + }); } const [transcriptText] = await generateTranscript(ticket);