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 { parseDuration } from '../moderation/duration.js'; import { giveawayCommandData } from './command-definitions.js'; import { cancelGiveawayJob, deleteGiveaway, endGiveaway, GiveawayError, listActiveGiveaways, pauseGiveaway, rerollGiveaway, startGiveaway } from './service.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); } const giveawayCommand: SlashCommand = { data: giveawayCommandData, 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(); if (sub === 'start') { if (!(await ensureManageGuild(interaction, locale))) { return; } const durationInput = interaction.options.getString('duration', true); let durationMs: number; try { durationMs = parseDuration(durationInput); } catch { await interaction.reply({ content: t(locale, 'giveaway.error.invalid_duration'), ephemeral: true }); return; } if (durationMs < 60_000) { await interaction.reply({ content: t(locale, 'giveaway.error.duration_too_short'), ephemeral: true }); return; } const channel = interaction.channel; if (!channel?.isTextBased() || channel.isDMBased()) { await interaction.reply({ content: t(locale, 'giveaway.error.invalid_channel'), ephemeral: true }); return; } const giveaway = await startGiveaway( context, { guildId: interaction.guildId!, channelId: channel.id, hostId: interaction.user.id, prize: interaction.options.getString('prize', true), winnerCount: interaction.options.getInteger('winners', true), durationMs, requiredRoleId: interaction.options.getRole('role')?.id, requiredLevel: interaction.options.getInteger('level'), requiredMemberDays: interaction.options.getInteger('member_days') }, locale ); await interaction.reply({ content: tf(locale, 'giveaway.started', { id: giveaway.id }), ephemeral: true }); return; } if (!(await ensureManageGuild(interaction, locale))) { return; } try { if (sub === 'end') { const id = interaction.options.getString('id', true).trim(); const giveaway = await context.prisma.giveaway.findUnique({ where: { id } }); if (!giveaway || giveaway.guildId !== interaction.guildId) { throw new GiveawayError('not_found'); } await cancelGiveawayJob(id); await endGiveaway(context, id); await interaction.reply({ content: t(locale, 'giveaway.ended'), ephemeral: true }); return; } if (sub === 'reroll') { const id = interaction.options.getString('id', true).trim(); const existing = await context.prisma.giveaway.findUnique({ where: { id } }); if (!existing || existing.guildId !== interaction.guildId) { throw new GiveawayError('not_found'); } const updated = await rerollGiveaway(context, id, locale); await interaction.reply({ content: tf(locale, 'giveaway.rerolled', { winners: updated.winners.map((w) => `<@${w}>`).join(', ') || '—' }), ephemeral: true }); return; } if (sub === 'list') { const active = await listActiveGiveaways(context, interaction.guildId!); if (active.length === 0) { await interaction.reply({ content: t(locale, 'giveaway.list.empty'), ephemeral: true }); return; } const lines = active.map( (g) => `\`${g.id}\` — **${g.prize}** (${g.entrants.length} ${t(locale, 'giveaway.list.entrants')}, )` ); await interaction.reply({ content: `${t(locale, 'giveaway.list.header')}\n${lines.join('\n')}`, ephemeral: true }); return; } if (sub === 'delete') { const id = interaction.options.getString('id', true).trim(); const existing = await context.prisma.giveaway.findUnique({ where: { id } }); if (!existing || existing.guildId !== interaction.guildId) { throw new GiveawayError('not_found'); } await deleteGiveaway(context, id); await interaction.reply({ content: t(locale, 'giveaway.deleted'), ephemeral: true }); return; } if (sub === 'pause') { const id = interaction.options.getString('id', true).trim(); const existing = await context.prisma.giveaway.findUnique({ where: { id } }); if (!existing || existing.guildId !== interaction.guildId) { throw new GiveawayError('not_found'); } const updated = await pauseGiveaway(context, id, locale); await interaction.reply({ content: t(locale, updated.paused ? 'giveaway.paused' : 'giveaway.resumed'), ephemeral: true }); } } catch (error) { if (error instanceof GiveawayError) { await interaction.reply({ content: t(locale, `giveaway.error.${error.code}`), ephemeral: true }); return; } throw error; } } }; export const giveawayCommands = [giveawayCommand];