import { Guild, GuildMember, Role } from 'discord.js'; import { prisma } from '../../core/client'; import { config } from '../../core/config'; interface RoleMapping { sourceGuild: string; sourceRole: string; targetGuild: string; targetRole: string; roleName: string; } export class FederationManager { private roleMappings: RoleMapping[] = []; async loadMappings(): Promise { this.roleMappings = await prisma.roleMapping.findMany(); console.log(`📋 Loaded ${this.roleMappings.length} role mappings`); } async addMapping(mapping: Omit): Promise { await prisma.roleMapping.create({ data: mapping, }); await this.loadMappings(); } async syncRoleGrant(member: GuildMember, role: Role): Promise { const sourceGuildId = member.guild.id; const mappings = this.roleMappings.filter( m => m.sourceGuild === sourceGuildId && m.sourceRole === role.id ); if (mappings.length === 0) return; console.log(`🔄 Syncing role "${role.name}" for ${member.user.tag} to ${mappings.length} guild(s)`); for (const mapping of mappings) { try { const targetGuild = member.client.guilds.cache.get(mapping.targetGuild); if (!targetGuild) { console.warn(`⚠️ Target guild ${mapping.targetGuild} not found`); continue; } const targetMember = await targetGuild.members.fetch(member.id).catch(() => null); if (!targetMember) { console.warn(`⚠️ User ${member.user.tag} not in target guild ${targetGuild.name}`); continue; } const targetRole = targetGuild.roles.cache.get(mapping.targetRole); if (!targetRole) { console.warn(`⚠️ Target role ${mapping.targetRole} not found in ${targetGuild.name}`); continue; } if (!targetMember.roles.cache.has(targetRole.id)) { await targetMember.roles.add(targetRole, `Federation sync from ${member.guild.name}`); console.log(`✅ Granted "${targetRole.name}" to ${member.user.tag} in ${targetGuild.name}`); } } catch (error) { console.error(`❌ Failed to sync role to guild ${mapping.targetGuild}:`, error); } } await this.updateUserRoles(member.id); } async syncRoleRemove(member: GuildMember, role: Role): Promise { const sourceGuildId = member.guild.id; const mappings = this.roleMappings.filter( m => m.sourceGuild === sourceGuildId && m.sourceRole === role.id ); if (mappings.length === 0) return; console.log(`🔄 Removing synced role "${role.name}" for ${member.user.tag} from ${mappings.length} guild(s)`); for (const mapping of mappings) { try { const targetGuild = member.client.guilds.cache.get(mapping.targetGuild); if (!targetGuild) continue; const targetMember = await targetGuild.members.fetch(member.id).catch(() => null); if (!targetMember) continue; const targetRole = targetGuild.roles.cache.get(mapping.targetRole); if (!targetRole) continue; if (targetMember.roles.cache.has(targetRole.id)) { await targetMember.roles.remove(targetRole, `Federation sync removal from ${member.guild.name}`); console.log(`✅ Removed "${targetRole.name}" from ${member.user.tag} in ${targetGuild.name}`); } } catch (error) { console.error(`❌ Failed to remove synced role:`, error); } } await this.updateUserRoles(member.id); } private async updateUserRoles(userId: string): Promise { const syncedRoles = this.roleMappings .filter(m => m.sourceGuild === config.guilds.hub) .map(m => m.roleName); await prisma.user.upsert({ where: { id: userId }, update: { roles: syncedRoles }, create: { id: userId, roles: syncedRoles }, }); } getGuildType(guildId: string): string | null { const entries = Object.entries(config.guilds); const found = entries.find(([, id]) => id === guildId); return found ? found[0] : null; } } export const federationManager = new FederationManager();