import type { DiscordMemberProfile } from "@/lib/discord/discord"; import type { DiscordVoiceChannel } from "@/lib/leaderboard/discord"; import type { getMemberVoiceStats, getMemberXpStats, } from "@/lib/leaderboard/database"; import { buildLevelProgress, type LevelProgress } from "@/lib/leaderboard/level-progress"; export type GuildProfileXp = LevelProgress & { rank: number | null }; export type GuildProfileVoice = Awaited>; export type GuildProfileRead = { profile: DiscordMemberProfile | null; profileUnavailable: boolean; xp: GuildProfileXp | null; voice: GuildProfileVoice | null; }; export type GuildProfileSources = { getProfile: (discordId: string) => Promise; getXp: (discordId: string) => ReturnType; getVoice: ( discordId: string, currentChannelIds: string[], ) => ReturnType; getCurrentVoiceChannels: () => Promise; reportFailure?: (source: "profile" | "xp" | "voice", error: unknown) => void; }; function defaultReportFailure(source: string, error: unknown) { console.error(`Failed to load own Guild profile ${source} data`, error); } export function createGuildProfileReader( sources: GuildProfileSources, ) { return async function loadGuildProfile(discordId: string): Promise { const reportFailure = sources.reportFailure ?? defaultReportFailure; const [profileResult, xpResult, voiceResult] = await Promise.allSettled([ sources.getProfile(discordId), sources.getXp(discordId), sources.getCurrentVoiceChannels().then((channels) => sources.getVoice(discordId, channels.map((channel) => channel.id)), ), ]); if (profileResult.status === "rejected") { reportFailure("profile", profileResult.reason); } if (xpResult.status === "rejected") { reportFailure("xp", xpResult.reason); } if (voiceResult.status === "rejected") { reportFailure("voice", voiceResult.reason); } const profile = profileResult.status === "fulfilled" ? profileResult.value : null; const xpStats = xpResult.status === "fulfilled" ? xpResult.value : null; const voice = voiceResult.status === "fulfilled" ? voiceResult.value : null; return { profile, profileUnavailable: !profile, xp: xpStats ? { ...buildLevelProgress(xpStats.level, xpStats.totalXp), rank: xpStats.rank, } : null, voice, }; }; } export async function loadGuildProfile(discordId: string) { const [discord, leaderboardDiscord, leaderboardDatabase] = await Promise.all([ import("@/lib/discord/discord"), import("@/lib/leaderboard/discord"), import("@/lib/leaderboard/database"), ]); return createGuildProfileReader({ getProfile: discord.getDiscordMemberProfile, getXp: leaderboardDatabase.getMemberXpStats, getVoice: leaderboardDatabase.getMemberVoiceStats, getCurrentVoiceChannels: leaderboardDiscord.getCurrentDiscordVoiceChannels, })(discordId); }