diff --git a/src/client/actions/ActionsManager.js b/src/client/actions/ActionsManager.js index 0f3f53169f7a..bcb9f2744ed2 100644 --- a/src/client/actions/ActionsManager.js +++ b/src/client/actions/ActionsManager.js @@ -4,6 +4,7 @@ class ActionsManager { constructor(client) { this.client = client; + this.register(require('./ApplicationCommandPermissionsUpdate')); this.register(require('./AutoModerationActionExecution')); this.register(require('./AutoModerationRuleCreate')); this.register(require('./AutoModerationRuleDelete')); diff --git a/src/client/actions/ApplicationCommandPermissionsUpdate.js b/src/client/actions/ApplicationCommandPermissionsUpdate.js new file mode 100644 index 000000000000..0aa518cafd4d --- /dev/null +++ b/src/client/actions/ApplicationCommandPermissionsUpdate.js @@ -0,0 +1,34 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +/** + * The data received in the {@link Client#event:applicationCommandPermissionsUpdate} event + * @typedef {Object} ApplicationCommandPermissionsUpdateData + * @property {Snowflake} id The id of the command or global entity that was updated + * @property {Snowflake} guildId The id of the guild in which permissions were updated + * @property {Snowflake} applicationId The id of the application that owns the command or entity being updated + * @property {ApplicationCommandPermissions[]} permissions The updated permissions + */ + +class ApplicationCommandPermissionsUpdateAction extends Action { + handle(data) { + const client = this.client; + /** + * Emitted whenever permissions for an application command in a guild were updated. + * This includes permission updates for other applications in addition to the logged in client, + * check `data.applicationId` to verify which application the update is for + * @event Client#applicationCommandPermissionsUpdate + * @param {ApplicationCommandPermissionsUpdateData} data The updated permissions + */ + client.emit(Events.APPLICATION_COMMAND_PERMISSIONS_UPDATE, { + permissions: data.permissions, + id: data.id, + guildId: data.guild_id, + applicationId: data.application_id, + }); + } +} + +module.exports = ApplicationCommandPermissionsUpdateAction; diff --git a/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js b/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js new file mode 100644 index 000000000000..73d4ec47f464 --- /dev/null +++ b/src/client/websocket/handlers/APPLICATION_COMMAND_PERMISSIONS_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.ApplicationCommandPermissionsUpdate.handle(packet.d); +}; diff --git a/src/client/websocket/handlers/index.js b/src/client/websocket/handlers/index.js index 7a0926499b69..62e43a991996 100644 --- a/src/client/websocket/handlers/index.js +++ b/src/client/websocket/handlers/index.js @@ -6,6 +6,7 @@ const handlers = Object.fromEntries([ ['APPLICATION_COMMAND_CREATE', require('./APPLICATION_COMMAND_CREATE')], ['APPLICATION_COMMAND_DELETE', require('./APPLICATION_COMMAND_DELETE')], ['APPLICATION_COMMAND_UPDATE', require('./APPLICATION_COMMAND_UPDATE')], + ['APPLICATION_COMMAND_PERMISSIONS_UPDATE', require('./APPLICATION_COMMAND_PERMISSIONS_UPDATE')], ['AUTO_MODERATION_ACTION_EXECUTION', require('./AUTO_MODERATION_ACTION_EXECUTION')], ['AUTO_MODERATION_RULE_CREATE', require('./AUTO_MODERATION_RULE_CREATE')], ['AUTO_MODERATION_RULE_DELETE', require('./AUTO_MODERATION_RULE_DELETE')], diff --git a/src/structures/GuildAuditLogs.js b/src/structures/GuildAuditLogs.js index 2a736f51795c..ed4bbfa95caf 100644 --- a/src/structures/GuildAuditLogs.js +++ b/src/structures/GuildAuditLogs.js @@ -1,6 +1,7 @@ 'use strict'; const { Collection } = require('@discordjs/collection'); +const ApplicationCommand = require('./ApplicationCommand'); const AutoModerationRule = require('./AutoModerationRule'); const { GuildScheduledEvent } = require('./GuildScheduledEvent'); const Integration = require('./Integration'); @@ -27,6 +28,7 @@ const Util = require('../util/Util'); * * STICKER * * THREAD * * GUILD_SCHEDULED_EVENT + * * APPLICATION_COMMAND * * AUTO_MODERATION * @typedef {string} AuditLogTargetType */ @@ -51,6 +53,7 @@ const Targets = { STAGE_INSTANCE: 'STAGE_INSTANCE', STICKER: 'STICKER', THREAD: 'THREAD', + APPLICATION_COMMAND: 'APPLICATION_COMMAND', AUTO_MODERATION: 'AUTO_MODERATION', UNKNOWN: 'UNKNOWN', }; @@ -105,6 +108,7 @@ const Targets = { * * THREAD_CREATE: 110 * * THREAD_UPDATE: 111 * * THREAD_DELETE: 112 + * * APPLICATION_COMMAND_PERMISSION_UPDATE: 121 * * AUTO_MODERATION_RULE_CREATE: 140 * * AUTO_MODERATION_RULE_UPDATE: 141 * * AUTO_MODERATION_RULE_DELETE: 142 @@ -169,6 +173,7 @@ const Actions = { THREAD_CREATE: 110, THREAD_UPDATE: 111, THREAD_DELETE: 112, + APPLICATION_COMMAND_PERMISSION_UPDATE: 121, AUTO_MODERATION_RULE_CREATE: 140, AUTO_MODERATION_RULE_UPDATE: 141, AUTO_MODERATION_RULE_DELETE: 142, @@ -208,6 +213,17 @@ class GuildAuditLogs { } } + /** + * Cached application commands, includes application commands from other applications + * @type {Collection} + * @private + */ + this.applicationCommands = new Collection(); + if (data.application_commands) { + for (const command of data.application_commands) { + this.applicationCommands.set(command.id, new ApplicationCommand(guild.client, command, guild)); + } + } /** * Cached auto moderation rules. * @type {Collection} @@ -255,11 +271,12 @@ class GuildAuditLogs { * * A sticker * * A guild scheduled event * * A thread + * * An application command * * An auto moderation rule * * An object with an id key if target was deleted * * An object where the keys represent either the new value or the old value * @typedef {?(Object|Guild|Channel|User|Role|Invite|Webhook|GuildEmoji|Message|Integration|StageInstance|Sticker| - * GuildScheduledEvent|AutoModerationRule)} AuditLogEntryTarget + * GuildScheduledEvent|ApplicationCommand|AutoModerationRule)} AuditLogEntryTarget */ /** @@ -281,6 +298,7 @@ class GuildAuditLogs { if (target < 100) return Targets.STICKER; if (target < 110) return Targets.GUILD_SCHEDULED_EVENT; if (target < 120) return Targets.THREAD; + if (target < 130) return Targets.APPLICATION_COMMAND; if (target >= 140 && target < 150) return Targets.AUTO_MODERATION; return Targets.UNKNOWN; } @@ -366,6 +384,7 @@ class GuildAuditLogs { Actions.STICKER_UPDATE, Actions.GUILD_SCHEDULED_EVENT_UPDATE, Actions.THREAD_UPDATE, + Actions.APPLICATION_COMMAND_PERMISSION_UPDATE, Actions.AUTO_MODERATION_RULE_UPDATE, ].includes(action) ) { @@ -506,6 +525,11 @@ class GuildAuditLogsEntry { channel: guild.client.channels.cache.get(data.options?.channel_id) ?? { id: data.options?.channel_id }, }; break; + case Actions.APPLICATION_COMMAND_PERMISSION_UPDATE: + this.extra = { + applicationId: data.options.application_id, + }; + break; case Actions.AUTO_MODERATION_BLOCK_MESSAGE: case Actions.AUTO_MODERATION_FLAG_TO_CHANNEL: case Actions.AUTO_MODERATION_USER_COMMUNICATION_DISABLED: @@ -641,6 +665,8 @@ class GuildAuditLogsEntry { { id: data.target_id, guild_id: guild.id }, ), ); + } else if (targetType === Targets.APPLICATION_COMMAND) { + this.target = logs?.applicationCommands.get(data.target_id) ?? { id: data.target_id }; } else if (targetType === Targets.AUTO_MODERATION) { this.target = guild.autoModerationRules.cache.get(data.target_id) ?? diff --git a/src/util/Constants.js b/src/util/Constants.js index cf70715960db..06a1f1720132 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -174,6 +174,7 @@ exports.Opcodes = { * * APPLICATION_COMMAND_CREATE: applicationCommandCreate (deprecated) * * APPLICATION_COMMAND_DELETE: applicationCommandDelete (deprecated) * * APPLICATION_COMMAND_UPDATE: applicationCommandUpdate (deprecated) + * * APPLICATION_COMMAND_PERMISSIONS_UPDATE: applicationCommandPermissionsUpdate * * AUTO_MODERATION_ACTION_EXECUTION: autoModerationActionExecution * * AUTO_MODERATION_RULE_CREATE: autoModerationRuleCreate * * AUTO_MODERATION_RULE_DELETE: autoModerationRuleDelete @@ -256,6 +257,7 @@ exports.Events = { APPLICATION_COMMAND_CREATE: 'applicationCommandCreate', APPLICATION_COMMAND_DELETE: 'applicationCommandDelete', APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate', + APPLICATION_COMMAND_PERMISSIONS_UPDATE: 'applicationCommandPermissionsUpdate', AUTO_MODERATION_ACTION_EXECUTION: 'autoModerationActionExecution', AUTO_MODERATION_RULE_CREATE: 'autoModerationRuleCreate', AUTO_MODERATION_RULE_DELETE: 'autoModerationRuleDelete', @@ -368,6 +370,7 @@ exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE', * * RESUMED * * APPLICATION_COMMAND_CREATE (deprecated) * * APPLICATION_COMMAND_DELETE (deprecated) + * * APPLICATION_COMMAND_PERMISSIONS_UPDATE * * APPLICATION_COMMAND_UPDATE (deprecated) * * GUILD_CREATE * * GUILD_DELETE @@ -428,6 +431,7 @@ exports.WSEvents = keyMirror([ 'APPLICATION_COMMAND_CREATE', 'APPLICATION_COMMAND_DELETE', 'APPLICATION_COMMAND_UPDATE', + 'APPLICATION_COMMAND_PERMISSIONS_UPDATE', 'GUILD_CREATE', 'GUILD_DELETE', 'GUILD_UPDATE', diff --git a/typings/index.d.ts b/typings/index.d.ts index e4547b92fe99..63a0067bf9eb 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -4178,6 +4178,13 @@ export interface ApplicationCommandPermissionData { permission: boolean; } +export interface ApplicationCommandPermissionsUpdateData { + permissions: ApplicationCommandPermissions; + id: Snowflake; + guildId: Snowflake; + applicationId: Snowflake; +} + export interface ApplicationCommandPermissions extends ApplicationCommandPermissionData { type: ApplicationCommandPermissionType; } @@ -4441,6 +4448,7 @@ export interface ClientEvents extends BaseClientEvents { applicationCommandDelete: [command: ApplicationCommand]; /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ applicationCommandUpdate: [oldCommand: ApplicationCommand | null, newCommand: ApplicationCommand]; + applicationCommandPermissionsUpdate: [data: ApplicationCommandPermissionsUpdateData]; autoModerationActionExecution: [autoModerationActionExecution: AutoModerationActionExecution]; autoModerationRuleCreate: [autoModerationRule: AutoModerationRule]; autoModerationRuleDelete: [autoModerationRule: AutoModerationRule]; @@ -4719,6 +4727,7 @@ export interface ConstantsEvents { APPLICATION_COMMAND_CREATE: 'applicationCommandCreate'; /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ APPLICATION_COMMAND_DELETE: 'applicationCommandDelete'; + APPLICATION_COMMAND_PERMISSIONS_UPDATE: 'applicationCommandPermissionsUpdate'; /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate'; AUTO_MODERATION_ACTION_EXECUTION: 'autoModerationActionExecution'; @@ -5081,6 +5090,7 @@ interface GuildAuditLogsTypes { THREAD_CREATE: ['THREAD', 'CREATE']; THREAD_UPDATE: ['THREAD', 'UPDATE']; THREAD_DELETE: ['THREAD', 'DELETE']; + APPLICATION_COMMAND_PERMISSION_UPDATE: ['APPLICATION_COMMAND_PERMISSION', 'UPDATE']; AUTO_MODERATION_RULE_CREATE: ['AUTO_MODERATION', 'CREATE']; AUTO_MODERATION_RULE_UPDATE: ['AUTO_MODERATION', 'UPDATE']; AUTO_MODERATION_RULE_DELETE: ['AUTO_MODERATION', 'DELETE']; @@ -5137,6 +5147,7 @@ export interface GuildAuditLogsIds { 110: 'THREAD_CREATE'; 111: 'THREAD_UPDATE'; 112: 'THREAD_DELETE'; + 121: 'APPLICATION_COMMAND_PERMISSION_UPDATE'; 140: 'AUTO_MODERATION_RULE_CREATE'; 141: 'AUTO_MODERATION_RULE_UPDATE'; 142: 'AUTO_MODERATION_RULE_DELETE'; @@ -5177,6 +5188,9 @@ export interface GuildAuditLogsEntryExtraField { STAGE_INSTANCE_CREATE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_DELETE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_UPDATE: StageChannel | { id: Snowflake }; + APPLICATION_COMMAND_PERMISSION_UPDATE: { + applicationId: Snowflake; + }; AUTO_MODERATION_BLOCK_MESSAGE: { autoModerationRuleName: string; autoModerationRuleTriggerType: AutoModerationRuleTriggerType; @@ -5203,6 +5217,7 @@ export interface GuildAuditLogsEntryTargetField