Files
AuroraBot-discord/src/commands/admin/update.ts

100 lines
3.7 KiB
TypeScript

import { createCommand } from "@lib/utils";
import { SlashCommandBuilder, PermissionFlagsBits, MessageFlags, ComponentType } from "discord.js";
import { UpdateService } from "@/modules/admin/update.service";
import {
getCheckingEmbed,
getNoUpdatesEmbed,
getUpdatesAvailableMessage,
getPreparingEmbed,
getUpdatingEmbed,
getCancelledEmbed,
getTimeoutEmbed,
getErrorEmbed
} from "@/modules/admin/update.view";
export const update = createCommand({
data: new SlashCommandBuilder()
.setName("update")
.setDescription("Check for updates and restart the bot")
.addBooleanOption(option =>
option.setName("force")
.setDescription("Force update even if checks fail (not recommended)")
.setRequired(false)
)
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator),
execute: async (interaction) => {
await interaction.deferReply({ flags: MessageFlags.Ephemeral });
const force = interaction.options.getBoolean("force") || false;
try {
await interaction.editReply({ embeds: [getCheckingEmbed()] });
const { hasUpdates, log, branch } = await UpdateService.checkForUpdates();
if (!hasUpdates && !force) {
await interaction.editReply({ embeds: [getNoUpdatesEmbed()] });
return;
}
const { embeds, components } = getUpdatesAvailableMessage(branch, log, force);
const response = await interaction.editReply({ embeds, components });
try {
const confirmation = await response.awaitMessageComponent({
filter: (i) => i.user.id === interaction.user.id,
componentType: ComponentType.Button,
time: 30000
});
if (confirmation.customId === "confirm_update") {
await confirmation.update({
embeds: [getPreparingEmbed()],
components: []
});
// 1. Check what the update requires
const { needsInstall, needsMigrations } = await UpdateService.checkUpdateRequirements(branch);
// 2. Prepare context BEFORE update
await UpdateService.prepareRestartContext({
channelId: interaction.channelId,
userId: interaction.user.id,
timestamp: Date.now(),
runMigrations: needsMigrations,
installDependencies: needsInstall
});
// 3. Update UI to "Restarting" state
await interaction.editReply({ embeds: [getUpdatingEmbed(needsInstall)] });
// 4. Perform Update (Danger Zone)
await UpdateService.performUpdate(branch);
// 5. Trigger Restart (if we are still alive)
await UpdateService.triggerRestart();
} else {
await confirmation.update({
embeds: [getCancelledEmbed()],
components: []
});
}
} catch (e) {
if (e instanceof Error && e.message.includes("time")) {
await interaction.editReply({
embeds: [getTimeoutEmbed()],
components: []
});
} else {
throw e;
}
}
} catch (error) {
console.error("Update failed:", error);
await interaction.editReply({ embeds: [getErrorEmbed(error)] });
}
}
});