Skip to content
50 changes: 40 additions & 10 deletions apps/api/src/guild/guild.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ import { PlayerService } from "#player";
import { flatten } from "@statsify/util";
import type { ReturnModelType } from "@typegoose/typegoose";

type MemberName = Pick<Player, "username" | "displayName" | "guildId">;

const isNameCached = (cachedMember?: GuildMember): cachedMember is GuildMember =>
!!cachedMember && Date.now() < cachedMember.expiresAt;

@Injectable()
export class GuildService {
private readonly logger = new Logger("GuildService");
Expand Down Expand Up @@ -70,10 +75,16 @@ export class GuildService {
const guildExpHistory: Record<string, number> = {};
const requireGuildId: string[] = [];

const memberNames = await this.getMemberNames(
guild.members
.filter((member) => !isNameCached(memberMap[member.uuid]))
.map((member) => member.uuid)
);

const fetchMembers = guild.members.map(async (member) => {
const cacheMember = memberMap[member.uuid];

await this.getMemberName(member, cacheMember);
await this.getMemberName(member, cacheMember, memberNames.get(member.uuid));

// These members will need their player document updated with the correct guild id
if (member.guildId !== guild.id) {
Expand Down Expand Up @@ -208,22 +219,41 @@ export class GuildService {
return await this.guildModel.deleteOne({ id: cachedGuild.id }).lean().exec();
}

private async getMemberName(member: GuildMember, cachedMember?: GuildMember) {
if (cachedMember && Date.now() < cachedMember.expiresAt) {
private async getMemberNames(uuids: string[]): Promise<Map<string, MemberName>> {
if (uuids.length === 0) return new Map();

const players = await this.playerModel
.find({ uuid: { $in: uuids } })
.select({ uuid: true, username: true, displayName: true, guildId: true })
.lean()
.exec();

return new Map(players.map((player) => [player.uuid, player]));
}

private async getMemberName(
member: GuildMember,
cachedMember?: GuildMember,
memberName?: MemberName
) {
if (isNameCached(cachedMember)) {
member.displayName = cachedMember.displayName;
member.username = cachedMember.username;
member.expiresAt = cachedMember.expiresAt;
member.guildId = cachedMember.guildId;
return;
}

const player = await this.playerService
.get(member.uuid, CacheLevel.CACHE_ONLY, {
username: true,
displayName: true,
guildId: true,
})
.catch(() => null);
// CACHE_ONLY falls back to Hypixel for players Mongo doesn't have
const player =
memberName ??
(await this.playerService
.get(member.uuid, CacheLevel.CACHE_ONLY, {
username: true,
displayName: true,
guildId: true,
})
.catch(() => null));

if (player) {
member.username = player.username;
Expand Down
Loading