diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 6d543efeaad4..2620aee410d5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -120,7 +120,7 @@ body: - No Intents - Guilds - GuildMembers - - GuildBans + - GuildModeration - GuildEmojisAndStickers - GuildIntegrations - GuildWebhooks diff --git a/packages/discord.js/src/client/actions/ActionsManager.js b/packages/discord.js/src/client/actions/ActionsManager.js index bcb9f2744ed2..3c8e2d549fec 100644 --- a/packages/discord.js/src/client/actions/ActionsManager.js +++ b/packages/discord.js/src/client/actions/ActionsManager.js @@ -12,6 +12,7 @@ class ActionsManager { this.register(require('./ChannelCreate')); this.register(require('./ChannelDelete')); this.register(require('./ChannelUpdate')); + this.register(require('./GuildAuditLogEntryCreate')); this.register(require('./GuildBanAdd')); this.register(require('./GuildBanRemove')); this.register(require('./GuildChannelsPositionUpdate')); diff --git a/packages/discord.js/src/client/actions/GuildAuditLogEntryCreate.js b/packages/discord.js/src/client/actions/GuildAuditLogEntryCreate.js new file mode 100644 index 000000000000..fa16de60b88e --- /dev/null +++ b/packages/discord.js/src/client/actions/GuildAuditLogEntryCreate.js @@ -0,0 +1,29 @@ +'use strict'; + +const Action = require('./Action'); +const GuildAuditLogsEntry = require('../../structures/GuildAuditLogsEntry'); +const Events = require('../../util/Events'); + +class GuildAuditLogEntryCreateAction extends Action { + handle(data) { + const client = this.client; + const guild = client.guilds.cache.get(data.guild_id); + let auditLogEntry; + + if (guild) { + auditLogEntry = new GuildAuditLogsEntry(guild, data); + + /** + * Emitted whenever a guild audit log entry is created. + * @event Client#guildAuditLogEntryCreate + * @param {GuildAuditLogsEntry} auditLogEntry The entry that was created + * @param {Guild} guild The guild where the entry was created + */ + client.emit(Events.GuildAuditLogEntryCreate, auditLogEntry, guild); + } + + return { auditLogEntry }; + } +} + +module.exports = GuildAuditLogEntryCreateAction; diff --git a/packages/discord.js/src/client/websocket/handlers/GUILD_AUDIT_LOG_ENTRY_CREATE.js b/packages/discord.js/src/client/websocket/handlers/GUILD_AUDIT_LOG_ENTRY_CREATE.js new file mode 100644 index 000000000000..8623141669eb --- /dev/null +++ b/packages/discord.js/src/client/websocket/handlers/GUILD_AUDIT_LOG_ENTRY_CREATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.GuildAuditLogEntryCreate.handle(packet.d); +}; diff --git a/packages/discord.js/src/client/websocket/handlers/index.js b/packages/discord.js/src/client/websocket/handlers/index.js index ee90befafe7d..f175dbe22d9b 100644 --- a/packages/discord.js/src/client/websocket/handlers/index.js +++ b/packages/discord.js/src/client/websocket/handlers/index.js @@ -10,6 +10,7 @@ const handlers = Object.fromEntries([ ['CHANNEL_DELETE', require('./CHANNEL_DELETE')], ['CHANNEL_PINS_UPDATE', require('./CHANNEL_PINS_UPDATE')], ['CHANNEL_UPDATE', require('./CHANNEL_UPDATE')], + ['GUILD_AUDIT_LOG_ENTRY_CREATE', require('./GUILD_AUDIT_LOG_ENTRY_CREATE')], ['GUILD_BAN_ADD', require('./GUILD_BAN_ADD')], ['GUILD_BAN_REMOVE', require('./GUILD_BAN_REMOVE')], ['GUILD_CREATE', require('./GUILD_CREATE')], diff --git a/packages/discord.js/src/managers/GuildBanManager.js b/packages/discord.js/src/managers/GuildBanManager.js index 995780a6cdee..a07fd7e6e39a 100644 --- a/packages/discord.js/src/managers/GuildBanManager.js +++ b/packages/discord.js/src/managers/GuildBanManager.js @@ -12,7 +12,7 @@ const { GuildMember } = require('../structures/GuildMember'); let deprecationEmittedForDeleteMessageDays = false; /** - * Manages API methods for GuildBans and stores their cache. + * Manages API methods for guild bans and stores their cache. * @extends {CachedManager} */ class GuildBanManager extends CachedManager { diff --git a/packages/discord.js/src/structures/GuildAuditLogs.js b/packages/discord.js/src/structures/GuildAuditLogs.js index e4ddfe0ab726..2ce13a8da312 100644 --- a/packages/discord.js/src/structures/GuildAuditLogs.js +++ b/packages/discord.js/src/structures/GuildAuditLogs.js @@ -78,7 +78,7 @@ class GuildAuditLogs { */ this.entries = new Collection(); for (const item of data.audit_log_entries) { - const entry = new GuildAuditLogsEntry(this, guild, item); + const entry = new GuildAuditLogsEntry(guild, item, this); this.entries.set(entry.id, entry); } } diff --git a/packages/discord.js/src/structures/GuildAuditLogsEntry.js b/packages/discord.js/src/structures/GuildAuditLogsEntry.js index 42a468744c00..87c9734dcd6a 100644 --- a/packages/discord.js/src/structures/GuildAuditLogsEntry.js +++ b/packages/discord.js/src/structures/GuildAuditLogsEntry.js @@ -94,7 +94,7 @@ class GuildAuditLogsEntry { */ static Targets = Targets; - constructor(logs, guild, data) { + constructor(guild, data, logs) { /** * The target type of this entry * @type {AuditLogTargetType} @@ -120,6 +120,12 @@ class GuildAuditLogsEntry { */ this.reason = data.reason ?? null; + /** + * The id of the user that executed this entry + * @type {?Snowflake} + */ + this.executorId = data.user_id; + /** * The user that executed this entry * @type {?User} @@ -127,7 +133,7 @@ class GuildAuditLogsEntry { this.executor = data.user_id ? guild.client.options.partials.includes(Partials.User) ? guild.client.users._add({ id: data.user_id }) - : guild.client.users.cache.get(data.user_id) + : guild.client.users.cache.get(data.user_id) ?? null : null; /** @@ -239,6 +245,12 @@ class GuildAuditLogsEntry { break; } + /** + * The id of the target of this entry + * @type {?Snowflake} + */ + this.targetId = data.target_id; + /** * The target of this entry * @type {?AuditLogEntryTarget} @@ -254,12 +266,12 @@ class GuildAuditLogsEntry { } else if (targetType === Targets.User && data.target_id) { this.target = guild.client.options.partials.includes(Partials.User) ? guild.client.users._add({ id: data.target_id }) - : guild.client.users.cache.get(data.target_id); + : guild.client.users.cache.get(data.target_id) ?? null; } else if (targetType === Targets.Guild) { this.target = guild.client.guilds.cache.get(data.target_id); } else if (targetType === Targets.Webhook) { this.target = - logs.webhooks.get(data.target_id) ?? + logs?.webhooks.get(data.target_id) ?? new Webhook( guild.client, this.changes.reduce( @@ -294,10 +306,10 @@ class GuildAuditLogsEntry { this.target = data.action_type === AuditLogEvent.MessageBulkDelete ? guild.channels.cache.get(data.target_id) ?? { id: data.target_id } - : guild.client.users.cache.get(data.target_id); + : guild.client.users.cache.get(data.target_id) ?? null; } else if (targetType === Targets.Integration) { this.target = - logs.integrations.get(data.target_id) ?? + logs?.integrations.get(data.target_id) ?? new Integration( guild.client, this.changes.reduce( @@ -363,7 +375,7 @@ class GuildAuditLogsEntry { ), ); } else if (targetType === Targets.ApplicationCommand) { - this.target = logs.applicationCommands.get(data.target_id) ?? { id: data.target_id }; + this.target = logs?.applicationCommands.get(data.target_id) ?? { id: data.target_id }; } else if (targetType === Targets.AutoModeration) { this.target = guild.autoModerationRules.cache.get(data.target_id) ?? diff --git a/packages/discord.js/src/util/Events.js b/packages/discord.js/src/util/Events.js index 630034a10af5..de2516ad3825 100644 --- a/packages/discord.js/src/util/Events.js +++ b/packages/discord.js/src/util/Events.js @@ -11,6 +11,7 @@ * @property {string} ClientReady ready * @property {string} Debug debug * @property {string} Error error + * @property {string} GuildAuditLogEntryCreate guildAuditLogEntryCreate * @property {string} GuildBanAdd guildBanAdd * @property {string} GuildBanRemove guildBanRemove * @property {string} GuildCreate guildCreate @@ -91,6 +92,7 @@ module.exports = { ClientReady: 'ready', Debug: 'debug', Error: 'error', + GuildAuditLogEntryCreate: 'guildAuditLogEntryCreate', GuildBanAdd: 'guildBanAdd', GuildBanRemove: 'guildBanRemove', GuildCreate: 'guildCreate', diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 086b551aae9f..d922372d7567 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -1387,19 +1387,21 @@ export class GuildAuditLogsEntry< : GuildAuditLogsTargetType, TResolvedType = TAction extends null ? AuditLogEvent : TAction, > { - private constructor(logs: GuildAuditLogs, guild: Guild, data: RawGuildAuditLogEntryData); + private constructor(guild: Guild, data: RawGuildAuditLogEntryData, logs?: GuildAuditLogs); public static Targets: GuildAuditLogsTargets; public action: TResolvedType; public actionType: TActionType; public changes: AuditLogChange[]; public get createdAt(): Date; public get createdTimestamp(): number; + public executorId: Snowflake | null; public executor: User | null; public extra: TResolvedType extends keyof GuildAuditLogsEntryExtraField ? GuildAuditLogsEntryExtraField[TResolvedType] : null; public id: Snowflake; public reason: string | null; + public targetId: Snowflake | null; public target: TTargetType extends keyof GuildAuditLogsEntryTargetField ? GuildAuditLogsEntryTargetField[TTargetType] : Role | GuildEmoji | { id: Snowflake } | null; @@ -4691,6 +4693,7 @@ export interface ClientEvents { emojiDelete: [emoji: GuildEmoji]; emojiUpdate: [oldEmoji: GuildEmoji, newEmoji: GuildEmoji]; error: [error: Error]; + guildAuditLogEntryCreate: [auditLogEntry: GuildAuditLogsEntry, guild: Guild]; guildBanAdd: [ban: GuildBan]; guildBanRemove: [ban: GuildBan]; guildCreate: [guild: Guild]; @@ -4897,6 +4900,7 @@ export enum Events { AutoModerationRuleDelete = 'autoModerationRuleDelete', AutoModerationRuleUpdate = 'autoModerationRuleUpdate', ClientReady = 'ready', + GuildAuditLogEntryCreate = 'guildAuditLogEntryCreate', GuildCreate = 'guildCreate', GuildDelete = 'guildDelete', GuildUpdate = 'guildUpdate', @@ -5307,7 +5311,7 @@ export interface GuildAuditLogsEntryTargetField(anySelectMenu); } + +client.on('guildAuditLogEntryCreate', (auditLogEntry, guild) => { + expectType(auditLogEntry); + expectType(guild); +});