import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from "discord.js"; import { createInfoEmbed, createSuccessEmbed, createWarningEmbed, createErrorEmbed } from "@lib/embeds"; import type { UpdateInfo, UpdateCheckResult } from "./update.types"; // Constants for UI const LOG_TRUNCATE_LENGTH = 800; const OUTPUT_TRUNCATE_LENGTH = 400; function truncate(text: string, maxLength: number): string { if (!text) return ""; return text.length > maxLength ? `${text.substring(0, maxLength)}...` : text; } // ============ Pre-Update Embeds ============ export function getCheckingEmbed() { return createInfoEmbed("šŸ” Fetching latest changes from remote...", "Checking for Updates"); } export function getNoUpdatesEmbed(currentCommit: string) { return createSuccessEmbed( `You're running the latest version.\n\n**Current:** \`${currentCommit}\``, "āœ… Already Up to Date" ); } export function getUpdatesAvailableMessage( updateInfo: UpdateInfo, requirements: UpdateCheckResult, changeCategories: Record, force: boolean ) { const { branch, currentCommit, latestCommit, commitCount, commits } = updateInfo; const { needsRootInstall, needsWebInstall, needsMigrations } = requirements; // Build commit list (max 5) const commitList = commits .slice(0, 5) .map(c => `\`${c.hash}\` ${truncate(c.message, 50)}`) .join("\n"); const moreCommits = commitCount > 5 ? `\n*...and ${commitCount - 5} more*` : ""; // Build change categories const categoryList = Object.entries(changeCategories) .map(([cat, count]) => `• ${cat}: ${count} file${count > 1 ? "s" : ""}`) .join("\n"); // Build requirements list const reqs: string[] = []; if (needsRootInstall) reqs.push("šŸ“¦ Install root dependencies"); if (needsWebInstall) reqs.push("🌐 Install web dependencies"); if (needsMigrations) reqs.push("šŸ—ƒļø Run database migrations"); if (reqs.length === 0) reqs.push("⚔ Quick update (no extra steps)"); const embed = new EmbedBuilder() .setTitle("šŸ“„ Updates Available") .setColor(force ? 0xFF6B6B : 0x5865F2) .addFields( { name: "Version", value: `\`${currentCommit}\` → \`${latestCommit}\``, inline: true }, { name: "Branch", value: `\`${branch}\``, inline: true }, { name: "Commits", value: `${commitCount} new commit${commitCount > 1 ? "s" : ""}`, inline: true }, { name: "Recent Changes", value: commitList + moreCommits || "No commits", inline: false }, { name: "Files Changed", value: categoryList || "Unknown", inline: true }, { name: "Update Actions", value: reqs.join("\n"), inline: true } ) .setFooter({ text: force ? "āš ļø Force mode enabled" : "This will restart the bot" }) .setTimestamp(); const confirmButton = new ButtonBuilder() .setCustomId("confirm_update") .setLabel(force ? "Force Update" : "Update Now") .setEmoji(force ? "āš ļø" : "šŸš€") .setStyle(force ? ButtonStyle.Danger : ButtonStyle.Success); const cancelButton = new ButtonBuilder() .setCustomId("cancel_update") .setLabel("Cancel") .setStyle(ButtonStyle.Secondary); const row = new ActionRowBuilder().addComponents(confirmButton, cancelButton); return { embeds: [embed], components: [row] }; } // ============ Update Progress Embeds ============ export function getPreparingEmbed() { return createInfoEmbed( "šŸ”’ Saving rollback point...\nšŸ“„ Preparing to download updates...", "ā³ Preparing Update" ); } export function getUpdatingEmbed(requirements: UpdateCheckResult) { const steps: string[] = ["āœ… Rollback point saved"]; steps.push("šŸ“„ Downloading updates..."); if (requirements.needsRootInstall || requirements.needsWebInstall) { steps.push("šŸ“¦ Dependencies will be installed after restart"); } if (requirements.needsMigrations) { steps.push("šŸ—ƒļø Migrations will run after restart"); } steps.push("\nšŸ”„ **Restarting now...**"); return createWarningEmbed(steps.join("\n"), "šŸš€ Updating"); } export function getCancelledEmbed() { return createInfoEmbed("Update cancelled. No changes were made.", "āŒ Cancelled"); } export function getTimeoutEmbed() { return createWarningEmbed( "No response received within 30 seconds.\nRun `/update` again when ready.", "ā° Timed Out" ); } export function getErrorEmbed(error: unknown) { const message = error instanceof Error ? error.message : String(error); return createErrorEmbed( `The update could not be completed:\n\`\`\`\n${truncate(message, 500)}\n\`\`\``, "āŒ Update Failed" ); } // ============ Post-Restart Embeds ============ export interface PostRestartResult { installSuccess: boolean; installOutput: string; migrationSuccess: boolean; migrationOutput: string; ranInstall: boolean; ranMigrations: boolean; previousCommit?: string; newCommit?: string; } export function getPostRestartEmbed(result: PostRestartResult, hasRollback: boolean) { const isSuccess = result.installSuccess && result.migrationSuccess; const embed = new EmbedBuilder() .setTitle(isSuccess ? "āœ… Update Complete" : "āš ļø Update Completed with Issues") .setColor(isSuccess ? 0x57F287 : 0xFEE75C) .setTimestamp(); // Version info if (result.previousCommit && result.newCommit) { embed.addFields({ name: "Version", value: `\`${result.previousCommit}\` → \`${result.newCommit}\``, inline: false }); } // Results summary const results: string[] = []; if (result.ranInstall) { results.push(result.installSuccess ? "āœ… Dependencies installed" : "āŒ Dependency installation failed" ); } if (result.ranMigrations) { results.push(result.migrationSuccess ? "āœ… Migrations applied" : "āŒ Migration failed" ); } if (results.length > 0) { embed.addFields({ name: "Actions Performed", value: results.join("\n"), inline: false }); } // Output details (collapsed if too long) if (result.installOutput && !result.installSuccess) { embed.addFields({ name: "Install Output", value: `\`\`\`\n${truncate(result.installOutput, OUTPUT_TRUNCATE_LENGTH)}\n\`\`\``, inline: false }); } if (result.migrationOutput && !result.migrationSuccess) { embed.addFields({ name: "Migration Output", value: `\`\`\`\n${truncate(result.migrationOutput, OUTPUT_TRUNCATE_LENGTH)}\n\`\`\``, inline: false }); } // Footer with rollback hint if (!isSuccess && hasRollback) { embed.setFooter({ text: "šŸ’” Use /update rollback to revert if needed" }); } // Build components const components: ActionRowBuilder[] = []; if (!isSuccess && hasRollback) { const rollbackButton = new ButtonBuilder() .setCustomId("rollback_update") .setLabel("Rollback") .setEmoji("ā†©ļø") .setStyle(ButtonStyle.Danger); components.push(new ActionRowBuilder().addComponents(rollbackButton)); } return { embeds: [embed], components }; } export function getInstallingDependenciesEmbed() { return createInfoEmbed( "šŸ“¦ Installing dependencies for root and web projects...\nThis may take a moment.", "ā³ Installing Dependencies" ); } export function getRunningMigrationsEmbed() { return createInfoEmbed( "šŸ—ƒļø Applying database migrations...", "ā³ Running Migrations" ); } export function getRollbackSuccessEmbed(commit: string) { return createSuccessEmbed( `Successfully rolled back to commit \`${commit}\`.\nThe bot will restart now.`, "ā†©ļø Rollback Complete" ); } export function getRollbackFailedEmbed(error: string) { return createErrorEmbed( `Could not rollback:\n\`\`\`\n${error}\n\`\`\``, "āŒ Rollback Failed" ); }