const { Observer } = require("../../../interfaces"); const { Infraction } = require('../../../moderation/interfaces/'); class AuditLogObserver extends Observer { constructor(client) { super(client, { name: 'auditLog', priority: 0, disabled: false }); this.client = client; this.hooks = [ ['guildBanAdd', this.guildBanAdd.bind(this)], ['guildBanRemove', this.guildBanRemove.bind(this)], ['guildMemberRemove', this.guildMemberRemove.bind(this)], ['guildMemberUpdate', this.guildMemberUpdate.bind(this)] ]; } async guildBanAdd(guild, user) { const settings = await guild.settings(); if(!settings.moderationLog.channel || !settings.moderationLog.infractions.includes('BAN')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier. const audit = await this._fetchFirstEntry(guild, user, 'MEMBER_BAN_ADD'); new Infraction(this.client, { type: 'BAN', targetType: 'USER', guild, target: user, executor: audit.executor, reason: `${audit.reason || 'N/A'}\n*This action was performed without the bot.*` }).handle(); } async guildBanRemove(guild, user) { const settings = await guild.settings(); if(!settings.moderationLog.channel || !settings.moderationLog.infractions.includes('UNBAN')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier. const audit = await this._fetchFirstEntry(guild, user, 'MEMBER_BAN_REMOVE'); new Infraction(this.client, { type: 'UNBAN', targetType: 'USER', guild, target: user, executor: audit.executor, reason: `*This action was performed without the bot.*` }).handle(); } async guildMemberRemove(member) { const settings = await member.guild.settings(); if(!settings.moderationLog.channel || !settings.moderationLog.infractions.includes('KICK')) return undefined; //This is checked by the infraction handling, but it may save resources if checked earlier. const audit = await this._fetchFirstEntry(member.guild, member.user, 'MEMBER_KICK'); new Infraction(this.client, { type: 'KICK', targetType: 'USER', guild: member.guild, target: member.user, executor: audit.executor, reason: `${audit.reason || 'N/A'}\n*This action was performed without the bot.*` }).handle(); } async guildMemberUpdate(oldMember, newMember) { if(oldMember.roles.cache.size === newMember.roles.cache.size) return undefined; const { guild } = newMember; const settings = await guild.settings(); if(!settings.moderationLog.channel) return undefined; const mutedRole = settings.mute.role; if(!mutedRole) return undefined; if(!oldMember.roles.cache.has(mutedRole) && newMember.roles.cache.has(mutedRole)) { //Member was assigned muted role. if(!settings.moderationLog.infractions.includes('MUTE')) return undefined; const audit = await this._fetchFirstEntry(newMember.guild, newMember.user, 'MEMBER_ROLE_UPDATE'); new Infraction(this.client, { type: 'MUTE', targetType: 'USER', guild: newMember.guild, target: newMember.user, executor: audit.executor, reason: "*This action was performed without the bot.*" }).handle(); } else if(oldMember.roles.cache.has(mutedRole) && !newMember.roles.cache.has(mutedRole)) { //Member was unassigned muted role. if(!settings.moderationLog.infractions.includes('UNMUTE')) return undefined; const audit = await this._fetchFirstEntry(newMember.guild, newMember.user, 'MEMBER_ROLE_UPDATE'); new Infraction(this.client, { type: 'UNMUTE', targetType: 'USER', guild: newMember.guild, target: newMember.user, executor: audit.executor, reason: "*This action was performed without the bot.*" }).handle(); } } async _fetchFirstEntry(guild, user, type) { if(!guild.me.hasPermission('VIEW_AUDIT_LOG')) return null; const audit = await guild.fetchAuditLogs({ limit: 1 }); if(audit.entries.size === 0) return null; const entry = audit.entries.filter((e) => e?.target?.id === user.id).first(); if(!entry || entry.executor.id === this.client.user.id) return null; if(entry.target.id !== user.id) return null; if(entry.action !== type) return null; return entry; } } module.exports = AuditLogObserver;