diff --git a/src/managers/AutoModerationRuleManager.js b/src/managers/AutoModerationRuleManager.js index 0a0aeac26..c6dd3399c 100644 --- a/src/managers/AutoModerationRuleManager.js +++ b/src/managers/AutoModerationRuleManager.js @@ -58,6 +58,7 @@ class AutoModerationRuleManager extends CachedManager { * @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 + * @property {boolean} [mentionRaidProtectionEnabled] Whether to automatically detect mention raids */ /** @@ -126,6 +127,7 @@ class AutoModerationRuleManager extends CachedManager { ), allow_list: triggerMetadata.allowList, mention_total_limit: triggerMetadata.mentionTotalLimit, + mention_raid_protection_enabled: triggerMetadata.mentionRaidProtectionEnabled, }, actions: actions.map(action => ({ type: typeof action.type === 'number' ? action.type : AutoModerationActionTypes[action.type], @@ -188,6 +190,7 @@ class AutoModerationRuleManager extends CachedManager { ), allow_list: triggerMetadata.allowList, mention_total_limit: triggerMetadata.mentionTotalLimit, + mention_raid_protection_enabled: triggerMetadata.mentionRaidProtectionEnabled, }, actions: actions?.map(action => ({ type: typeof action.type === 'number' ? action.type : AutoModerationActionTypes[action.type], diff --git a/src/structures/AutoModerationRule.js b/src/structures/AutoModerationRule.js index df6a428ac..fbc6f021a 100644 --- a/src/structures/AutoModerationRule.js +++ b/src/structures/AutoModerationRule.js @@ -73,6 +73,7 @@ class AutoModerationRule extends Base { * @property {string[]} allowList The substrings that will be exempt from triggering * {@link AutoModerationRuleTriggerTypes.KEYWORD} and {@link AutoModerationRuleTriggerTypes.KEYWORD_PRESET} * @property {?number} mentionTotalLimit The total number of role & user mentions allowed per message + * @property {boolean} mentionRaidProtectionEnabled Whether mention raid protection is enabled */ /** @@ -85,6 +86,7 @@ class AutoModerationRule extends Base { presets: data.trigger_metadata.presets?.map(preset => AutoModerationRuleKeywordPresetTypes[preset]) ?? [], allowList: data.trigger_metadata.allow_list ?? [], mentionTotalLimit: data.trigger_metadata.mention_total_limit ?? null, + mentionRaidProtectionEnabled: data.trigger_metadata.mention_raid_protection_enabled ?? false, }; } @@ -236,6 +238,17 @@ class AutoModerationRule extends Base { return this.edit({ triggerMetadata: { ...this.triggerMetadata, mentionTotalLimit }, reason }); } + /** + * Sets whether to enable mention raid protection for this auto moderation rule. + * @param {boolean} mentionRaidProtectionEnabled + * Whether to enable mention raid protection for this auto moderation rule + * @param {string} [reason] The reason for changing the mention raid protection of this auto moderation rule + * @returns {Promise} + */ + setMentionRaidProtectionEnabled(mentionRaidProtectionEnabled, reason) { + return this.edit({ triggerMetadata: { ...this.triggerMetadata, mentionRaidProtectionEnabled }, reason }); + } + /** * Sets the actions for this auto moderation rule. * @param {AutoModerationActionOptions[]} actions The actions of this auto moderation rule diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 100799d69..4c69263ae 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -255,6 +255,7 @@ class Guild extends AnonymousGuild { * * SEVEN_DAY_THREAD_ARCHIVE * * PRIVATE_THREADS * * ROLE_ICONS + * * RAID_ALERTS_DISABLED * * ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE * * ROLE_SUBSCRIPTIONS_ENABLED * @typedef {string} Features @@ -442,6 +443,16 @@ class Guild extends AnonymousGuild { this.preferredLocale = data.preferred_locale; } + if ('safety_alerts_channel_id' in data) { + /** + * The safety alerts channel's id for the guild + * @type {?Snowflake} + */ + this.safetyAlertsChannelId = data.safety_alerts_channel_id; + } else { + this.safetyAlertsChannelId ??= null; + } + if (data.channels) { this.channels.cache.clear(); for (const rawChannel of data.channels) { @@ -575,6 +586,15 @@ class Guild extends AnonymousGuild { return this.client.channels.resolve(this.systemChannelId); } + /** + * Safety alerts channel for this guild + * @type {?TextChannel} + * @readonly + */ + get safetyAlertsChannel() { + return this.client.channels.resolve(this.safetyAlertsChannelId); + } + /** * Widget channel for this guild * @type {?(TextChannel|NewsChannel|VoiceChannel|StageChannel|ForumChannel)} @@ -839,6 +859,7 @@ class Guild extends AnonymousGuild { * @property {?TextChannelResolvable} [rulesChannel] The rules channel of the guild * @property {?TextChannelResolvable} [publicUpdatesChannel] The community updates channel of the guild * @property {?string} [preferredLocale] The preferred locale of the guild + * @property {?TextChannelResolvable} [safetyAlertsChannel] The safety alerts channel of the guild * @property {boolean} [premiumProgressBarEnabled] Whether the guild's premium progress bar is enabled * @property {?string} [description] The discovery description of the guild * @property {Features[]} [features] The features of the guild @@ -922,6 +943,9 @@ class Guild extends AnonymousGuild { _data.description = data.description; } if (typeof data.preferredLocale !== 'undefined') _data.preferred_locale = data.preferredLocale; + if (typeof data.safetyAlertsChannel !== 'undefined') { + _data.safety_alerts_channel_id = this.client.channels.resolveId(data.safetyAlertsChannel); + } if ('premiumProgressBarEnabled' in data) _data.premium_progress_bar_enabled = data.premiumProgressBarEnabled; const newData = await this.client.api.guilds(this.id).patch({ data: _data, reason }); return this.client.actions.GuildUpdate.handle(newData).updated; @@ -1224,6 +1248,21 @@ class Guild extends AnonymousGuild { return this.edit({ preferredLocale }, reason); } + /** + * Edits the safety alerts channel of the guild. + * @param {?TextChannelResolvable} safetyAlertsChannel The new safety alerts channel + * @param {string} [reason] Reason for changing the guild's safety alerts channel + * @returns {Promise} + * @example + * // Edit the guild safety alerts channel + * guild.setSafetyAlertsChannel(channel) + * .then(updated => console.log(`Updated guild safety alerts channel to ${updated.safetyAlertsChannel.name}`)) + * .catch(console.error); + */ + setSafetyAlertsChannel(safetyAlertsChannel, reason) { + return this.edit({ safetyAlertsChannel }, reason); + } + /** * Edits the enabled state of the guild's premium progress bar * @param {boolean} [enabled=true] The new enabled state of the guild's premium progress bar diff --git a/typings/index.d.ts b/typings/index.d.ts index 36ee40293..116d4fbb7 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -1013,6 +1013,8 @@ export class Guild extends AnonymousGuild { public roles: RoleManager; public readonly rulesChannel: TextChannel | null; public rulesChannelId: Snowflake | null; + public readonly safetyAlertsChannel: TextChannel | null; + public safetyAlertsChannelId: Snowflake | null; public scheduledEvents: GuildScheduledEventManager; public readonly shard: WebSocketShard; public shardId: number; @@ -1073,6 +1075,7 @@ export class Guild extends AnonymousGuild { /** @deprecated Use {@link RoleManager.setPositions} instead */ public setRolePositions(rolePositions: readonly RolePosition[]): Promise; public setRulesChannel(rulesChannel: TextChannelResolvable | null, reason?: string): Promise; + public setSafetyAlertsChannel(safetyAlertsChannel: TextChannelResolvable | null, reason?: string): Promise; public setSplash(splash: BufferResolvable | Base64Resolvable | null, reason?: string): Promise; public setSystemChannel(systemChannel: TextChannelResolvable | null, reason?: string): Promise; public setSystemChannelFlags(systemChannelFlags: SystemChannelFlagsResolvable, reason?: string): Promise; @@ -4227,6 +4230,10 @@ export class AutoModerationRule extends Base { public setRegexPatterns(regexPatterns: string[], reason?: string): Promise; public setPresets(presets: AutoModerationRuleKeywordPresetType[], reason?: string): Promise; public setAllowList(allowList: string[], reason?: string): Promise; + public setMentionRaidProtectionEnabled( + mentionRaidProtectionEnabled: boolean, + reason?: string, + ): Promise; public setMentionTotalLimit(mentionTotalLimit: number, reason?: string): Promise; public setActions(actions: AutoModerationActionOptions[], reason?: string): Promise; public setEnabled(enabled?: boolean, reason?: string): Promise; @@ -4315,6 +4322,7 @@ export interface AutoModerationTriggerMetadata { regexPatterns: string[]; presets: (AutoModerationRuleKeywordPresetType | AutoModerationRuleKeywordPresetTypes)[]; allowList: string[]; + mentionRaidProtectionEnabled: boolean; mentionTotalLimit: number | null; } @@ -5419,6 +5427,7 @@ export interface GuildEditData { rulesChannel?: TextChannelResolvable | null; publicUpdatesChannel?: TextChannelResolvable | null; preferredLocale?: string | null; + safetyAlertsChannel?: TextChannelResolvable | null; premiumProgressBarEnabled?: boolean; description?: string | null; features?: GuildFeatures[]; @@ -5471,6 +5480,7 @@ export type GuildFeatures = | 'SEVEN_DAY_THREAD_ARCHIVE' | 'PRIVATE_THREADS' | 'ROLE_ICONS' + | 'RAID_ALERTS_DISABLED' | 'ROLE_SUBSCRIPTIONS_AVAILABLE_FOR_PURCHASE' | 'ROLE_SUBSCRIPTIONS_ENABLED'; diff --git a/typings/rawDataTypes.d.ts b/typings/rawDataTypes.d.ts index 72268b762..b551fe937 100644 --- a/typings/rawDataTypes.d.ts +++ b/typings/rawDataTypes.d.ts @@ -11,7 +11,6 @@ import { APIChannel, APIEmoji, APIExtendedInvite, - APIGuild, APIGuildIntegration, APIGuildIntegrationApplication, APIGuildMember, @@ -79,6 +78,16 @@ import { APITextInputComponent, APIModalActionRowComponent, APIModalSubmitInteraction, + Permissions, + GuildDefaultMessageNotifications, + GuildExplicitContentFilter, + GuildMFALevel, + GuildSystemChannelFlags, + GuildPremiumTier, + GuildNSFWLevel, + GuildHubType, + GuildVerificationLevel, + GuildFeature, LocalizationMap } from 'discord-api-types/v9'; import { GuildChannel, Guild, PermissionOverwrites, InteractionType } from '.'; @@ -282,6 +291,50 @@ export interface APIAutoModerationRuleTriggerMetadata { allow_list?: string[]; regex_patterns?: string[]; mention_total_limit?: number; + mention_raid_protection_enabled?: boolean; +} + + +export interface APIGuild extends APIPartialGuild { + icon_hash?: string | null; + discovery_splash: string | null; + owner?: boolean; + owner_id: Snowflake; + permissions?: Permissions; + region: string; + afk_channel_id: Snowflake | null; + afk_timeout: number; + widget_enabled?: boolean; + widget_channel_id?: Snowflake | null; + verification_level: GuildVerificationLevel; + default_message_notifications: GuildDefaultMessageNotifications; + explicit_content_filter: GuildExplicitContentFilter; + roles: APIRole[]; + emojis: APIEmoji[]; + features: GuildFeature[]; + mfa_level: GuildMFALevel; + application_id: Snowflake | null; + system_channel_id: Snowflake | null; + system_channel_flags: GuildSystemChannelFlags; + rules_channel_id: Snowflake | null; + max_presences?: number | null; + max_members?: number; + vanity_url_code: string | null; + description: string | null; + banner: string | null; + premium_tier: GuildPremiumTier; + premium_subscription_count?: number; + preferred_locale: string; + public_updates_channel_id: Snowflake | null; + max_video_channel_users?: number; + approximate_member_count?: number; + approximate_presence_count?: number; + welcome_screen?: APIGuildWelcomeScreen; + nsfw_level: GuildNSFWLevel; + stickers: APISticker[]; + premium_progress_bar_enabled: boolean; + hub_type: GuildHubType | null; + safety_alerts_channel_id: Snowflake | null; } export interface APIApplicationRoleConnectionMetadata {