import { PermissionFlagsBits } from 'discord.js'; import { SocialFeedTypeSchema, t, tf } from '@nexumi/shared'; import type { SlashCommand } from '../../types.js'; import { getGuildLocale } from '../../i18n.js'; import { requirePermission } from '../../permissions.js'; import { feedsCommandData } from './command-definitions.js'; import { addFeed, FeedAddSchema, FeedError, getFeedById, listFeeds, removeFeed, testFeed } from './service.js'; const feedsCommand: SlashCommand = { data: feedsCommandData, 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(true); const guildId = interaction.guildId!; if (!(await requirePermission(interaction, PermissionFlagsBits.ManageGuild, locale))) { return; } try { if (sub === 'add') { const input = FeedAddSchema.parse({ type: SocialFeedTypeSchema.parse(interaction.options.getString('type', true)), source: interaction.options.getString('source', true), channelId: interaction.options.getChannel('channel', true).id, rolePingId: interaction.options.getRole('role')?.id, template: interaction.options.getString('template') ?? undefined }); const feed = await addFeed(context.prisma, guildId, input); await interaction.reply({ content: tf(locale, 'feeds.added', { id: feed.id, type: feed.type }), ephemeral: true }); return; } if (sub === 'remove') { const feedId = interaction.options.getString('id', true); const removed = await removeFeed(context.prisma, guildId, feedId); if (!removed) { await interaction.reply({ content: t(locale, 'feeds.error.not_found'), ephemeral: true }); return; } await interaction.reply({ content: tf(locale, 'feeds.removed', { id: removed.id }), ephemeral: true }); return; } if (sub === 'list') { const feeds = await listFeeds(context.prisma, guildId); if (feeds.length === 0) { await interaction.reply({ content: t(locale, 'feeds.list.empty'), ephemeral: true }); return; } const lines = feeds.map((feed) => tf(locale, 'feeds.list.line', { id: feed.id, type: feed.type, source: feed.sourceId, channel: `<#${feed.channelId}>` }) ); await interaction.reply({ content: `${t(locale, 'feeds.list.header')}\n${lines.join('\n')}`, ephemeral: true }); return; } if (sub === 'test') { const feedId = interaction.options.getString('id', true); const feed = await getFeedById(context.prisma, guildId, feedId); if (!feed) { await interaction.reply({ content: t(locale, 'feeds.error.not_found'), ephemeral: true }); return; } const result = await testFeed(context, feed); if (result.skipped) { const skipKey = result.message === 'twitch_credentials_missing' ? 'feeds.test.twitchSkipped' : 'feeds.test.skipped'; await interaction.reply({ content: t(locale, skipKey), ephemeral: true }); return; } if (result.message && result.posted === 0) { await interaction.reply({ content: tf(locale, 'feeds.test.failed', { reason: result.message }), ephemeral: true }); return; } if (result.posted === 0) { await interaction.reply({ content: t(locale, 'feeds.test.noItems'), ephemeral: true }); return; } await interaction.reply({ content: tf(locale, 'feeds.test.posted', { count: result.posted }), ephemeral: true }); } } catch (error) { if (error instanceof FeedError) { await interaction.reply({ content: t(locale, `feeds.error.${error.code}`), ephemeral: true }); return; } throw error; } } }; export const feedsCommands: SlashCommand[] = [feedsCommand];