import "server-only"; import { getDiscordServerConfig } from "@/lib/config/server"; export type DiscordVoiceChannel = { id: string; name: string; type: 2 | 13; position: number; }; type DiscordChannelPayload = { id?: unknown; name?: unknown; type?: unknown; position?: unknown; }; const CHANNEL_CACHE_TTL_MS = 5 * 60_000; const channelCache: { data: DiscordVoiceChannel[] | null; expiresAt: number; } = { data: null, expiresAt: 0, }; export function parseCurrentVoiceChannels(payload: unknown): DiscordVoiceChannel[] { if (!Array.isArray(payload)) { throw new Error("Discord returned an invalid channel list."); } return payload .filter((channel): channel is DiscordChannelPayload => { if (!channel || typeof channel !== "object") return false; const type = (channel as DiscordChannelPayload).type; return type === 2 || type === 13; }) .filter((channel) => ( typeof channel.id === "string" && typeof channel.name === "string" )) .map((channel) => ({ id: channel.id as string, name: channel.name as string, type: channel.type as 2 | 13, position: typeof channel.position === "number" ? channel.position : 0, })) .sort((a, b) => a.position - b.position || a.name.localeCompare(b.name, "th")); } export async function getCurrentDiscordVoiceChannels() { if (channelCache.data && Date.now() < channelCache.expiresAt) { return channelCache.data; } const { token, guildId } = getDiscordServerConfig(); const response = await fetch( `https://discord.com/api/v10/guilds/${guildId}/channels`, { headers: { Authorization: `Bot ${token}` }, cache: "no-store", signal: AbortSignal.timeout(5_000), }, ); if (!response.ok) { throw new Error(`Discord channel request failed with status ${response.status}.`); } const channels = parseCurrentVoiceChannels(await response.json()); channelCache.data = channels; channelCache.expiresAt = Date.now() + CHANNEL_CACHE_TTL_MS; return channels; }