bot: bug reports
vi did:web:vt3e.cat
Mon, 29 Jun 2026 18:21:26 +0100
10 files changed,
2174 insertions(+),
3 deletions(-)
jump to
A
apps/bot/src/feats/bugs/commands.ts
@@ -0,0 +1,15 @@
+import { kitten } from "@/client"; + +import { buildReportModal } from "./shared"; + +export const bugCommand = kitten.command("bug", { + description: "Make a bug report", +}); + +bugCommand.subcommand("report", { + description: "Report a new bug", + async run(interaction) { + const modal = buildReportModal(); + await interaction.showModal(modal); + }, +});
A
apps/bot/src/feats/bugs/index.ts
@@ -0,0 +1,2 @@
+export { bugCommand } from "./commands"; +export { bugModal } from "./modals";
A
apps/bot/src/feats/bugs/modals.ts
@@ -0,0 +1,117 @@
+import db, { eq, sql } from "@stealth-developers/db"; +import { bugs } from "@stealth-developers/db/src/schemas/bugs"; +import { AttachmentBuilder } from "discord.js"; + +import { errorMessage, getTextChannel } from "@/lib"; +import { useGetData } from "@/middleware"; + +import { constructBugContainer } from "./shared"; + +export const bugModal = useGetData.modal("bug-report", { + run: async (interaction, _, { actor, guild }) => { + if (!actor || !guild) + return errorMessage(interaction, "You must be in a server to report a bug."); + + const { fields: _fields } = interaction; + const fields = { + affected: _fields.getStringSelectValues("affected"), + title: _fields.getTextInputValue("title"), + description: _fields.getTextInputValue("description"), + media: _fields.getUploadedFiles("media", false), + }; + + await interaction.deferReply({ flags: ["Ephemeral"] }); + + const bugRes = await db.transaction(async (tx) => { + const [res] = await tx + .select({ count: sql<number>`COUNT(*)`.mapWith(Number) }) + .from(bugs) + .where(eq(bugs.guildId, guild.id)); + + if (!res) return null; + const { count } = res; + + const bugNumber = count + 1; + + return await tx + .insert(bugs) + .values({ + guildId: guild.id, + actorId: actor.id, + + bugNumber: bugNumber, + status: "open", + + title: fields.title, + description: fields.description, + affectedProjects: [...fields.affected], + + sent: false, + messageId: null, + threadId: null, + + createdAt: new Date(), + updatedAt: new Date(), + closedAt: null, + }) + .returning(); + }); + if (!bugRes) return errorMessage(interaction, "Failed to create bug report."); + + const bug = bugRes[0]; + if (!bug) return errorMessage(interaction, "Failed to create bug report."); + + let attachment: AttachmentBuilder | undefined; + if (fields.media && fields.media.size > 0) { + const [_, _attachment] = Array.from(fields.media)[0]!; + try { + const fileUrl = _attachment.url; + const fileName = _attachment.name; + if (fileUrl) { + const res = await fetch(fileUrl); + if (res.ok) { + const arrayBuffer = await res.arrayBuffer(); + const buffer = Buffer.from(arrayBuffer); + attachment = new AttachmentBuilder(buffer, { name: fileName }); + } + } + } catch (err) { + console.error("error processing uploaded file:", err); + } + } + + const container = await constructBugContainer(bug, actor, attachment); + if (!container) return; + + const channel = await getTextChannel("1512924775842840767"); + if (!channel) return errorMessage(interaction, "Failed to send bug report."); + + const message = await channel.send({ + components: [container], + flags: ["IsComponentsV2"], + ...(attachment ? { files: [attachment] } : {}), + }); + + function truncateTitle(title: string, maxLength: number): string { + return title.length > maxLength ? `${title.substring(0, maxLength)}...` : title; + } + const paddedNumber = bug.bugNumber.toString().padStart(4, "0"); + const threadName = `#${paddedNumber} ${truncateTitle(fields.title, 150)}`; + const thread = await message.startThread({ + name: threadName, + autoArchiveDuration: 1440, + }); + + await db + .update(bugs) + .set({ threadId: thread.id, messageId: message.id }) + .where(eq(bugs.id, bug.id)); + + thread.send({ + content: "Use this space to discuss the bug, provide additional details, or ask questions.", + }); + interaction.editReply( + `Bug #${paddedNumber} created successfully! Find it here: ${message.url}`, + ); + }, +});
M
apps/bot/src/feats/index.ts
→
apps/bot/src/feats/index.ts
@@ -1,5 +1,6 @@
+import { bugCommand, bugModal } from "./bugs"; import { miscCommands } from "./misc"; import { ticketCommands, ticketComponents } from "./tickets"; -export const commands = [...ticketCommands, ...miscCommands]; -export const components = [...ticketComponents]; +export const commands = [...ticketCommands, ...miscCommands, bugCommand]; +export const components = [...ticketComponents, bugModal];
A
pkgs/db/drizzle/20260629171950_dizzy_expediter/migration.sql
@@ -0,0 +1,18 @@
+CREATE TABLE `bugs` ( + `id` integer PRIMARY KEY AUTOINCREMENT, + `guild_id` integer NOT NULL, + `actor_id` integer NOT NULL, + `bug_number` integer NOT NULL, + `status` text, + `title` text NOT NULL, + `description` text NOT NULL, + `affected_projects` text DEFAULT '[]' NOT NULL, + `sent` integer DEFAULT false NOT NULL, + `message_id` text, + `thread_id` text, + `created_at` integer DEFAULT (unixepoch()) NOT NULL, + `updated_at` integer DEFAULT (unixepoch()) NOT NULL, + `closed_at` integer, + CONSTRAINT `fk_bugs_guild_id_guilds_id_fk` FOREIGN KEY (`guild_id`) REFERENCES `guilds`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_bugs_actor_id_actors_id_fk` FOREIGN KEY (`actor_id`) REFERENCES `actors`(`id`) ON DELETE CASCADE +);
A
pkgs/db/drizzle/20260629171950_dizzy_expediter/snapshot.json
@@ -0,0 +1,1728 @@
+{ + "version": "7", + "dialect": "sqlite", + "id": "7307595b-c82d-458e-81d5-05f82bed2390", + "prevIds": [ + "35559d0e-f33e-4ca5-8f75-37ac46e70ec0" + ], + "ddl": [ + { + "name": "sessions", + "entityType": "tables" + }, + { + "name": "users", + "entityType": "tables" + }, + { + "name": "actors", + "entityType": "tables" + }, + { + "name": "guilds", + "entityType": "tables" + }, + { + "name": "ticket_attachments", + "entityType": "tables" + }, + { + "name": "ticket_comments", + "entityType": "tables" + }, + { + "name": "ticket_messages", + "entityType": "tables" + }, + { + "name": "ticket_participants", + "entityType": "tables" + }, + { + "name": "tickets", + "entityType": "tables" + }, + { + "name": "bugs", + "entityType": "tables" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "sessions" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "user_id", + "entityType": "columns", + "table": "sessions" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "access_token", + "entityType": "columns", + "table": "sessions" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "refresh_token", + "entityType": "columns", + "table": "sessions" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "scope", + "entityType": "columns", + "table": "sessions" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "token_type", + "entityType": "columns", + "table": "sessions" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "expires_at", + "entityType": "columns", + "table": "sessions" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "created_at", + "entityType": "columns", + "table": "sessions" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "users" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "discord_id", + "entityType": "columns", + "table": "users" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "display_name", + "entityType": "columns", + "table": "users" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "username", + "entityType": "columns", + "table": "users" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "avatar", + "entityType": "columns", + "table": "users" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "accepted_privacy_policy", + "entityType": "columns", + "table": "users" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "accepted_terms_of_service", + "entityType": "columns", + "table": "users" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "created_at", + "entityType": "columns", + "table": "users" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "updated_at", + "entityType": "columns", + "table": "users" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "actors" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "guild_id", + "entityType": "columns", + "table": "actors" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "user_id", + "entityType": "columns", + "table": "actors" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "discord_id", + "entityType": "columns", + "table": "actors" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "display_name", + "entityType": "columns", + "table": "actors" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "username", + "entityType": "columns", + "table": "actors" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "avatar", + "entityType": "columns", + "table": "actors" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "moderator", + "entityType": "columns", + "table": "actors" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "created_at", + "entityType": "columns", + "table": "actors" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "updated_at", + "entityType": "columns", + "table": "actors" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "deleted_at", + "entityType": "columns", + "table": "actors" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "discord_id", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "name", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "icon", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_category", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_log_channel", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_greeting", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_prompt_channel", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_prompt", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "created_at", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "updated_at", + "entityType": "columns", + "table": "guilds" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "ticket_attachments" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "message_id", + "entityType": "columns", + "table": "ticket_attachments" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_id", + "entityType": "columns", + "table": "ticket_attachments" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "actor_id", + "entityType": "columns", + "table": "ticket_attachments" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": "'watever'", + "generated": null, + "name": "key", + "entityType": "columns", + "table": "ticket_attachments" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "file_name", + "entityType": "columns", + "table": "ticket_attachments" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "file_type", + "entityType": "columns", + "table": "ticket_attachments" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "file_size", + "entityType": "columns", + "table": "ticket_attachments" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "ticket_comments" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_id", + "entityType": "columns", + "table": "ticket_comments" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "actor_id", + "entityType": "columns", + "table": "ticket_comments" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "content", + "entityType": "columns", + "table": "ticket_comments" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "created_at", + "entityType": "columns", + "table": "ticket_comments" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "updated_at", + "entityType": "columns", + "table": "ticket_comments" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "message_id", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_id", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "actor_id", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "actor_role", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "is_private", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "content", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "embeds", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "components", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "created_at", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "edited_at", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "deleted_at", + "entityType": "columns", + "table": "ticket_messages" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "ticket_participants" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "ticket_id", + "entityType": "columns", + "table": "ticket_participants" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "actor_id", + "entityType": "columns", + "table": "ticket_participants" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "role", + "entityType": "columns", + "table": "ticket_participants" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "reason", + "entityType": "columns", + "table": "ticket_participants" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "created_at", + "entityType": "columns", + "table": "ticket_participants" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "guild_id", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "local_id", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "status", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "channel_id", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "thread_id", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "close_reason", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "private_reason", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "opened_at", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "closed_at", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "closed_by", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "last_subject_activity_at", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "last_moderator_activity_at", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "opened_by", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "open_reason", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "subject", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "claimed_by", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "claimed_at", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "thanked_at", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "warned_at", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "last_message_at", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "has_moderator_message", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "has_author_message", + "entityType": "columns", + "table": "tickets" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": true, + "default": null, + "generated": null, + "name": "id", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "guild_id", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "actor_id", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "bug_number", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "status", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "title", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": null, + "generated": null, + "name": "description", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "text", + "notNull": true, + "autoincrement": false, + "default": "'[]'", + "generated": null, + "name": "affected_projects", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "false", + "generated": null, + "name": "sent", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "message_id", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "text", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "thread_id", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())", + "generated": null, + "name": "created_at", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "integer", + "notNull": true, + "autoincrement": false, + "default": "(unixepoch())", + "generated": null, + "name": "updated_at", + "entityType": "columns", + "table": "bugs" + }, + { + "type": "integer", + "notNull": false, + "autoincrement": false, + "default": null, + "generated": null, + "name": "closed_at", + "entityType": "columns", + "table": "bugs" + }, + { + "columns": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_sessions_user_id_users_id_fk", + "entityType": "fks", + "table": "sessions" + }, + { + "columns": [ + "guild_id" + ], + "tableTo": "guilds", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_actors_guild_id_guilds_id_fk", + "entityType": "fks", + "table": "actors" + }, + { + "columns": [ + "user_id" + ], + "tableTo": "users", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_actors_user_id_users_id_fk", + "entityType": "fks", + "table": "actors" + }, + { + "columns": [ + "message_id" + ], + "tableTo": "ticket_messages", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_attachments_message_id_ticket_messages_id_fk", + "entityType": "fks", + "table": "ticket_attachments" + }, + { + "columns": [ + "ticket_id" + ], + "tableTo": "tickets", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_attachments_ticket_id_tickets_id_fk", + "entityType": "fks", + "table": "ticket_attachments" + }, + { + "columns": [ + "actor_id" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_attachments_actor_id_actors_id_fk", + "entityType": "fks", + "table": "ticket_attachments" + }, + { + "columns": [ + "ticket_id" + ], + "tableTo": "tickets", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_comments_ticket_id_tickets_id_fk", + "entityType": "fks", + "table": "ticket_comments" + }, + { + "columns": [ + "actor_id" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_comments_actor_id_actors_id_fk", + "entityType": "fks", + "table": "ticket_comments" + }, + { + "columns": [ + "ticket_id" + ], + "tableTo": "tickets", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_messages_ticket_id_tickets_id_fk", + "entityType": "fks", + "table": "ticket_messages" + }, + { + "columns": [ + "actor_id" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_messages_actor_id_actors_id_fk", + "entityType": "fks", + "table": "ticket_messages" + }, + { + "columns": [ + "ticket_id" + ], + "tableTo": "tickets", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_participants_ticket_id_tickets_id_fk", + "entityType": "fks", + "table": "ticket_participants" + }, + { + "columns": [ + "actor_id" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_ticket_participants_actor_id_actors_id_fk", + "entityType": "fks", + "table": "ticket_participants" + }, + { + "columns": [ + "guild_id" + ], + "tableTo": "guilds", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_tickets_guild_id_guilds_id_fk", + "entityType": "fks", + "table": "tickets" + }, + { + "columns": [ + "closed_by" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "nameExplicit": false, + "name": "fk_tickets_closed_by_actors_id_fk", + "entityType": "fks", + "table": "tickets" + }, + { + "columns": [ + "opened_by" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "nameExplicit": false, + "name": "fk_tickets_opened_by_actors_id_fk", + "entityType": "fks", + "table": "tickets" + }, + { + "columns": [ + "subject" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "nameExplicit": false, + "name": "fk_tickets_subject_actors_id_fk", + "entityType": "fks", + "table": "tickets" + }, + { + "columns": [ + "claimed_by" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "NO ACTION", + "nameExplicit": false, + "name": "fk_tickets_claimed_by_actors_id_fk", + "entityType": "fks", + "table": "tickets" + }, + { + "columns": [ + "guild_id" + ], + "tableTo": "guilds", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_bugs_guild_id_guilds_id_fk", + "entityType": "fks", + "table": "bugs" + }, + { + "columns": [ + "actor_id" + ], + "tableTo": "actors", + "columnsTo": [ + "id" + ], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "nameExplicit": false, + "name": "fk_bugs_actor_id_actors_id_fk", + "entityType": "fks", + "table": "bugs" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "sessions_pk", + "table": "sessions", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "users_pk", + "table": "users", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "actors_pk", + "table": "actors", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "guilds_pk", + "table": "guilds", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "ticket_attachments_pk", + "table": "ticket_attachments", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "ticket_comments_pk", + "table": "ticket_comments", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "ticket_messages_pk", + "table": "ticket_messages", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "ticket_participants_pk", + "table": "ticket_participants", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "tickets_pk", + "table": "tickets", + "entityType": "pks" + }, + { + "columns": [ + "id" + ], + "nameExplicit": false, + "name": "bugs_pk", + "table": "bugs", + "entityType": "pks" + }, + { + "columns": [ + { + "value": "guild_id", + "isExpression": false + }, + { + "value": "user_id", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "actors_guild_user_idx", + "entityType": "indexes", + "table": "actors" + }, + { + "columns": [ + { + "value": "ticket_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_attachments_ticket_idx", + "entityType": "indexes", + "table": "ticket_attachments" + }, + { + "columns": [ + { + "value": "actor_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_attachments_actor_idx", + "entityType": "indexes", + "table": "ticket_attachments" + }, + { + "columns": [ + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_attachments_public_id_idx", + "entityType": "indexes", + "table": "ticket_attachments" + }, + { + "columns": [ + { + "value": "key", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_attachments_key_idx", + "entityType": "indexes", + "table": "ticket_attachments" + }, + { + "columns": [ + { + "value": "ticket_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_comments_ticket_idx", + "entityType": "indexes", + "table": "ticket_comments" + }, + { + "columns": [ + { + "value": "actor_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_comments_actor_idx", + "entityType": "indexes", + "table": "ticket_comments" + }, + { + "columns": [ + { + "value": "ticket_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_messages_ticket_idx", + "entityType": "indexes", + "table": "ticket_messages" + }, + { + "columns": [ + { + "value": "actor_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_messages_actor_idx", + "entityType": "indexes", + "table": "ticket_messages" + }, + { + "columns": [ + { + "value": "id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_messages_public_id_idx", + "entityType": "indexes", + "table": "ticket_messages" + }, + { + "columns": [ + { + "value": "ticket_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_participants_ticket_idx", + "entityType": "indexes", + "table": "ticket_participants" + }, + { + "columns": [ + { + "value": "actor_id", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_participants_actor_idx", + "entityType": "indexes", + "table": "ticket_participants" + }, + { + "columns": [ + { + "value": "role", + "isExpression": false + } + ], + "isUnique": false, + "where": null, + "origin": "manual", + "name": "ticket_participants_role_idx", + "entityType": "indexes", + "table": "ticket_participants" + }, + { + "columns": [ + { + "value": "guild_id", + "isExpression": false + }, + { + "value": "local_id", + "isExpression": false + } + ], + "isUnique": true, + "where": null, + "origin": "manual", + "name": "tickets_guild_local_idx", + "entityType": "indexes", + "table": "tickets" + }, + { + "columns": [ + "discord_id" + ], + "nameExplicit": false, + "name": "users_discord_id_unique", + "entityType": "uniques", + "table": "users" + }, + { + "columns": [ + "discord_id" + ], + "nameExplicit": false, + "name": "guilds_discord_id_unique", + "entityType": "uniques", + "table": "guilds" + } + ], + "renames": [] +}
A
pkgs/db/src/schemas/bugs.ts
@@ -0,0 +1,46 @@
+import { sql } from "drizzle-orm"; +import * as s from "drizzle-orm/sqlite-core"; + +import { actors } from "./actors"; +import { guilds } from "./guilds"; + +export const bugs = s.sqliteTable("bugs", { + id: s.integer("id").primaryKey({ autoIncrement: true }), + guildId: s + .integer("guild_id") + .notNull() + .references(() => guilds.id, { onDelete: "cascade" }), + actorId: s + .integer("actor_id") + .notNull() + .references(() => actors.id, { onDelete: "cascade" }), + + bugNumber: s.integer("bug_number").notNull(), + status: s.text("status", { enum: ["open", "closed", "resolved"] }), + + title: s.text("title").notNull(), + description: s.text("description").notNull(), + + affectedProjects: s + .text("affected_projects", { mode: "json" }) + .$type<string[]>() + .default([]) + .notNull(), + + sent: s.integer("sent", { mode: "boolean" }).default(false).notNull(), + messageId: s.text("message_id"), + threadId: s.text("thread_id"), + + createdAt: s + .integer("created_at", { mode: "timestamp" }) + .default(sql`(unixepoch())`) + .notNull(), + updatedAt: s + .integer("updated_at", { mode: "timestamp" }) + .default(sql`(unixepoch())`) + .notNull(), + closedAt: s.integer("closed_at", { mode: "timestamp" }), +}); + +export type Bug = typeof bugs.$inferSelect; +export type NewBug = typeof bugs.$inferInsert;
M
pkgs/db/src/schemas/index.ts
→
pkgs/db/src/schemas/index.ts
@@ -3,3 +3,5 @@ export * from "./actors";
export * from "./guilds"; export * from "./tickets"; + +export * from "./bugs";
M
pkgs/db/src/schemas/relations.ts
→
pkgs/db/src/schemas/relations.ts
@@ -1,6 +1,7 @@
import { defineRelations } from "drizzle-orm"; import { actors } from "./actors"; +import { bugs } from "./bugs"; import { guilds } from "./guilds"; import { ticketAttachments,@@ -10,7 +11,6 @@ ticketParticipants,
tickets, } from "./tickets"; import { sessions, users } from "./users"; - export default defineRelations( { guilds,@@ -22,6 +22,7 @@ ticketParticipants,
ticketMessages, ticketAttachments, ticketComments, + bugs, }, (r) => ({ guilds: {@@ -41,6 +42,7 @@
actors: { guild: r.one.guilds({ from: r.actors.guildId, to: r.guilds.id }), user: r.one.users({ from: r.actors.userId, to: r.users.id }), + bugs: r.many.bugs({ from: r.actors.id, to: r.bugs.actorId }), openedTickets: r.many.tickets({ from: r.actors.id,@@ -134,6 +136,10 @@ message: r.one.ticketMessages({
from: r.ticketAttachments.messageId, to: r.ticketMessages.id, }), + }, + + bugs: { + actor: r.one.actors({ from: r.bugs.actorId, to: r.actors.id }), }, }), );