all repos — stealth-developers @ f1905f7477593bc2b93f7d27e474d67e8ec02eba

pkgs/bot/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
import config from "@/config";
import type { Ticket } from "@/database/schema";
import client from "@/discord";
import { marshallIntoApiUser } from "./api";
import _logger from "@/utils/logging";
import type { User as UserProfile, Ticket as ApiTicket } from "@stealth-developers/api";

const logger = _logger.child({ name: "server" });

export const cache = new Map<string, UserProfile | null>();

export async function getUsers(ids: (string | null | undefined)[]): Promise<void> {
	const uniqueIds = [...new Set(ids.filter(Boolean) as string[])];
	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, marshallIntoApiUser(member)));
		} catch (err) {
			logger.error({ err }, "failed to fetch guild members chunk");
		}
	}

	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, marshallIntoApiUser(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[]): Promise<ApiTicket[]> {
	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.closedBy && ticket.closedBy !== "reporter") allIds.add(ticket.closedBy);
	}

	await getUsers([...allIds]);

	return allTickets.map((ticket) => {
		const fallbackSubject: UserProfile = {
			id: ticket.authorId,
			name: "Subject",
			avatarUrl: "",
			isModerator: false,
		};

		return {
			number: String(ticket.id),
			status: ticket.status as "open" | "closed" | "archived",

			closedBy: ticket.closedBy ? (cache.get(ticket.closedBy) ?? null) : null,
			closedAt: ticket.closedAt?.toUTCString() || null,

			topic: ticket.topic,
			closeReason: ticket.closeReason,
			privateReason: ticket.privateReason,

			openedAt: ticket.createdAt?.toUTCString(),
			openedBy: ticket.openedBy ? (cache.get(ticket.openedBy) ?? null) : null,
			subject: cache.get(ticket.authorId) ?? fallbackSubject,
		};
	});
}