89 lines
3.0 KiB
TypeScript
89 lines
3.0 KiB
TypeScript
import { Client, Guild, Invite, GuildMember } from 'discord.js';
|
|
import { redis } from '../cache';
|
|
import { prisma } from '../database';
|
|
import { logger } from '../utils/logger';
|
|
|
|
export class InviteService {
|
|
public static async cacheAllInvites(client: Client) {
|
|
for (const [, guild] of client.guilds.cache) {
|
|
await this.cacheGuildInvites(guild);
|
|
}
|
|
logger.info('InviteMaster: Finished caching all invites.');
|
|
}
|
|
|
|
public static async cacheGuildInvites(guild: Guild) {
|
|
try {
|
|
const invites = await guild.invites.fetch();
|
|
const inviteData = invites.map(inv => ({
|
|
code: inv.code,
|
|
uses: inv.uses || 0
|
|
}));
|
|
await redis.set(`invites:${guild.id}`, JSON.stringify(inviteData));
|
|
} catch (error) {
|
|
logger.error(`InviteMaster: Failed to cache invites for guild ${guild.id}:`, error);
|
|
}
|
|
}
|
|
|
|
public static async handleInviteCreate(invite: Invite) {
|
|
if (!invite.guild) return;
|
|
logger.debug(`InviteMaster: New invite created: ${invite.code}`);
|
|
await this.cacheGuildInvites(invite.guild as Guild);
|
|
}
|
|
|
|
public static async handleInviteDelete(invite: Invite) {
|
|
if (!invite.guild) return;
|
|
logger.debug(`InviteMaster: Invite deleted: ${invite.code}`);
|
|
await this.cacheGuildInvites(invite.guild as Guild);
|
|
}
|
|
|
|
public static async handleMemberAdd(member: GuildMember) {
|
|
const guild = member.guild;
|
|
try {
|
|
// Fetch current active invites
|
|
const newInvites = await guild.invites.fetch();
|
|
const cachedData = await redis.get(`invites:${guild.id}`);
|
|
|
|
let usedInvite: Invite | undefined;
|
|
|
|
if (cachedData) {
|
|
const cachedInvites: { code: string, uses: number }[] = JSON.parse(cachedData);
|
|
|
|
// Find the invite where 'uses' has increased
|
|
usedInvite = newInvites.find(inv => {
|
|
const cached = cachedInvites.find(c => c.code === inv.code);
|
|
return cached ? (inv.uses || 0) > cached.uses : false;
|
|
});
|
|
}
|
|
|
|
// Update the cache immediately to account for this new join
|
|
await this.cacheGuildInvites(guild);
|
|
|
|
if (usedInvite) {
|
|
logger.info(`InviteMaster: ${member.user.tag} joined using invite ${usedInvite.code}`);
|
|
|
|
// Check DB for mapped role
|
|
const inviteRole = await prisma.inviteRole.findFirst({
|
|
where: {
|
|
guildId: guild.id,
|
|
inviteCode: usedInvite.code
|
|
}
|
|
});
|
|
|
|
if (inviteRole) {
|
|
const role = guild.roles.cache.get(inviteRole.roleId);
|
|
if (role) {
|
|
await member.roles.add(role);
|
|
logger.info(`InviteMaster: Assigned role ${role.name} to ${member.user.tag}`);
|
|
} else {
|
|
logger.warn(`InviteMaster: Role ${inviteRole.roleId} mapped to invite ${usedInvite.code} not found.`);
|
|
}
|
|
}
|
|
} else {
|
|
logger.info(`InviteMaster: ${member.user.tag} joined but invite could not be determined (ex: Vanity URL).`);
|
|
}
|
|
} catch (error) {
|
|
logger.error(`InviteMaster: Failed to handle member add tracking:`, error);
|
|
}
|
|
}
|
|
}
|