import type { DiscordChannelOption, DiscordRoleOption } from '@nexumi/shared'; import { env } from './env'; import { redis } from './redis'; const DISCORD_API_BASE = 'https://discord.com/api/v10'; const CACHE_TTL_SECONDS = 60; /** Discord channel types usable for slash commands. */ const COMMAND_CHANNEL_TYPES = new Set([ 0, // GUILD_TEXT 5, // GUILD_ANNOUNCEMENT 10, // ANNOUNCEMENT_THREAD 11, // PUBLIC_THREAD 12, // PRIVATE_THREAD 15, // GUILD_FORUM 16 // GUILD_MEDIA ]); async function discordBotFetch(path: string): Promise { const response = await fetch(`${DISCORD_API_BASE}${path}`, { headers: { Authorization: `Bot ${env.BOT_TOKEN}` }, cache: 'no-store' }); if (!response.ok) { throw new Error(`Discord API request to ${path} failed with status ${response.status}`); } return (await response.json()) as T; } interface DiscordChannelRest { id: string; name: string; type: number; parent_id?: string | null; position?: number; } interface DiscordRoleRest { id: string; name: string; color: number; position: number; managed?: boolean; } export async function listGuildChannelsForDashboard(guildId: string): Promise { const cacheKey = `dashboard:guild:${guildId}:channels`; const cached = await redis.get(cacheKey); if (cached) { return JSON.parse(cached) as DiscordChannelOption[]; } const channels = await discordBotFetch(`/guilds/${guildId}/channels`); const options = channels .filter((channel) => COMMAND_CHANNEL_TYPES.has(channel.type)) .map((channel) => ({ id: channel.id, name: channel.name, type: channel.type, parentId: channel.parent_id ?? null })) .sort((a, b) => a.name.localeCompare(b.name)); await redis.set(cacheKey, JSON.stringify(options), 'EX', CACHE_TTL_SECONDS); return options; } export async function listGuildRolesForDashboard(guildId: string): Promise { const cacheKey = `dashboard:guild:${guildId}:roles`; const cached = await redis.get(cacheKey); if (cached) { return JSON.parse(cached) as DiscordRoleOption[]; } const roles = await discordBotFetch(`/guilds/${guildId}/roles`); const options = roles .filter((role) => role.name !== '@everyone') .map((role) => ({ id: role.id, name: role.name, color: role.color, position: role.position })) .sort((a, b) => b.position - a.position); await redis.set(cacheKey, JSON.stringify(options), 'EX', CACHE_TTL_SECONDS); return options; }