import { type APIGuildMember, type APIUser } from "discord-api-types/v10"; import type { GuildMember, User } from "discord.js"; import config from "@/config"; import type { Ticket } from "@/database/schema"; import client from "@/discord"; import _logger from "@/utils/logging"; const logger = _logger.child({ name: "server" }); export type UserProfile = APIGuildMember | { user: APIUser }; export const cache = new Map(); function mapUser(user: User): APIUser { return { id: user.id, username: user.username, discriminator: user.discriminator, global_name: user.globalName, avatar: user.avatar, bot: user.bot, system: user.system, banner: user.banner, accent_color: user.accentColor, public_flags: user.flags?.bitfield, } as APIUser; } function mapMember(member: GuildMember): APIGuildMember { return { user: mapUser(member.user), nick: member.nickname, avatar: member.avatar, roles: Array.from(member.roles.cache.keys()), joined_at: member.joinedAt?.toISOString() || new Date().toISOString(), deaf: member.voice.serverDeaf || false, mute: member.voice.serverMute || false, flags: member.flags.bitfield, pending: member.pending, premium_since: member.premiumSince?.toISOString() || null, communication_disabled_until: member.communicationDisabledUntil?.toISOString() || null, } as APIGuildMember; } export async function getUsers(ids: string[]): Promise { const uniqueIds = [...new Set(ids.filter(Boolean))]; const missingIds = uniqueIds.filter((id) => id !== "system" && !cache.has(id)); if (missingIds.length === 0) return; logger.debug(missingIds, `fetching ${missingIds.length} users`); const guild = client.guilds.cache.get(config.discord.guild); if (guild) { try { const members = await guild.members.fetch({ user: missingIds }); members.forEach((member) => cache.set(member.id, mapMember(member))); } catch (err) { console.error("failed to fetch guild members chunk:", err); } } const stillMissing = missingIds.filter((id) => !cache.has(id)); if (stillMissing.length > 0) { await Promise.all( stillMissing.map(async (id) => { try { const user = await client.users.fetch(id); cache.set(id, { user: mapUser(user) }); } catch { cache.set(id, null); } }), ); } } export async function getUser(id: string): Promise { await getUsers([id]); return cache.get(id) || null; } export async function hydrateTickets(allTickets: Ticket[]) { const allIds = new Set(); for (const ticket of allTickets) { if (ticket.authorId) allIds.add(ticket.authorId); if (ticket.openedBy) allIds.add(ticket.openedBy); if (ticket.claimedBy) allIds.add(ticket.claimedBy); if (ticket.closedBy && ticket.closedBy !== "reporter") allIds.add(ticket.closedBy); } await getUsers(Array.from(allIds)); return allTickets.map((ticket) => ({ ...ticket, authorId: ticket.authorId ? cache.get(ticket.authorId) : null, openedBy: ticket.openedBy ? cache.get(ticket.openedBy) : null, claimedBy: ticket.claimedBy ? cache.get(ticket.claimedBy) : null, closedBy: ticket.closedBy && ticket.closedBy !== "reporter" ? cache.get(ticket.closedBy) : null, })); }