import config from "@/config"; import { bugs, db } from "@/database"; import { getGuild } from "@/database/queries"; import { parseComponentId } from "@/utils/discord/components"; import { hasManagerPermissions } from "@/utils/discord/permissions"; import type { ButtonInteraction, Client, GuildMember } from "discord.js"; import { eq } from "drizzle-orm"; import { buildReportModal, updateBugEmbed } from "./_shared"; async function fetchBugById(id: string) { const parsedId = Number(id); if (Number.isNaN(parsedId)) return null; const results = await db.select().from(bugs).where(eq(bugs.id, parsedId)); return results[0] ?? null; } export async function handleCloseButton(client: Client, interaction: ButtonInteraction) { const guildId = interaction.guildId; if (!guildId) return; const { parts } = parseComponentId(interaction.customId); const id = parts[1]; const bug = await fetchBugById(id); if (!bug) { await interaction.reply({ content: "❌ Bug not found.", flags: ["Ephemeral"], }); return; } await db .update(bugs) .set({ status: "closed", closedAt: new Date(), updatedAt: new Date() }) .where(eq(bugs.id, bug.id)); const { data, exists } = await getGuild(guildId); if (!exists) { await interaction.reply({ content: "❌ Guild configuration not found.", flags: ["Ephemeral"], }); return; } const channelId = data.bug_channel_id; if (!channelId) { await interaction.reply({ content: "❌ Bug channel not configured.", flags: ["Ephemeral"], }); return; } if (bug.messageId) { await updateBugEmbed(client, { ...bug, status: "closed" }, bug.messageId, channelId); } await interaction.reply({ content: `✅ Bug #${bug.bugNumber.toString().padStart(4, "0")} closed.`, flags: ["Ephemeral"], }); } export async function handleOpenButton(client: Client, interaction: ButtonInteraction) { const guildId = interaction.guildId; if (!guildId) return; const { parts } = parseComponentId(interaction.customId); const id = parts[1]; const bug = await fetchBugById(id); if (!bug) { await interaction.reply({ content: "❌ Bug not found.", flags: ["Ephemeral"], }); return; } await db .update(bugs) .set({ status: "open", closedAt: null, updatedAt: new Date() }) .where(eq(bugs.id, bug.id)); const { data, exists } = await getGuild(guildId); if (!exists) { await interaction.reply({ content: "❌ Guild configuration not found.", flags: ["Ephemeral"], }); return; } const channelId = data.bug_channel_id; if (!channelId) { await interaction.reply({ content: "❌ Bug channel not configured.", flags: ["Ephemeral"], }); return; } if (bug.messageId) { await updateBugEmbed(client, { ...bug, status: "open" }, bug.messageId, channelId); } await interaction.reply({ content: `✅ Bug #${bug.bugNumber.toString().padStart(4, "0")} reopened.`, flags: ["Ephemeral"], }); } export async function handleEditButton(_client: Client, interaction: ButtonInteraction) { const { parts } = parseComponentId(interaction.customId); const id = parts[1]; const bug = await fetchBugById(id); if (!bug) { await interaction.reply({ content: "❌ Bug not found.", flags: ["Ephemeral"], }); return; } const modal = buildReportModal(String(bug.id), bug); await interaction.showModal(modal); } export async function handleDeleteButton(_client: Client, interaction: ButtonInteraction) { const guildId = interaction.guildId; if (!guildId) return; const { parts } = parseComponentId(interaction.customId); const id = parts[1]; const bug = await fetchBugById(id); if (!bug) { await interaction.reply({ content: "❌ Bug not found.", flags: ["Ephemeral"], }); return; } const isManager = await hasManagerPermissions(interaction.member as GuildMember); const isBugReporter = bug.authorId === interaction.user.id; if (!isManager && !isBugReporter) { await interaction.reply({ content: "❌ You don't have permission to delete this bug.", flags: ["Ephemeral"], }); return; } await db.delete(bugs).where(eq(bugs.id, bug.id)); const { data, exists } = await getGuild(guildId); if (!exists) { await interaction.reply({ content: "❌ Guild configuration not found, bug removed from database.", flags: ["Ephemeral"], }); return; } const channelId = data.bug_channel_id; if (!channelId) { await interaction.reply({ content: "✅ Bug removed from database. (No bug channel configured to remove message/thread)", flags: ["Ephemeral"], }); return; } try { const channel = await interaction.guild?.channels.fetch(channelId); if (channel?.isTextBased() && bug.messageId) { const { message } = interaction; const { thread } = message; if (message) { if (thread) await thread .delete(`bug ${bug.bugNumber} deleted by <@${interaction.user.id}>`) .catch(() => {}); await message.delete().catch(() => {}); } } } catch {} await interaction.reply({ content: `✅ Bug #${bug.bugNumber.toString().padStart(4, "0")} deleted.`, flags: ["Ephemeral"], }); } export async function handleTrelloButton(_client: Client, interaction: ButtonInteraction) { const { parts } = parseComponentId(interaction.customId); const id = parts[1]; const bug = await fetchBugById(id); if (!bug) { await interaction.reply({ content: "❌ Bug not found.", flags: ["Ephemeral"], }); return; } const params = new URLSearchParams({ name: bug.title, url: interaction.message.url, idBoard: config.trelloBoardId || "", }); const url = new URL("https://trello.com/addCard"); url.search = params.toString(); await interaction.reply({ content: url.toString(), flags: ["Ephemeral"], }); }