import type { DiscordChannelOption, DiscordRoleOption } from '@nexumi/shared'; import { DASHBOARD_CHANNEL_TYPES } from './discord-channel-types'; import { env } from './env'; import { redis } from './redis'; const DISCORD_API_BASE = 'https://discord.com/api/v10'; const CACHE_TTL_SECONDS = 60; export async function discordBotFetch( path: string, init?: { method?: string; body?: unknown } ): Promise { const method = init?.method ?? 'GET'; const response = await fetch(`${DISCORD_API_BASE}${path}`, { method, headers: { Authorization: `Bot ${env.BOT_TOKEN}`, ...(init?.body !== undefined ? { 'Content-Type': 'application/json' } : {}) }, body: init?.body !== undefined ? JSON.stringify(init.body) : undefined, cache: 'no-store' }); if (!response.ok) { throw new Error(`Discord API request to ${path} failed with status ${response.status}`); } if (response.status === 204) { return undefined as T; } 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:v2`; 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) => DASHBOARD_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; } export { CHANNEL_TYPES, channelPrefix, type ChannelKind } from './discord-channel-types';