src/server/discord.ts (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
import type { APIGuildMember, 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<string, UserProfile | null>();
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<void> {
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<UserProfile | null> {
await getUsers([id]);
return cache.get(id) || null;
}
export async function hydrateTickets(allTickets: Ticket[]) {
const allIds = new Set<string>();
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,
}));
}
|