Initializes the Aethex Sentinel bot project, including package.json, Prisma schema, core client, configuration, health server, and event listeners for audit logs and member updates. Implements commands for federation management, sentinel security status, and ticket system. Replit-Commit-Author: Agent Replit-Commit-Session-Id: e72fc1b7-94bd-4d6c-801f-cbac2fae245c Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Event-Id: fc082e4d-cb52-4049-9c2e-69ba6c2e78d4 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3bdfff67-975a-46ad-9845-fbb6b4a4c4b5/e72fc1b7-94bd-4d6c-801f-cbac2fae245c/jW8PJKQ Replit-Helium-Checkpoint-Created: true
124 lines
4.1 KiB
TypeScript
124 lines
4.1 KiB
TypeScript
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<void> {
|
|
this.roleMappings = await prisma.roleMapping.findMany();
|
|
console.log(`📋 Loaded ${this.roleMappings.length} role mappings`);
|
|
}
|
|
|
|
async addMapping(mapping: Omit<RoleMapping, 'id'>): Promise<void> {
|
|
await prisma.roleMapping.create({
|
|
data: mapping,
|
|
});
|
|
await this.loadMappings();
|
|
}
|
|
|
|
async syncRoleGrant(member: GuildMember, role: Role): Promise<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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();
|