import { PermissionFlagsBits } from 'discord.js'; import { t, tf } from '@nexumi/shared'; import type { SlashCommand } from '../../types.js'; import { getGuildLocale } from '../../i18n.js'; import { requirePermission } from '../../permissions.js'; import { backupCommandData } from './command-definitions.js'; import { createGuildBackup, ensureBotCanRestore, getGuildBackup, GuildBackupError, listGuildBackups, parseBackupPayload } from './service.js'; import { promptDeleteConfirmation, promptRestoreConfirmation } from './confirmations.js'; async function ensureManageGuild( interaction: Parameters[0], locale: 'de' | 'en' ): Promise { if (!interaction.inGuild()) { await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true }); return false; } return requirePermission(interaction, PermissionFlagsBits.ManageGuild, locale); } function defaultBackupName(): string { return `backup-${new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)}`; } const backupCommand: SlashCommand = { data: backupCommandData, async execute(interaction, context) { const locale = await getGuildLocale(context.prisma, interaction.guildId); if (!interaction.inGuild()) { await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true }); return; } const sub = interaction.options.getSubcommand(); const guild = interaction.guild!; if (sub === 'create') { if (!(await ensureManageGuild(interaction, locale))) { return; } const name = interaction.options.getString('name')?.trim() || defaultBackupName(); if (name.length < 1 || name.length > 100) { await interaction.reply({ content: t(locale, 'guildbackup.error.invalid_name'), ephemeral: true }); return; } await interaction.deferReply({ ephemeral: true }); try { const backup = await createGuildBackup(context, guild, name, interaction.user.id); await interaction.editReply({ content: tf(locale, 'guildbackup.created', { id: backup.id, name: backup.name }) }); } catch (error) { if (error instanceof GuildBackupError) { await interaction.editReply({ content: t(locale, `guildbackup.error.${error.code}`) }); return; } throw error; } return; } if (sub === 'list') { if (!(await ensureManageGuild(interaction, locale))) { return; } const backups = await listGuildBackups(context, guild.id); if (backups.length === 0) { await interaction.reply({ content: t(locale, 'guildbackup.list.empty'), ephemeral: true }); return; } const lines = backups.map((backup) => tf(locale, 'guildbackup.list.line', { id: backup.id, name: backup.name, date: backup.createdAt.toISOString().slice(0, 10) }) ); await interaction.reply({ content: `${t(locale, 'guildbackup.list.header')}\n${lines.join('\n')}`, ephemeral: true }); return; } if (sub === 'info') { if (!(await ensureManageGuild(interaction, locale))) { return; } const backupId = interaction.options.getString('id', true); const backup = await getGuildBackup(context, guild.id, backupId); if (!backup) { await interaction.reply({ content: t(locale, 'guildbackup.error.not_found'), ephemeral: true }); return; } let roleCount = 0; let channelCount = 0; try { const payload = parseBackupPayload(backup.payload); roleCount = payload.roles.length; channelCount = payload.channels.length; } catch { await interaction.reply({ content: t(locale, 'guildbackup.error.invalid_payload'), ephemeral: true }); return; } await interaction.reply({ content: [ tf(locale, 'guildbackup.info.id', { id: backup.id }), tf(locale, 'guildbackup.info.name', { name: backup.name }), tf(locale, 'guildbackup.info.created', { date: backup.createdAt.toISOString() }), tf(locale, 'guildbackup.info.createdBy', { userId: backup.createdById }), tf(locale, 'guildbackup.info.roles', { count: roleCount }), tf(locale, 'guildbackup.info.channels', { count: channelCount }), t(locale, 'guildbackup.info.limitations') ].join('\n'), ephemeral: true }); return; } if (sub === 'delete') { if (!(await ensureManageGuild(interaction, locale))) { return; } const backupId = interaction.options.getString('id', true); const backup = await getGuildBackup(context, guild.id, backupId); if (!backup) { await interaction.reply({ content: t(locale, 'guildbackup.error.not_found'), ephemeral: true }); return; } await promptDeleteConfirmation(interaction, context, locale, backupId); return; } if (sub === 'restore') { if (guild.ownerId !== interaction.user.id) { await interaction.reply({ content: t(locale, 'guildbackup.error.owner_only'), ephemeral: true }); return; } if (!ensureBotCanRestore(guild)) { await interaction.reply({ content: t(locale, 'guildbackup.error.bot_missing_permissions'), ephemeral: true }); return; } const backupId = interaction.options.getString('id', true); const backup = await getGuildBackup(context, guild.id, backupId); if (!backup) { await interaction.reply({ content: t(locale, 'guildbackup.error.not_found'), ephemeral: true }); return; } try { parseBackupPayload(backup.payload); } catch { await interaction.reply({ content: t(locale, 'guildbackup.error.invalid_payload'), ephemeral: true }); return; } await promptRestoreConfirmation(interaction, context, locale, backupId); } } }; export const guildBackupCommands = [backupCommand];