From 294005bac76db7d34a3991e6178076229faa35b9 Mon Sep 17 00:00:00 2001 From: March 7th <71698422+aiko-chan-ai@users.noreply.github.com> Date: Tue, 20 Dec 2022 22:46:50 +0700 Subject: [PATCH] feat: automod #8886 djs --- src/client/actions/ActionsManager.js | 4 + .../actions/AutoModerationActionExecution.js | 26 ++ .../actions/AutoModerationRuleCreate.js | 27 ++ .../actions/AutoModerationRuleDelete.js | 31 ++ .../actions/AutoModerationRuleUpdate.js | 29 ++ .../AUTO_MODERATION_ACTION_EXECUTION.js | 5 + .../handlers/AUTO_MODERATION_RULE_CREATE.js | 5 + .../handlers/AUTO_MODERATION_RULE_DELETE.js | 5 + .../handlers/AUTO_MODERATION_RULE_UPDATE.js | 5 + src/client/websocket/handlers/index.js | 4 + src/index.js | 3 + src/managers/AutoModerationRuleManager.js | 289 ++++++++++++++++++ .../AutoModerationActionExecution.js | 89 ++++++ src/structures/AutoModerationRule.js | 279 +++++++++++++++++ src/structures/Guild.js | 7 + src/structures/GuildAuditLogs.js | 59 +++- src/util/Constants.js | 55 ++++ src/util/Intents.js | 4 + src/util/Sweepers.js | 10 + typings/enums.d.ts | 23 ++ typings/index.d.ts | 181 ++++++++++- typings/rawDataTypes.d.ts | 55 +++- 22 files changed, 1191 insertions(+), 4 deletions(-) create mode 100644 src/client/actions/AutoModerationActionExecution.js create mode 100644 src/client/actions/AutoModerationRuleCreate.js create mode 100644 src/client/actions/AutoModerationRuleDelete.js create mode 100644 src/client/actions/AutoModerationRuleUpdate.js create mode 100644 src/client/websocket/handlers/AUTO_MODERATION_ACTION_EXECUTION.js create mode 100644 src/client/websocket/handlers/AUTO_MODERATION_RULE_CREATE.js create mode 100644 src/client/websocket/handlers/AUTO_MODERATION_RULE_DELETE.js create mode 100644 src/client/websocket/handlers/AUTO_MODERATION_RULE_UPDATE.js create mode 100644 src/managers/AutoModerationRuleManager.js create mode 100644 src/structures/AutoModerationActionExecution.js create mode 100644 src/structures/AutoModerationRule.js diff --git a/src/client/actions/ActionsManager.js b/src/client/actions/ActionsManager.js index 5841777..0f3f531 100644 --- a/src/client/actions/ActionsManager.js +++ b/src/client/actions/ActionsManager.js @@ -4,6 +4,10 @@ class ActionsManager { constructor(client) { this.client = client; + this.register(require('./AutoModerationActionExecution')); + this.register(require('./AutoModerationRuleCreate')); + this.register(require('./AutoModerationRuleDelete')); + this.register(require('./AutoModerationRuleUpdate')); this.register(require('./ChannelCreate')); this.register(require('./ChannelDelete')); this.register(require('./ChannelUpdate')); diff --git a/src/client/actions/AutoModerationActionExecution.js b/src/client/actions/AutoModerationActionExecution.js new file mode 100644 index 00000000..0c43801 --- /dev/null +++ b/src/client/actions/AutoModerationActionExecution.js @@ -0,0 +1,26 @@ +'use strict'; + +const Action = require('./Action'); +const AutoModerationActionExecution = require('../../structures/AutoModerationActionExecution'); +const { Events } = require('../../util/Constants'); + +class AutoModerationActionExecutionAction extends Action { + handle(data) { + const { client } = this; + const guild = client.guilds.cache.get(data.guild_id); + + if (guild) { + /** + * Emitted whenever an auto moderation rule is triggered. + * This event requires the {@link Permissions.FLAGS.MANAGE_GUILD} permission. + * @event Client#autoModerationActionExecution + * @param {AutoModerationActionExecution} autoModerationActionExecution The data of the execution + */ + client.emit(Events.AUTO_MODERATION_ACTION_EXECUTION, new AutoModerationActionExecution(data, guild)); + } + + return {}; + } +} + +module.exports = AutoModerationActionExecutionAction; diff --git a/src/client/actions/AutoModerationRuleCreate.js b/src/client/actions/AutoModerationRuleCreate.js new file mode 100644 index 00000000..a20bd30 --- /dev/null +++ b/src/client/actions/AutoModerationRuleCreate.js @@ -0,0 +1,27 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class AutoModerationRuleCreateAction extends Action { + handle(data) { + const { client } = this; + const guild = client.guilds.cache.get(data.guild_id); + + if (guild) { + const autoModerationRule = guild.autoModerationRules._add(data); + + /** + * Emitted whenever an auto moderation rule is created. + * This event requires the {@link Permissions.FLAGS.MANAGE_GUILD} permission. + * @event Client#autoModerationRuleCreate + * @param {AutoModerationRule} autoModerationRule The created auto moderation rule + */ + client.emit(Events.AUTO_MODERATION_RULE_CREATE, autoModerationRule); + } + + return {}; + } +} + +module.exports = AutoModerationRuleCreateAction; diff --git a/src/client/actions/AutoModerationRuleDelete.js b/src/client/actions/AutoModerationRuleDelete.js new file mode 100644 index 00000000..a08b458 --- /dev/null +++ b/src/client/actions/AutoModerationRuleDelete.js @@ -0,0 +1,31 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class AutoModerationRuleDeleteAction extends Action { + handle(data) { + const { client } = this; + const guild = client.guilds.cache.get(data.guild_id); + + if (guild) { + const autoModerationRule = guild.autoModerationRules.cache.get(data.id); + + if (autoModerationRule) { + guild.autoModerationRules.cache.delete(autoModerationRule.id); + + /** + * Emitted whenever an auto moderation rule is deleted. + * This event requires the {@link Permissions.FLAGS.MANAGE_GUILD} permission. + * @event Client#autoModerationRuleDelete + * @param {AutoModerationRule} autoModerationRule The deleted auto moderation rule + */ + client.emit(Events.AUTO_MODERATION_RULE_DELETE, autoModerationRule); + } + } + + return {}; + } +} + +module.exports = AutoModerationRuleDeleteAction; diff --git a/src/client/actions/AutoModerationRuleUpdate.js b/src/client/actions/AutoModerationRuleUpdate.js new file mode 100644 index 00000000..0a608fa --- /dev/null +++ b/src/client/actions/AutoModerationRuleUpdate.js @@ -0,0 +1,29 @@ +'use strict'; + +const Action = require('./Action'); +const { Events } = require('../../util/Constants'); + +class AutoModerationRuleUpdateAction extends Action { + handle(data) { + const { client } = this; + const guild = client.guilds.cache.get(data.guild_id); + + if (guild) { + const oldAutoModerationRule = guild.autoModerationRules.cache.get(data.id)?._clone() ?? null; + const newAutoModerationRule = guild.autoModerationRules._add(data); + + /** + * Emitted whenever an auto moderation rule gets updated. + * This event requires the {@link Permissions.FLAGS.MANAGE_GUILD} permission. + * @event Client#autoModerationRuleUpdate + * @param {?AutoModerationRule} oldAutoModerationRule The auto moderation rule before the update + * @param {AutoModerationRule} newAutoModerationRule The auto moderation rule after the update + */ + client.emit(Events.AUTO_MODERATION_RULE_UPDATE, oldAutoModerationRule, newAutoModerationRule); + } + + return {}; + } +} + +module.exports = AutoModerationRuleUpdateAction; diff --git a/src/client/websocket/handlers/AUTO_MODERATION_ACTION_EXECUTION.js b/src/client/websocket/handlers/AUTO_MODERATION_ACTION_EXECUTION.js new file mode 100644 index 00000000..22463b6 --- /dev/null +++ b/src/client/websocket/handlers/AUTO_MODERATION_ACTION_EXECUTION.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.AutoModerationActionExecution.handle(packet.d); +}; diff --git a/src/client/websocket/handlers/AUTO_MODERATION_RULE_CREATE.js b/src/client/websocket/handlers/AUTO_MODERATION_RULE_CREATE.js new file mode 100644 index 00000000..af64b9c --- /dev/null +++ b/src/client/websocket/handlers/AUTO_MODERATION_RULE_CREATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.AutoModerationRuleCreate.handle(packet.d); +}; diff --git a/src/client/websocket/handlers/AUTO_MODERATION_RULE_DELETE.js b/src/client/websocket/handlers/AUTO_MODERATION_RULE_DELETE.js new file mode 100644 index 00000000..56ec504 --- /dev/null +++ b/src/client/websocket/handlers/AUTO_MODERATION_RULE_DELETE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.AutoModerationRuleDelete.handle(packet.d); +}; diff --git a/src/client/websocket/handlers/AUTO_MODERATION_RULE_UPDATE.js b/src/client/websocket/handlers/AUTO_MODERATION_RULE_UPDATE.js new file mode 100644 index 00000000..3caf6ba --- /dev/null +++ b/src/client/websocket/handlers/AUTO_MODERATION_RULE_UPDATE.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = (client, packet) => { + client.actions.AutoModerationRuleUpdate.handle(packet.d); +}; diff --git a/src/client/websocket/handlers/index.js b/src/client/websocket/handlers/index.js index d5d8ac5..a5f3ec8 100644 --- a/src/client/websocket/handlers/index.js +++ b/src/client/websocket/handlers/index.js @@ -10,6 +10,10 @@ 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')], + ['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')], + ['AUTO_MODERATION_RULE_UPDATE', require('./AUTO_MODERATION_RULE_UPDATE')], ['CALL_CREATE', require('./CALL_CREATE')], ['CALL_UPDATE', require('./CALL_UPDATE')], ['CALL_DELETE', require('./CALL_DELETE')], diff --git a/src/index.js b/src/index.js index bb90451..c311926 100644 --- a/src/index.js +++ b/src/index.js @@ -38,6 +38,7 @@ exports.PurchasedFlags = require('./util/PurchasedFlags'); // Managers exports.ApplicationCommandManager = require('./managers/ApplicationCommandManager'); exports.ApplicationCommandPermissionsManager = require('./managers/ApplicationCommandPermissionsManager'); +exports.AutoModerationRuleManager = require('./managers/AutoModerationRuleManager'); exports.BaseGuildEmojiManager = require('./managers/BaseGuildEmojiManager'); exports.CachedManager = require('./managers/CachedManager'); exports.ChannelManager = require('./managers/ChannelManager'); @@ -76,6 +77,8 @@ exports.AnonymousGuild = require('./structures/AnonymousGuild'); exports.Application = require('./structures/interfaces/Application'); exports.ApplicationCommand = require('./structures/ApplicationCommand'); exports.AutocompleteInteraction = require('./structures/AutocompleteInteraction'); +exports.AutoModerationActionExecution = require('./structures/AutoModerationActionExecution'); +exports.AutoModerationRule = require('./structures/AutoModerationRule'); exports.Base = require('./structures/Base'); exports.BaseCommandInteraction = require('./structures/BaseCommandInteraction'); exports.BaseGuild = require('./structures/BaseGuild'); diff --git a/src/managers/AutoModerationRuleManager.js b/src/managers/AutoModerationRuleManager.js new file mode 100644 index 00000000..2b37326 --- /dev/null +++ b/src/managers/AutoModerationRuleManager.js @@ -0,0 +1,289 @@ +'use strict'; + +const { Collection } = require('@discordjs/collection'); +const CachedManager = require('./CachedManager'); +const AutoModerationRule = require('../structures/AutoModerationRule'); +const { + AutoModerationRuleEventTypes, + AutoModerationRuleTriggerTypes, + AutoModerationActionTypes, + AutoModerationRuleKeywordPresetTypes, +} = require('../util/Constants'); + +/** + * Manages API methods for auto moderation rules and stores their cache. + * @extends {CachedManager} + */ +class AutoModerationRuleManager extends CachedManager { + constructor(guild, iterable) { + super(guild.client, AutoModerationRule, iterable); + + /** + * The guild this manager belongs to. + * @type {Guild} + */ + this.guild = guild; + } + + _add(data, cache) { + return super._add(data, cache, { extras: [this.guild] }); + } + + /** + * Options used to set the trigger metadata of an auto moderation rule. + * @typedef {Object} AutoModerationTriggerMetadataOptions + * @property {string[]} [keywordFilter] The substrings that will be searched for in the content + * @property {string[]} [regexPatterns] The regular expression patterns + * which will be matched against the content + * Only Rust-flavored regular expressions are supported. + * @property {AutoModerationRuleKeywordPresetType[]} [presets] + * The internally pre-defined wordsets which will be searched for in the content + * @property {string[]} [allowList] The substrings that will be exempt from triggering + * {@link AutoModerationRuleTriggerType.KEYWORD} and {@link AutoModerationRuleTriggerType.KEYWORD_PRESET} + * @property {?number} [mentionTotalLimit] The total number of role & user mentions allowed per message + */ + + /** + * Options used to set the actions of an auto moderation rule. + * @typedef {Object} AutoModerationActionOptions + * @property {AutoModerationActionType} type The type of this auto moderation rule action + * @property {AutoModerationActionMetadataOptions} [metadata] Additional metadata needed during execution + * This property is required if using a `type` of + * {@link AutoModerationActionType.SEND_ALERT_MESSAGE} or {@link AutoModerationActionType.TIMEOUT}. + */ + + /** + * Options used to set the metadata of an auto moderation rule action. + * @typedef {Object} AutoModerationActionMetadataOptions + * @property {GuildTextChannelResolvable|ThreadChannel} [channel] The channel to which content will be logged + * @property {number} [durationSeconds] The timeout duration in seconds + */ + + /** + * Options used to create an auto moderation rule. + * @typedef {Object} AutoModerationRuleCreateOptions + * @property {string} name The name of the auto moderation rule + * @property {AutoModerationRuleEventType} eventType The event type of the auto moderation rule + * @property {AutoModerationRuleTriggerType} triggerType The trigger type of the auto moderation rule + * @property {AutoModerationTriggerMetadataOptions} [triggerMetadata] The trigger metadata of the auto moderation rule + * This property is required if using a `triggerType` of + * {@link AutoModerationRuleTriggerTypes.KEYWORD}, {@link AutoModerationRuleTriggerTypes.KEYWORD_PRESET}, + * or {@link AutoModerationRuleTriggerTypes.MENTION_SPAM}. + * @property {AutoModerationActionOptions[]} actions + * The actions that will execute when the auto moderation rule is triggered + * @property {boolean} [enabled] Whether the auto moderation rule should be enabled + * @property {Collection|RoleResolvable[]} [exemptRoles] + * The roles that should not be affected by the auto moderation rule + * @property {Collection|GuildChannelResolvable[]} [exemptChannels] + * The channels that should not be affected by the auto moderation rule + * @property {string} [reason] The reason for creating the auto moderation rule + */ + + /** + * Creates a new auto moderation rule. + * @param {AutoModerationRuleCreateOptions} options Options for creating the auto moderation rule + * @returns {Promise} + */ + async create({ + name, + eventType, + triggerType, + triggerMetadata, + actions, + enabled, + exemptRoles, + exemptChannels, + reason, + }) { + const data = await this.client.api.guilds(this.guild.id)['auto-moderation'].rules.post({ + data: { + name, + event_type: typeof eventType === 'number' ? eventType : AutoModerationRuleEventTypes[eventType], + trigger_type: typeof triggerType === 'number' ? triggerType : AutoModerationRuleTriggerTypes[triggerType], + trigger_metadata: triggerMetadata && { + keyword_filter: triggerMetadata.keywordFilter, + regex_patterns: triggerMetadata.regexPatterns, + presets: triggerMetadata.presets?.map(preset => + typeof preset === 'number' ? preset : AutoModerationRuleKeywordPresetTypes[preset], + ), + allow_list: triggerMetadata.allowList, + mention_total_limit: triggerMetadata.mentionTotalLimit, + }, + actions: actions?.map(action => ({ + type: typeof action.type === 'number' ? action.type : AutoModerationActionTypes[action.type], + metadata: { + duration_seconds: action.metadata?.durationSeconds, + channel_id: action.metadata?.channel && this.guild.channels.resolveId(action.metadata.channel), + }, + })), + enabled, + exempt_roles: exemptRoles?.map(exemptRole => this.guild.roles.resolveId(exemptRole)), + exempt_channels: exemptChannels?.map(exemptChannel => this.guild.channels.resolveId(exemptChannel)), + }, + reason, + }); + + return this._add(data); + } + + /** + * Options used to edit an auto moderation rule. + * @typedef {Object} AutoModerationRuleEditOptions + * @property {string} [name] The name of the auto moderation rule + * @property {AutoModerationRuleEventType} [eventType] The event type of the auto moderation rule + * @property {AutoModerationTriggerMetadataOptions} [triggerMetadata] The trigger metadata of the auto moderation rule + * @property {AutoModerationActionOptions[]} [actions] + * The actions that will execute when the auto moderation rule is triggered + * @property {boolean} [enabled] Whether the auto moderation rule should be enabled + * @property {Collection|RoleResolvable[]} [exemptRoles] + * The roles that should not be affected by the auto moderation rule + * @property {Collection|GuildChannelResolvable[]} [exemptChannels] + * The channels that should not be affected by the auto moderation rule + * @property {string} [reason] The reason for creating the auto moderation rule + */ + + /** + * Edits an auto moderation rule. + * @param {AutoModerationRuleResolvable} autoModerationRule The auto moderation rule to edit + * @param {AutoModerationRuleEditOptions} options Options for editing the auto moderation rule + * @returns {Promise} + */ + async edit( + autoModerationRule, + { name, eventType, triggerMetadata, actions, enabled, exemptRoles, exemptChannels, reason }, + ) { + const autoModerationRuleId = this.resolveId(autoModerationRule); + + const data = await this.client.api + .guilds(this.guild.id)('auto-moderation') + .rules(autoModerationRuleId) + .patch({ + data: { + name, + event_type: typeof eventType === 'number' ? eventType : AutoModerationRuleEventTypes[eventType], + trigger_metadata: triggerMetadata && { + keyword_filter: triggerMetadata.keywordFilter, + regex_patterns: triggerMetadata.regexPatterns, + presets: triggerMetadata.presets?.map(preset => + typeof preset === 'number' ? preset : AutoModerationRuleKeywordPresetTypes[preset], + ), + allow_list: triggerMetadata.allowList, + mention_total_limit: triggerMetadata.mentionTotalLimit, + }, + actions: actions?.map(action => ({ + type: typeof action.type === 'number' ? action.type : AutoModerationActionTypes[action.type], + metadata: { + duration_seconds: action.metadata?.durationSeconds, + channel_id: action.metadata?.channel && this.guild.channels.resolveId(action.metadata.channel), + }, + })), + enabled, + exempt_roles: exemptRoles?.map(exemptRole => this.guild.roles.resolveId(exemptRole)), + exempt_channels: exemptChannels?.map(exemptChannel => this.guild.channels.resolveId(exemptChannel)), + }, + reason, + }); + + return this._add(data); + } + + /** + * Data that can be resolved to give an AutoModerationRule object. This can be: + * * An AutoModerationRule + * * A Snowflake + * @typedef {AutoModerationRule|Snowflake} AutoModerationRuleResolvable + */ + + /** + * Options used to fetch a single auto moderation rule from a guild. + * @typedef {BaseFetchOptions} FetchAutoModerationRuleOptions + * @property {AutoModerationRuleResolvable} autoModerationRule The auto moderation rule to fetch + */ + + /** + * Options used to fetch all auto moderation rules from a guild. + * @typedef {Object} FetchAutoModerationRulesOptions + * @property {boolean} [cache] Whether to cache the fetched auto moderation rules + */ + + /** + * Fetches auto moderation rules from Discord. + * @param {AutoModerationRuleResolvable|FetchAutoModerationRuleOptions|FetchAutoModerationRulesOptions} [options] + * Options for fetching auto moderation rule(s) + * @returns {Promise>} + * @example + * // Fetch all auto moderation rules from a guild without caching + * guild.autoModerationRules.fetch({ cache: false }) + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch a single auto moderation rule + * guild.autoModerationRules.fetch('979083472868098119') + * .then(console.log) + * .catch(console.error); + * @example + * // Fetch a single auto moderation rule without checking cache and without caching + * guild.autoModerationRules.fetch({ autoModerationRule: '979083472868098119', cache: false, force: true }) + * .then(console.log) + * .catch(console.error) + */ + fetch(options) { + if (!options) return this._fetchMany(); + const { autoModerationRule, cache, force } = options; + const resolvedAutoModerationRule = this.resolveId(autoModerationRule ?? options); + if (resolvedAutoModerationRule) { + return this._fetchSingle({ autoModerationRule: resolvedAutoModerationRule, cache, force }); + } + return this._fetchMany(options); + } + + async _fetchSingle({ autoModerationRule, cache, force = false }) { + if (!force) { + const existing = this.cache.get(autoModerationRule); + if (existing) return existing; + } + + const data = await this.client.api.guilds(this.guild.id)('auto-moderation').rules(autoModerationRule).get(); + return this._add(data, cache); + } + + async _fetchMany(options = {}) { + const data = await this.client.api.guilds(this.guild.id)('auto-moderation').rules.get(); + + return data.reduce( + (col, autoModerationRule) => col.set(autoModerationRule.id, this._add(autoModerationRule, options.cache)), + new Collection(), + ); + } + + /** + * Deletes an auto moderation rule. + * @param {AutoModerationRuleResolvable} autoModerationRule The auto moderation rule to delete + * @param {string} [reason] The reason for deleting the auto moderation rule + * @returns {Promise} + */ + async delete(autoModerationRule, reason) { + const autoModerationRuleId = this.resolveId(autoModerationRule); + await this.client.api.guilds(this.guild.id)('auto-moderation').rules(autoModerationRuleId).delete({ reason }); + } + + /** + * Resolves an {@link AutoModerationRuleResolvable} to an {@link AutoModerationRule} object. + * @method resolve + * @memberof AutoModerationRuleManager + * @instance + * @param {AutoModerationRuleResolvable} autoModerationRule The AutoModerationRule resolvable to resolve + * @returns {?AutoModerationRule} + */ + + /** + * Resolves an {@link AutoModerationRuleResolvable} to a {@link AutoModerationRule} id. + * @method resolveId + * @memberof AutoModerationRuleManager + * @instance + * @param {AutoModerationRuleResolvable} autoModerationRule The AutoModerationRule resolvable to resolve + * @returns {?Snowflake} + */ +} + +module.exports = AutoModerationRuleManager; diff --git a/src/structures/AutoModerationActionExecution.js b/src/structures/AutoModerationActionExecution.js new file mode 100644 index 00000000..7de7e54 --- /dev/null +++ b/src/structures/AutoModerationActionExecution.js @@ -0,0 +1,89 @@ +'use strict'; + +const { AutoModerationRuleTriggerTypes } = require('../util/Constants'); + +/** + * Represents the structure of an executed action when an {@link AutoModerationRule} is triggered. + */ +class AutoModerationActionExecution { + constructor(data, guild) { + /** + * The guild where this action was executed from. + * @type {Guild} + */ + this.guild = guild; + + /** + * The action that was executed. + * @type {AutoModerationAction} + */ + this.action = data.action; + + /** + * The id of the auto moderation rule this action belongs to. + * @type {Snowflake} + */ + this.ruleId = data.rule_id; + + /** + * The trigger type of the auto moderation rule which was triggered. + * @type {AutoModerationRuleTriggerType} + */ + this.ruleTriggerType = AutoModerationRuleTriggerTypes[data.rule_trigger_type]; + + /** + * The id of the user that triggered this action. + * @type {Snowflake} + */ + this.userId = data.user_id; + + /** + * The id of the channel where this action was triggered from. + * @type {?Snowflake} + */ + this.channelId = data.channel_id ?? null; + + /** + * The id of the message that triggered this action. + * @type {?Snowflake} + * This will not be present if the message was blocked or the content was not part of any message. + */ + this.messageId = data.message_id ?? null; + + /** + * The id of any system auto moderation messages posted as a result of this action. + * @type {?Snowflake} + */ + this.alertSystemMessageId = data.alert_system_message_id ?? null; + + /** + * The content that triggered this action. + * This property requires the {@link GatewayIntentBits.MessageContent} privileged gateway intent. + * @type {string} + */ + this.content = data.content; + + /** + * The word or phrase configured in the rule that triggered this action. + * @type {?string} + */ + this.matchedKeyword = data.matched_keyword ?? null; + + /** + * The substring in content that triggered this action. + * @type {?string} + */ + this.matchedContent = data.matched_content ?? null; + } + + /** + * The auto moderation rule this action belongs to. + * @type {?AutoModerationRule} + * @readonly + */ + get autoModerationRule() { + return this.guild.autoModerationRules.cache.get(this.ruleId) ?? null; + } +} + +module.exports = AutoModerationActionExecution; diff --git a/src/structures/AutoModerationRule.js b/src/structures/AutoModerationRule.js new file mode 100644 index 00000000..1d88259 --- /dev/null +++ b/src/structures/AutoModerationRule.js @@ -0,0 +1,279 @@ +'use strict'; + +const { Collection } = require('@discordjs/collection'); +const Base = require('./Base'); +const { + AutoModerationRuleKeywordPresetTypes, + AutoModerationRuleTriggerTypes, + AutoModerationRuleEventTypes, + AutoModerationActionTypes, +} = require('../util/Constants'); + +/** + * Represents an auto moderation rule. + * @extends {Base} + */ +class AutoModerationRule extends Base { + constructor(client, data, guild) { + super(client); + + /** + * The id of this auto moderation rule. + * @type {Snowflake} + */ + this.id = data.id; + + /** + * The guild this auto moderation rule is for. + * @type {Guild} + */ + this.guild = guild; + + /** + * The user that created this auto moderation rule. + * @type {Snowflake} + */ + this.creatorId = data.creator_id; + + /** + * The trigger type of this auto moderation rule. + * @type {AutoModerationRuleTriggerType} + */ + this.triggerType = AutoModerationRuleTriggerTypes[data.trigger_type]; + + this._patch(data); + } + + _patch(data) { + if ('name' in data) { + /** + * The name of this auto moderation rule. + * @type {string} + */ + this.name = data.name; + } + + if ('event_type' in data) { + /** + * The event type of this auto moderation rule. + * @type {AutoModerationRuleEventType} + */ + this.eventType = AutoModerationRuleEventTypes[data.event_type]; + } + + if ('trigger_metadata' in data) { + /** + * Additional data used to determine whether an auto moderation rule should be triggered. + * @typedef {Object} AutoModerationTriggerMetadata + * @property {string[]} keywordFilter The substrings that will be searched for in the content + * @property {string[]} regexPatterns The regular expression patterns which will be matched against the content + * Only Rust-flavored regular expressions are supported. + * @property {AutoModerationRuleKeywordPresetType[]} presets + * The internally pre-defined wordsets which will be searched for in the content + * @property {string[]} allowList The substrings that will be exempt from triggering + * {@link AutoModerationRuleTriggerTypes.Keyword} and {@link AutoModerationRuleTriggerTypes.KeywordPreset} + * @property {?number} mentionTotalLimit The total number of role & user mentions allowed per message + */ + + /** + * The trigger metadata of the rule. + * @type {AutoModerationTriggerMetadata} + */ + this.triggerMetadata = { + keywordFilter: data.trigger_metadata.keyword_filter ?? [], + regexPatterns: data.trigger_metadata.regex_patterns ?? [], + presets: data.trigger_metadata.presets?.map(preset => AutoModerationRuleKeywordPresetTypes[preset]) ?? [], + allowList: data.trigger_metadata.allow_list ?? [], + mentionTotalLimit: data.trigger_metadata.mention_total_limit ?? null, + }; + } + + if ('actions' in data) { + /** + * An object containing information about an auto moderation rule action. + * @typedef {Object} AutoModerationAction + * @property {AutoModerationActionType} type The type of this auto moderation rule action + * @property {AutoModerationActionMetadata} metadata Additional metadata needed during execution + */ + + /** + * Additional data used when an auto moderation rule is executed. + * @typedef {Object} AutoModerationActionMetadata + * @property {?Snowflake} channelId The id of the channel to which content will be logged + * @property {?number} durationSeconds The timeout duration in seconds + */ + + /** + * The actions of this auto moderation rule. + * @type {AutoModerationAction[]} + */ + this.actions = data.actions.map(action => ({ + type: AutoModerationActionTypes[action.type], + metadata: { + durationSeconds: action.metadata.duration_seconds ?? null, + channelId: action.metadata.channel_id ?? null, + }, + })); + } + + if ('enabled' in data) { + /** + * Whether this auto moderation rule is enabled. + * @type {boolean} + */ + this.enabled = data.enabled; + } + + if ('exempt_roles' in data) { + /** + * The roles exempt by this auto moderation rule. + * @type {Collection} + */ + this.exemptRoles = new Collection( + data.exempt_roles.map(exemptRole => [exemptRole, this.guild.roles.cache.get(exemptRole)]), + ); + } + + if ('exempt_channels' in data) { + /** + * The channels exempt by this auto moderation rule. + * @type {Collection} + */ + this.exemptChannels = new Collection( + data.exempt_channels.map(exemptChannel => [exemptChannel, this.guild.channels.cache.get(exemptChannel)]), + ); + } + } + + /** + * Edits this auto moderation rule. + * @param {AutoModerationRuleEditOptions} options Options for editing this auto moderation rule + * @returns {Promise} + */ + edit(options) { + return this.guild.autoModerationRules.edit(this.id, options); + } + + /** + * Deletes this auto moderation rule. + * @param {string} [reason] The reason for deleting this auto moderation rule + * @returns {Promise} + */ + delete(reason) { + return this.guild.autoModerationRules.delete(this.id, reason); + } + + /** + * Sets the name for this auto moderation rule. + * @param {string} name The name of this auto moderation rule + * @param {string} [reason] The reason for changing the name of this auto moderation rule + * @returns {Promise} + */ + setName(name, reason) { + return this.edit({ name, reason }); + } + + /** + * Sets the event type for this auto moderation rule. + * @param {AutoModerationRuleEventType} eventType The event type of this auto moderation rule + * @param {string} [reason] The reason for changing the event type of this auto moderation rule + * @returns {Promise} + */ + setEventType(eventType, reason) { + return this.edit({ eventType, reason }); + } + + /** + * Sets the keyword filter for this auto moderation rule. + * @param {string[]} keywordFilter The keyword filter of this auto moderation rule + * @param {string} [reason] The reason for changing the keyword filter of this auto moderation rule + * @returns {Promise} + */ + setKeywordFilter(keywordFilter, reason) { + return this.edit({ triggerMetadata: { ...this.triggerMetadata, keywordFilter }, reason }); + } + + /** + * Sets the regular expression patterns for this auto moderation rule. + * @param {string[]} regexPatterns The regular expression patterns of this auto moderation rule + * Only Rust-flavored regular expressions are supported. + * @param {string} [reason] The reason for changing the regular expression patterns of this auto moderation rule + * @returns {Promise} + */ + setRegexPatterns(regexPatterns, reason) { + return this.edit({ triggerMetadata: { ...this.triggerMetadata, regexPatterns }, reason }); + } + + /** + * Sets the presets for this auto moderation rule. + * @param {AutoModerationRuleKeywordPresetType[]} presets The presets of this auto moderation rule + * @param {string} [reason] The reason for changing the presets of this auto moderation rule + * @returns {Promise} + */ + setPresets(presets, reason) { + return this.edit({ triggerMetadata: { ...this.triggerMetadata, presets }, reason }); + } + + /** + * Sets the allow list for this auto moderation rule. + * @param {string[]} allowList The allow list of this auto moderation rule + * @param {string} [reason] The reason for changing the allow list of this auto moderation rule + * @returns {Promise} + */ + setAllowList(allowList, reason) { + return this.edit({ triggerMetadata: { ...this.triggerMetadata, allowList }, reason }); + } + + /** + * Sets the mention total limit for this auto moderation rule. + * @param {number} mentionTotalLimit The mention total limit of this auto moderation rule + * @param {string} [reason] The reason for changing the mention total limit of this auto moderation rule + * @returns {Promise} + */ + setMentionTotalLimit(mentionTotalLimit, reason) { + return this.edit({ triggerMetadata: { ...this.triggerMetadata, mentionTotalLimit }, reason }); + } + + /** + * Sets the actions for this auto moderation rule. + * @param {AutoModerationActionOptions} actions The actions of this auto moderation rule + * @param {string} [reason] The reason for changing the actions of this auto moderation rule + * @returns {Promise} + */ + setActions(actions, reason) { + return this.edit({ actions, reason }); + } + + /** + * Sets whether this auto moderation rule should be enabled. + * @param {boolean} [enabled=true] Whether to enable this auto moderation rule + * @param {string} [reason] The reason for enabling or disabling this auto moderation rule + * @returns {Promise} + */ + setEnabled(enabled = true, reason) { + return this.edit({ enabled, reason }); + } + + /** + * Sets the exempt roles for this auto moderation rule. + * @param {Collection|RoleResolvable[]} [exemptRoles] The exempt roles of this auto moderation rule + * @param {string} [reason] The reason for changing the exempt roles of this auto moderation rule + * @returns {Promise} + */ + setExemptRoles(exemptRoles, reason) { + return this.edit({ exemptRoles, reason }); + } + + /** + * Sets the exempt channels for this auto moderation rule. + * @param {Collection|GuildChannelResolvable[]} [exemptChannels] + * The exempt channels of this auto moderation rule + * @param {string} [reason] The reason for changing the exempt channels of this auto moderation rule + * @returns {Promise} + */ + setExemptChannels(exemptChannels, reason) { + return this.edit({ exemptChannels, reason }); + } +} + +module.exports = AutoModerationRule; diff --git a/src/structures/Guild.js b/src/structures/Guild.js index f1f765a..1ca23b4 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -10,6 +10,7 @@ const Integration = require('./Integration'); const Webhook = require('./Webhook'); const WelcomeScreen = require('./WelcomeScreen'); const { Error } = require('../errors'); +const AutoModerationRuleManager = require('../managers/AutoModerationRuleManager'); const GuildBanManager = require('../managers/GuildBanManager'); const GuildChannelManager = require('../managers/GuildChannelManager'); const GuildEmojiManager = require('../managers/GuildEmojiManager'); @@ -111,6 +112,12 @@ class Guild extends AnonymousGuild { */ this.scheduledEvents = new GuildScheduledEventManager(this); + /** + * A manager of the auto moderation rules of this guild. + * @type {AutoModerationRuleManager} + */ + this.autoModerationRules = new AutoModerationRuleManager(this); + if (!data) return; if (data.unavailable) { diff --git a/src/structures/GuildAuditLogs.js b/src/structures/GuildAuditLogs.js index 48086c3..6626764 100644 --- a/src/structures/GuildAuditLogs.js +++ b/src/structures/GuildAuditLogs.js @@ -1,13 +1,14 @@ 'use strict'; const { Collection } = require('@discordjs/collection'); +const AutoModerationRule = require('./AutoModerationRule'); const { GuildScheduledEvent } = require('./GuildScheduledEvent'); const Integration = require('./Integration'); const Invite = require('./Invite'); const { StageInstance } = require('./StageInstance'); const { Sticker } = require('./Sticker'); const Webhook = require('./Webhook'); -const { OverwriteTypes, PartialTypes } = require('../util/Constants'); +const { OverwriteTypes, PartialTypes, AutoModerationRuleTriggerTypes } = require('../util/Constants'); const SnowflakeUtil = require('../util/SnowflakeUtil'); const Util = require('../util/Util'); @@ -26,6 +27,7 @@ const Util = require('../util/Util'); * * STICKER * * THREAD * * GUILD_SCHEDULED_EVENT + * * AUTO_MODERATION * @typedef {string} AuditLogTargetType */ @@ -49,6 +51,7 @@ const Targets = { STAGE_INSTANCE: 'STAGE_INSTANCE', STICKER: 'STICKER', THREAD: 'THREAD', + AUTO_MODERATION: 'AUTO_MODERATION', UNKNOWN: 'UNKNOWN', }; @@ -102,6 +105,12 @@ const Targets = { * * THREAD_CREATE: 110 * * THREAD_UPDATE: 111 * * THREAD_DELETE: 112 + * * AUTO_MODERATION_RULE_CREATE: 140 + * * AUTO_MODERATION_RULE_UPDATE: 141 + * * AUTO_MODERATION_RULE_DELETE: 142 + * * AUTO_MODERATION_BLOCK_MESSAGE: 143 + * * AUTO_MODERATION_FLAG_TO_CHANNEL: 144 + * * AUTO_MODERATION_USER_COMMUNICATION_DISABLED: 145 * @typedef {?(number|string)} AuditLogAction * @see {@link https://discord.com/developers/docs/resources/audit-log#audit-log-entry-object-audit-log-events} */ @@ -160,6 +169,12 @@ const Actions = { THREAD_CREATE: 110, THREAD_UPDATE: 111, THREAD_DELETE: 112, + AUTO_MODERATION_RULE_CREATE: 140, + AUTO_MODERATION_RULE_UPDATE: 141, + AUTO_MODERATION_RULE_DELETE: 142, + AUTO_MODERATION_BLOCK_MESSAGE: 143, + AUTO_MODERATION_FLAG_TO_CHANNEL: 144, + AUTO_MODERATION_USER_COMMUNICATION_DISABLED: 145, }; /** @@ -193,6 +208,17 @@ class GuildAuditLogs { } } + /** + * Cached auto moderation rules. + * @type {Collection} + * @private + */ + this.autoModerationRules = data.auto_moderation_rules.reduce( + (autoModerationRules, autoModerationRule) => + autoModerationRules.set(autoModerationRule.id, guild.autoModerationRules._add(autoModerationRule)), + new Collection(), + ); + /** * The entries for this guild's audit logs * @type {Collection} @@ -229,10 +255,11 @@ class GuildAuditLogs { * * A sticker * * A guild scheduled event * * A thread + * * 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)} AuditLogEntryTarget + * GuildScheduledEvent|AutoModerationRule)} AuditLogEntryTarget */ /** @@ -254,6 +281,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 >= 140 && target < 150) return Targets.AUTO_MODERATION; return Targets.UNKNOWN; } @@ -288,6 +316,8 @@ class GuildAuditLogs { Actions.STICKER_CREATE, Actions.GUILD_SCHEDULED_EVENT_CREATE, Actions.THREAD_CREATE, + Actions.AUTO_MODERATION_RULE_CREATE, + Actions.AUTO_MODERATION_BLOCK_MESSAGE, ].includes(action) ) { return 'CREATE'; @@ -313,6 +343,7 @@ class GuildAuditLogs { Actions.STICKER_DELETE, Actions.GUILD_SCHEDULED_EVENT_DELETE, Actions.THREAD_DELETE, + Actions.AUTO_MODERATION_RULE_DELETE, ].includes(action) ) { return 'DELETE'; @@ -335,6 +366,7 @@ class GuildAuditLogs { Actions.STICKER_UPDATE, Actions.GUILD_SCHEDULED_EVENT_UPDATE, Actions.THREAD_UPDATE, + Actions.AUTO_MODERATION_RULE_UPDATE, ].includes(action) ) { return 'UPDATE'; @@ -476,6 +508,15 @@ class GuildAuditLogsEntry { }; break; + case Actions.AUTO_MODERATION_BLOCK_MESSAGE: + case Actions.AUTO_MODERATION_FLAG_TO_CHANNEL: + case Actions.AUTO_MODERATION_USER_COMMUNICATION_DISABLED: + this.extra = { + autoModerationRuleName: data.options.auto_moderation_rule_name, + autoModerationRuleTriggerType: AutoModerationRuleTriggerTypes[data.options.auto_moderation_rule_trigger_type], + }; + break; + default: break; } @@ -603,6 +644,20 @@ class GuildAuditLogsEntry { { id: data.target_id, guild_id: guild.id }, ), ); + } else if (targetType === Targets.AUTO_MODERATION) { + this.target = + guild.autoModerationRules.cache.get(data.target_id) ?? + new AutoModerationRule( + guild.client, + this.changes.reduce( + (o, c) => { + o[c.key] = c.new ?? c.old; + return o; + }, + { id: data.target_id, guild_id: guild.id }, + ), + guild, + ); } else if (data.target_id) { this.target = guild[`${targetType.toLowerCase()}s`]?.cache.get(data.target_id) ?? { id: data.target_id }; } diff --git a/src/util/Constants.js b/src/util/Constants.js index b1f2ed1..5a6c728 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -329,6 +329,10 @@ exports.Opcodes = { * * APPLICATION_COMMAND_CREATE: applicationCommandCreate (deprecated) * * APPLICATION_COMMAND_DELETE: applicationCommandDelete (deprecated) * * APPLICATION_COMMAND_UPDATE: applicationCommandUpdate (deprecated) + * * AUTO_MODERATION_ACTION_EXECUTION: autoModerationActionExecution + * * AUTO_MODERATION_RULE_CREATE: autoModerationRuleCreate + * * AUTO_MODERATION_RULE_DELETE: autoModerationRuleDelete + * * AUTO_MODERATION_RULE_UPDATE: autoModerationRuleUpdate * * CALL_CREATE: callCreate * * CALL_DELETE: callDelete * * CALL_UPDATE: callUpdate @@ -414,6 +418,10 @@ exports.Events = { APPLICATION_COMMAND_CREATE: 'applicationCommandCreate', APPLICATION_COMMAND_DELETE: 'applicationCommandDelete', APPLICATION_COMMAND_UPDATE: 'applicationCommandUpdate', + AUTO_MODERATION_ACTION_EXECUTION: 'autoModerationActionExecution', + AUTO_MODERATION_RULE_CREATE: 'autoModerationRuleCreate', + AUTO_MODERATION_RULE_DELETE: 'autoModerationRuleDelete', + AUTO_MODERATION_RULE_UPDATE: 'autoModerationRuleUpdate', CALL_CREATE: 'callCreate', CALL_DELETE: 'callDelete', CALL_UPDATE: 'callUpdate', @@ -753,6 +761,7 @@ exports.MessageTypes = [ /** * The name of an item to be swept in Sweepers * * `applicationCommands` - both global and guild commands + * * `autoModerationRules` * * `bans` * * `emojis` * * `invites` - accepts the `lifetime` property, using it will sweep based on expires timestamp @@ -770,6 +779,7 @@ exports.MessageTypes = [ */ exports.SweeperKeys = [ 'applicationCommands', + 'autoModerationRules', 'bans', 'emojis', 'invites', @@ -1462,6 +1472,46 @@ exports.ApplicationCommandOptionTypes = createEnum([ */ exports.ApplicationCommandPermissionTypes = createEnum([null, 'ROLE', 'USER']); +/** + * The type of an {@link AutoModerationRuleTriggerTypes} object: + * * KEYWORD + * * SPAM + * * KEYWORD_PRESET + * * MENTION_SPAM + * @typedef {string} AutoModerationRuleTriggerType + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-trigger-types} + */ +exports.AutoModerationRuleTriggerTypes = createEnum([null, 'KEYWORD', null, 'SPAM', 'KEYWORD_PRESET', 'MENTION_SPAM']); + +/** + * The type of an {@link AutoModerationRuleKeywordPresetTypes} object: + * * KEYWORD + * * SPAM + * * KEYWORD_PRESET + * * MENTION_SPAM + * @typedef {string} AutoModerationRuleKeywordPresetType + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-keyword-preset-types} + */ +exports.AutoModerationRuleKeywordPresetTypes = createEnum([null, 'PROFANITY', 'SEXUAL_CONTENT', 'SLURS']); +/** + * The type of an {@link AutoModerationActionTypes} object: + * * BLOCK_MESSAGE + * * SEND_ALERT_MESSAGE + * * TIMEOUT + * @typedef {string} AutoModerationActionType + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-action-object-action-types} + */ +exports.AutoModerationActionTypes = createEnum([null, 'BLOCK_MESSAGE', 'SEND_ALERT_MESSAGE', 'TIMEOUT']); + +/** + * The type of an {@link AutoModerationRuleEventTypes} object: + * * MESSAGE_SEND + * @typedef {string} AutoModerationRuleEventType + * @see {@link https://discord.com/developers/docs/resources/auto-moderation#auto-moderation-rule-object-event-types} + */ + +exports.AutoModerationRuleEventTypes = createEnum([null, 'MESSAGE_SEND']); + /** * The type of an {@link Interaction} object: * * PING @@ -1709,6 +1759,11 @@ function createEnum(keys) { * The type of an {@link ApplicationCommandPermissions} object. * @property {Object} ApplicationCommandTypes * The type of an {@link ApplicationCommand} object. + * @property {Object} AutoModerationRuleTriggerTypes Characterizes the type + * of contentwhich can trigger the rule. + * @property {Object} AutoModerationActionTypes + * @property {Object} AutoModerationRuleKeywordPresetTypes + * @property {Object} AutoModerationRuleEventTypes * @property {Object} ChannelTypes All available channel types. * @property {ClientApplicationAssetTypes} ClientApplicationAssetTypes The types of an {@link ApplicationAsset} object. * @property {Object} Colors An object with regularly used colors. diff --git a/src/util/Intents.js b/src/util/Intents.js index 6600f59..77f856e 100644 --- a/src/util/Intents.js +++ b/src/util/Intents.js @@ -42,6 +42,8 @@ class Intents extends BitField {} * * `DIRECT_MESSAGE_TYPING` * * `MESSAGE_CONTENT` * * `GUILD_SCHEDULED_EVENTS` + * * `AUTO_MODERATION_CONFIGURATION` + * * `AUTO_MODERATION_EXECUTION` * @type {Object} * @see {@link https://discord.com/developers/docs/topics/gateway#list-of-intents} */ @@ -63,6 +65,8 @@ Intents.FLAGS = { DIRECT_MESSAGE_TYPING: 1 << 14, MESSAGE_CONTENT: 1 << 15, GUILD_SCHEDULED_EVENTS: 1 << 16, + AUTO_MODERATION_CONFIGURATION: 1 << 20, + AUTO_MODERATION_EXECUTION: 1 << 21, }; module.exports = Intents; diff --git a/src/util/Sweepers.js b/src/util/Sweepers.js index a826115..a104d3f 100644 --- a/src/util/Sweepers.js +++ b/src/util/Sweepers.js @@ -77,6 +77,16 @@ class Sweepers { return guildCommands + globalCommands; } + /** + * Sweeps all auto moderation rules and removes the ones which are indicated by the filter. + * @param {Function} filter The function used to determine + * which auto moderation rules will be removed from the caches + * @returns {number} Amount of auto moderation rules that were removed from the caches + */ + sweepAutoModerationRules(filter) { + return this._sweepGuildDirectProp('autoModerationRules', filter).items; + } + /** * Sweeps all guild bans and removes the ones which are indicated by the filter. * @param {Function} filter The function used to determine which bans will be removed from the caches. diff --git a/typings/enums.d.ts b/typings/enums.d.ts index d800df5..f2e96bb 100644 --- a/typings/enums.d.ts +++ b/typings/enums.d.ts @@ -96,6 +96,29 @@ export const enum ApplicationCommandPermissionTypes { USER = 2, } +export const enum AutoModerationRuleTriggerTypes { + KEYWORD = 1, + SPAM = 2, + KEYWORD_PRESET = 3, + MENTION_SPAM = 4, +} + +export const enum AutoModerationRuleKeywordPresetTypes { + PROFANITY = 1, + SEXUAL_CONTENT = 2, + SLURS = 3, +} + +export const enum AutoModerationActionTypes { + BLOCK_MESSAGE = 1, + SEND_ALERT_MESSAGE = 2, + TIMEOUT = 3, +} + +export const enum AutoModerationRuleEventTypes { + MESSAGE_SEND = 1, +} + export const enum ChannelTypes { GUILD_TEXT = 0, DM = 1, diff --git a/typings/index.d.ts b/typings/index.d.ts index a7254dc..062e1bb 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -66,6 +66,10 @@ import { ApplicationCommandOptionTypes, ApplicationCommandPermissionTypes, ApplicationCommandTypes, + AutoModerationActionTypes, + AutoModerationRuleEventTypes, + AutoModerationRuleKeywordPresetTypes, + AutoModerationRuleTriggerTypes, ChannelTypes, RelationshipTypes, localeSetting, @@ -103,6 +107,8 @@ import { SelectMenuComponentTypes, } from './enums'; import { + APIAutoModerationRule, + GatewayAutoModerationActionExecutionDispatchData, RawActivityData, RawAnonymousGuildData, RawApplicationCommandData, @@ -523,6 +529,8 @@ export class ApplicationCommand extends Base { export type ApplicationResolvable = Application | Activity | Snowflake; +export type AutoModerationRuleResolvable = AutoModerationRule | Snowflake; + export class ApplicationFlags extends BitField { public static FLAGS: Record; public static resolve(bit?: BitFieldResolvable): number; @@ -1259,6 +1267,7 @@ export class Guild extends AnonymousGuild { public applicationId: Snowflake | null; public approximateMemberCount: number | null; public approximatePresenceCount: number | null; + public autoModerationRules: AutoModerationRuleManager; public available: boolean; public bans: GuildBanManager; public channels: GuildChannelManager; @@ -1372,6 +1381,7 @@ export class GuildAuditLogs { private constructor(guild: Guild, data: RawGuildAuditLogData); private webhooks: Collection; private integrations: Collection; + private autoModerationRules: Collection; public entries: Collection>; @@ -3507,6 +3517,10 @@ export const Constants: { DefaultMessageNotificationLevels: EnumHolder; VerificationLevels: EnumHolder; MembershipStates: EnumHolder; + AutoModerationRuleTriggerTypes: EnumHolder; + AutoModerationRuleKeywordPresetTypes: EnumHolder; + AutoModerationActionTypes: EnumHolder; + AutoModerationRuleEventTypes: EnumHolder; ApplicationCommandOptionTypes: EnumHolder; ApplicationCommandPermissionTypes: EnumHolder; InteractionTypes: EnumHolder; @@ -4440,6 +4454,13 @@ 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]; + autoModerationActionExecution: [autoModerationActionExecution: AutoModerationActionExecution]; + autoModerationRuleCreate: [autoModerationRule: AutoModerationRule]; + autoModerationRuleDelete: [autoModerationRule: AutoModerationRule]; + autoModerationRuleUpdate: [ + oldAutoModerationRule: AutoModerationRule | null, + newAutoModerationRule: AutoModerationRule, + ]; cacheSweep: [message: string]; channelCreate: [channel: NonThreadGuildBasedChannel]; channelDelete: [channel: DMChannel | NonThreadGuildBasedChannel]; @@ -4935,6 +4956,84 @@ export type ApplicationCommandType = keyof typeof ApplicationCommandTypes; export type ApplicationCommandOptionType = keyof typeof ApplicationCommandOptionTypes; +export type AutoModerationRuleTriggerType = keyof typeof AutoModerationRuleTriggerTypes; + +export type AutoModerationRuleKeywordPresetType = keyof typeof AutoModerationRuleKeywordPresetTypes; + +export type AutoModerationActionType = keyof typeof AutoModerationActionTypes; + +export type AutoModerationRuleEventType = keyof typeof AutoModerationRuleEventTypes; + +export class AutoModerationActionExecution { + private constructor(data: GatewayAutoModerationActionExecutionDispatchData, guild: Guild); + public guild: Guild; + public action: AutoModerationAction; + public ruleId: Snowflake; + public ruleTriggerType: AutoModerationRuleTriggerType; + public userId: Snowflake; + public channelId: Snowflake | null; + public messageId: Snowflake | null; + public alertSystemMessageId: Snowflake | null; + public content: string; + public matchedKeyword: string | null; + public matchedContent: string | null; + public get autoModerationRule(): AutoModerationRule | null; +} + +export class AutoModerationRule extends Base { + private constructor(client: Client, data: APIAutoModerationRule, guild: Guild); + public id: Snowflake; + public guild: Guild; + public name: string; + public creatorId: Snowflake; + public eventType: AutoModerationRuleEventType; + public triggerType: AutoModerationRuleTriggerType; + public triggerMetadata: AutoModerationTriggerMetadata; + public actions: AutoModerationAction[]; + public enabled: boolean; + public exemptRoles: Collection; + public exemptChannels: Collection; + public edit(options: AutoModerationRuleEditOptions): Promise; + public delete(reason?: string): Promise; + public setName(name: string, reason?: string): Promise; + public setEventType( + eventType: AutoModerationRuleEventType | AutoModerationRuleEventTypes, + reason?: string, + ): Promise; + public setKeywordFilter(keywordFilter: string[], reason?: string): Promise; + public setRegexPatterns(regexPatterns: string[], reason?: string): Promise; + public setPresets(presets: AutoModerationRuleKeywordPresetType[], reason?: string): Promise; + public setAllowList(allowList: string[], reason?: string): Promise; + public setMentionTotalLimit(mentionTotalLimit: number, reason?: string): Promise; + public setActions(actions: AutoModerationActionOptions, reason?: string): Promise; + public setEnabled(enabled?: boolean, reason?: string): Promise; + public setExemptRoles( + roles: Collection | RoleResolvable[], + reason?: string, + ): Promise; + public setExemptChannels( + channels: Collection | GuildChannelResolvable[], + reason?: string, + ): Promise; +} + +export class AutoModerationRuleManager extends CachedManager< + Snowflake, + AutoModerationRule, + AutoModerationRuleResolvable +> { + private constructor(guild: Guild, iterable: unknown); + public guild: Guild; + public create(options: AutoModerationRuleCreateOptions): Promise; + public edit( + autoModerationRule: AutoModerationRuleResolvable, + options: AutoModerationRuleEditOptions, + ): Promise; + public fetch(options: AutoModerationRuleResolvable | FetchAutoModerationRuleOptions): Promise; + public fetch(options?: FetchAutoModerationRulesOptions): Promise>; + public delete(autoModerationRule: AutoModerationRuleResolvable, reason?: string): Promise; +} + export interface ApplicationCommandPermissionData { id: Snowflake; type: ApplicationCommandPermissionType | ApplicationCommandPermissionTypes; @@ -4961,6 +5060,57 @@ export type ApplicationFlagsString = | 'EMBEDDED_FIRST_PARTY' | 'APPLICATION_COMMAND_BADGE'; +export interface AutoModerationAction { + type: AutoModerationActionType | AutoModerationActionTypes; + metadata: AutoModerationActionMetadata; +} + +export interface AutoModerationActionMetadata { + channelId: Snowflake | null; + durationSeconds: number | null; +} + +export interface AutoModerationTriggerMetadata { + keywordFilter: string[]; + regexPatterns: string[]; + presets: (AutoModerationRuleKeywordPresetType | AutoModerationRuleKeywordPresetTypes)[]; + allowList: string[]; + mentionTotalLimit: number | null; +} + +export interface FetchAutoModerationRuleOptions extends BaseFetchOptions { + autoModerationRule: AutoModerationRuleResolvable; +} + +export interface FetchAutoModerationRulesOptions { + cache?: boolean; +} + +export interface AutoModerationRuleCreateOptions { + name: string; + eventType: AutoModerationRuleEventType | AutoModerationRuleEventTypes; + triggerType: AutoModerationRuleTriggerType | AutoModerationRuleTriggerTypes; + triggerMetadata?: AutoModerationTriggerMetadataOptions; + actions: AutoModerationActionOptions; + enabled?: boolean; + exemptRoles?: Collection | RoleResolvable[]; + exemptChannels?: Collection | GuildChannelResolvable[]; + reason?: string; +} + +export interface AutoModerationRuleEditOptions extends Partial> {} + +export interface AutoModerationTriggerMetadataOptions extends Partial {} + +export interface AutoModerationActionOptions { + type: AutoModerationActionType | AutoModerationActionTypes; + metadata?: AutoModerationActionMetadataOptions; +} + +export interface AutoModerationActionMetadataOptions extends Partial> { + channel: GuildTextChannelResolvable | ThreadChannel; +} + export interface AuditLogChange { key: APIAuditLogChange['key']; old?: APIAuditLogChange['old_value']; @@ -5020,6 +5170,7 @@ export type BufferResolvable = Buffer | string; export interface Caches { ApplicationCommandManager: [manager: typeof ApplicationCommandManager, holds: typeof ApplicationCommand]; + AutoModerationRuleManager: [manager: typeof AutoModerationRuleManager, holds: typeof AutoModerationRule]; BaseGuildEmojiManager: [manager: typeof BaseGuildEmojiManager, holds: typeof GuildEmoji]; GuildEmojiManager: [manager: typeof GuildEmojiManager, holds: typeof GuildEmoji]; // TODO: ChannelManager: [manager: typeof ChannelManager, holds: typeof Channel]; @@ -5579,6 +5730,12 @@ interface GuildAuditLogsTypes { THREAD_CREATE: ['THREAD', 'CREATE']; THREAD_UPDATE: ['THREAD', 'UPDATE']; THREAD_DELETE: ['THREAD', 'DELETE']; + AUTO_MODERATION_RULE_CREATE: ['AUTO_MODERATION', 'CREATE']; + AUTO_MODERATION_RULE_UPDATE: ['AUTO_MODERATION', 'UPDATE']; + AUTO_MODERATION_RULE_DELETE: ['AUTO_MODERATION', 'DELETE']; + AUTO_MODERATION_BLOCK_MESSAGE: ['AUTO_MODERATION', 'CREATE']; + AUTO_MODERATION_FLAG_TO_CHANNEL: ['AUTO_MODERATION', 'CREATE']; + AUTO_MODERATION_USER_COMMUNICATION_DISABLED: ['AUTO_MODERATION', 'CREATE']; } export interface GuildAuditLogsIds { @@ -5629,6 +5786,12 @@ export interface GuildAuditLogsIds { 110: 'THREAD_CREATE'; 111: 'THREAD_UPDATE'; 112: 'THREAD_DELETE'; + 140: 'AUTO_MODERATION_RULE_CREATE'; + 141: 'AUTO_MODERATION_RULE_UPDATE'; + 142: 'AUTO_MODERATION_RULE_DELETE'; + 143: 'AUTO_MODERATION_BLOCK_MESSAGE'; + 144: 'AUTO_MODERATION_FLAG_TO_CHANNEL'; + 145: 'AUTO_MODERATION_USER_COMMUNICATION_DISABLED'; } export type GuildAuditLogsActions = { [Key in keyof GuildAuditLogsIds as GuildAuditLogsIds[Key]]: Key } & { ALL: null }; @@ -5663,6 +5826,18 @@ export interface GuildAuditLogsEntryExtraField { STAGE_INSTANCE_CREATE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_DELETE: StageChannel | { id: Snowflake }; STAGE_INSTANCE_UPDATE: StageChannel | { id: Snowflake }; + AUTO_MODERATION_BLOCK_MESSAGE: { + autoModerationRuleName: string; + autoModerationRuleTriggerType: AutoModerationRuleTriggerType; + }; + AUTO_MODERATION_FLAG_TO_CHANNEL: { + autoModerationRuleName: string; + autoModerationRuleTriggerType: AutoModerationRuleTriggerType; + }; + AUTO_MODERATION_USER_COMMUNICATIONDISABLED: { + autoModerationRuleName: string; + autoModerationRuleTriggerType: AutoModerationRuleTriggerType; + }; } export interface GuildAuditLogsEntryTargetField { @@ -5677,6 +5852,7 @@ export interface GuildAuditLogsEntryTargetField { @@ -5994,7 +6170,9 @@ export type IntentsString = | 'DIRECT_MESSAGE_REACTIONS' | 'DIRECT_MESSAGE_TYPING' | 'MESSAGE_CONTENT' - | 'GUILD_SCHEDULED_EVENTS'; + | 'GUILD_SCHEDULED_EVENTS' + | 'AUTO_MODERATION_CONFIGURATION' + | 'AUTO_MODERATION_EXECUTION'; export interface InviteGenerationOptions { permissions?: PermissionResolvable; @@ -6703,6 +6881,7 @@ export interface LifetimeSweepOptions { export interface SweeperDefinitions { applicationCommands: [Snowflake, ApplicationCommand]; + autoModerationRules: [Snowflake, AutoModerationRule]; bans: [Snowflake, GuildBan]; emojis: [Snowflake, GuildEmoji]; invites: [string, Invite, true]; diff --git a/typings/rawDataTypes.d.ts b/typings/rawDataTypes.d.ts index 7c69081..b292755 100644 --- a/typings/rawDataTypes.d.ts +++ b/typings/rawDataTypes.d.ts @@ -82,7 +82,15 @@ import { APIModalSubmitInteraction, } from 'discord-api-types/v9'; import { GuildChannel, Guild, PermissionOverwrites, InteractionType } from '.'; -import type { InteractionTypes, MessageComponentTypes } from './enums'; + +import type { + AutoModerationActionTypes, + AutoModerationRuleEventTypes, + AutoModerationRuleKeywordPresetTypes, + AutoModerationRuleTriggerTypes, + InteractionTypes, + MessageComponentTypes, +} from './enums'; export type RawActivityData = GatewayActivity; @@ -216,3 +224,48 @@ export type RawWelcomeScreenData = APIGuildWelcomeScreen; export type RawWidgetData = APIGuildWidget; export type RawWidgetMemberData = APIGuildWidgetMember; + +export interface GatewayAutoModerationActionExecutionDispatchData { + guild_id: Snowflake; + action: APIAutoModerationAction; + rule_id: Snowflake; + rule_trigger_type: AutoModerationRuleTriggerTypes; + user_id: Snowflake; + channel_id?: Snowflake; + message_id?: Snowflake; + alert_system_message_id?: Snowflake; + content: string; + matched_keyword: string | null; + matched_content: string | null; +} + +export interface APIAutoModerationAction { + type: AutoModerationActionTypes; + metadata?: APIAutoModerationActionMetadata; +} +export interface APIAutoModerationActionMetadata { + channel_id?: Snowflake; + duration_seconds?: number; +} + +export interface APIAutoModerationRule { + id: Snowflake; + guild_id: Snowflake; + name: string; + creator_id: Snowflake; + event_type: AutoModerationRuleEventTypes; + trigger_type: AutoModerationRuleTriggerTypes; + trigger_metadata: APIAutoModerationRuleTriggerMetadata; + actions: APIAutoModerationAction[]; + enabled: boolean; + exempt_roles: Snowflake[]; + exempt_channels: Snowflake[]; +} + +export interface APIAutoModerationRuleTriggerMetadata { + keyword_filter?: string[]; + presets?: AutoModerationRuleKeywordPresetTypes[]; + allow_list?: string[]; + regex_patterns?: string[]; + mention_total_limit?: number; +} \ No newline at end of file