diff --git a/src/client/actions/ActionsManager.js b/src/client/actions/ActionsManager.js index 35c0e5a..3c8e2d5 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 00000000..0aa518c --- /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 00000000..73d4ec4 --- /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 ebacf81..340dd12 100644 --- a/src/client/websocket/handlers/index.js +++ b/src/client/websocket/handlers/index.js @@ -10,6 +10,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 dd52bab..8092aa7 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,18 @@ 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 +272,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 +299,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 +385,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) ) { @@ -514,6 +534,12 @@ class GuildAuditLogsEntry { }; 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: @@ -656,6 +682,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/structures/MessageSelectMenu.js b/src/structures/MessageSelectMenu.js index 91a6b98..bc2b42e 100644 --- a/src/structures/MessageSelectMenu.js +++ b/src/structures/MessageSelectMenu.js @@ -127,7 +127,7 @@ class MessageSelectMenu extends BaseMessageComponent { */ addChannelTypes(...channelTypes) { if (!channelTypes.every(channelType => ChannelTypes[channelType])) { - throw new TypeError('INVALID_TYPE', 'channelTypes', 'Array'); + throw new TypeError('INVALID_TYPE', 'channelTypes', 'Rest'); } this.channelTypes.push( ...channelTypes.map(channelType => (typeof channelType === 'string' ? channelType : ChannelTypes[channelType])), @@ -142,7 +142,7 @@ class MessageSelectMenu extends BaseMessageComponent { */ setChannelTypes(...channelTypes) { if (!channelTypes.every(channelType => ChannelTypes[channelType])) { - throw new TypeError('INVALID_TYPE', 'channelTypes', 'Array'); + throw new TypeError('INVALID_TYPE', 'channelTypes', 'Rest'); } this.channelTypes = channelTypes.map(channelType => typeof channelType === 'string' ? channelType : ChannelTypes[channelType], diff --git a/src/util/Constants.js b/src/util/Constants.js index 84742df..d51508a 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -333,6 +333,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 @@ -423,6 +424,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', @@ -555,6 +557,7 @@ exports.PartialTypes = keyMirror(['USER', 'CHANNEL', 'GUILD_MEMBER', 'MESSAGE', * * APPLICATION_COMMAND_CREATE (deprecated) * * APPLICATION_COMMAND_DELETE (deprecated) * * APPLICATION_COMMAND_UPDATE (deprecated) + * * APPLICATION_COMMAND_PERMISSIONS_UPDATE * * AUTO_MODERATION_ACTION_EXECUTION * * AUTO_MODERATION_RULE_CREATE * * AUTO_MODERATION_RULE_DELETE @@ -620,6 +623,7 @@ exports.WSEvents = keyMirror([ 'APPLICATION_COMMAND_CREATE', 'APPLICATION_COMMAND_DELETE', 'APPLICATION_COMMAND_UPDATE', + 'APPLICATION_COMMAND_PERMISSIONS_UPDATE', 'AUTO_MODERATION_ACTION_EXECUTION', 'AUTO_MODERATION_RULE_CREATE', 'AUTO_MODERATION_RULE_DELETE', diff --git a/typings/index.d.ts b/typings/index.d.ts index d251d4e..cd5126a 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -4572,6 +4572,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]; @@ -4698,6 +4699,7 @@ export interface ConstantsEvents { APPLICATION_COMMAND_DELETE: 'applicationCommandDelete'; /** @deprecated See [this issue](https://github.com/discord/discord-api-docs/issues/3690) for more information. */ APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate'; + APPLICATION_COMMAND_PERMISSIONS_UPDATE: 'applicationCommandPermissionsUpdate'; CALL_CREATE: 'callCreate'; CALL_DELETE: 'callDelete'; CALL_UPDATE: 'callUpdate'; @@ -5201,6 +5203,13 @@ export interface ApplicationCommandPermissionData { permission: boolean; } +export interface ApplicationCommandPermissionsUpdateData { + permissions: ApplicationCommandPermissions; + id: Snowflake; + guildId: Snowflake; + applicationId: Snowflake; +} + export interface ApplicationCommandPermissions extends ApplicationCommandPermissionData { type: ApplicationCommandPermissionType; } @@ -5935,6 +5944,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']; @@ -5991,6 +6001,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'; @@ -6031,6 +6042,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; @@ -6057,6 +6071,7 @@ export interface GuildAuditLogsEntryTargetField