From 41dee5177d9cb15f667e60a34619882222bf249c Mon Sep 17 00:00:00 2001 From: Jiralite <33201955+Jiralite@users.noreply.github.com> Date: Tue, 28 Jan 2025 19:43:20 +0000 Subject: [PATCH] feat: Incident Actions (#10727) * feat: initial commit * feat: add guild helper * docs: `guild` is required * docs(IncidentActions): move to guild * fix: `incidents_data` is nullable * fix: method typo * fix: default to `null` * fix: use `new Date()` * docs: note that it is not received over the gateway * refactor: use transformer * chore: resolve TODO * chore: typo Co-authored-by: Danial Raza * chore: suggestions Co-authored-by: Almeida * chore: consistency Co-authored-by: Almeida --------- Co-authored-by: Danial Raza Co-authored-by: Almeida Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../discord.js/src/managers/GuildManager.js | 34 +++++++++++++++++++ packages/discord.js/src/structures/Guild.js | 31 +++++++++++++++++ packages/discord.js/src/util/APITypes.js | 5 +++ packages/discord.js/src/util/Transformers.js | 16 +++++++++ packages/discord.js/typings/index.d.ts | 18 ++++++++++ 5 files changed, 104 insertions(+) diff --git a/packages/discord.js/src/managers/GuildManager.js b/packages/discord.js/src/managers/GuildManager.js index 1ab9090bd..08913886b 100644 --- a/packages/discord.js/src/managers/GuildManager.js +++ b/packages/discord.js/src/managers/GuildManager.js @@ -18,6 +18,7 @@ const { resolveImage } = require('../util/DataResolver'); const Events = require('../util/Events'); const PermissionsBitField = require('../util/PermissionsBitField'); const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField'); +const { _transformAPIIncidentsData } = require('../util/Transformers.js'); const { resolveColor } = require('../util/Util'); let cacheWarningEmitted = false; @@ -281,6 +282,39 @@ class GuildManager extends CachedManager { return data.reduce((coll, guild) => coll.set(guild.id, new OAuth2Guild(this.client, guild)), new Collection()); } + /** + * Options used to set incident actions. Supplying `null` to any option will disable the action. + * @typedef {Object} IncidentActionsEditOptions + * @property {?DateResolvable} [invitesDisabledUntil] When invites should be enabled again + * @property {?DateResolvable} [dmsDisabledUntil] When direct messages should be enabled again + */ + + /** + * Sets the incident actions for a guild. + * @param {GuildResolvable} guild The guild + * @param {IncidentActionsEditOptions} incidentActions The incident actions to set + * @returns {Promise} + */ + async setIncidentActions(guild, { invitesDisabledUntil, dmsDisabledUntil }) { + const guildId = this.resolveId(guild); + + const data = await this.client.rest.put(Routes.guildIncidentActions(guildId), { + body: { + invites_disabled_until: invitesDisabledUntil && new Date(invitesDisabledUntil).toISOString(), + dms_disabled_until: dmsDisabledUntil && new Date(dmsDisabledUntil).toISOString(), + }, + }); + + const parsedData = _transformAPIIncidentsData(data); + const resolvedGuild = this.resolve(guild); + + if (resolvedGuild) { + resolvedGuild.incidentsData = parsedData; + } + + return parsedData; + } + /** * Returns a URL for the PNG widget of a guild. * @param {GuildResolvable} guild The guild of the widget image diff --git a/packages/discord.js/src/structures/Guild.js b/packages/discord.js/src/structures/Guild.js index 6fc78f07a..7c755a539 100644 --- a/packages/discord.js/src/structures/Guild.js +++ b/packages/discord.js/src/structures/Guild.js @@ -29,6 +29,7 @@ const VoiceStateManager = require('../managers/VoiceStateManager'); const { resolveImage } = require('../util/DataResolver'); const Status = require('../util/Status'); const SystemChannelFlagsBitField = require('../util/SystemChannelFlagsBitField'); +const { _transformAPIIncidentsData } = require('../util/Transformers.js'); const { discordSort, getSortableGroupTypes, resolvePartialEmoji } = require('../util/Util'); /** @@ -470,6 +471,27 @@ class Guild extends AnonymousGuild { stickers: data.stickers, }); } + + if ('incidents_data' in data) { + /** + * Incident actions of a guild. + * @typedef {Object} IncidentActions + * @property {?Date} invitesDisabledUntil When invites would be enabled again + * @property {?Date} dmsDisabledUntil When direct messages would be enabled again + * @property {?Date} dmSpamDetectedAt When direct message spam was detected + * @property {?Date} raidDetectedAt When a raid was detected + */ + + /** + * The incidents data of this guild. + * You will need to fetch the guild using {@link BaseGuild#fetch} if you want to receive + * this property. + * @type {?IncidentActions} + */ + this.incidentsData = data.incidents_data && _transformAPIIncidentsData(data.incidents_data); + } else { + this.incidentsData ??= null; + } } /** @@ -1365,6 +1387,15 @@ class Guild extends AnonymousGuild { return this.edit({ features }); } + /** + * Sets the incident actions for a guild. + * @param {IncidentActionsEditOptions} incidentActions The incident actions to set + * @returns {Promise} + */ + async setIncidentActions(incidentActions) { + return this.client.guilds.setIncidentActions(this.id, incidentActions); + } + /** * Whether this guild equals another guild. It compares all properties, so for most operations * it is advisable to just compare `guild.id === guild2.id` as it is much faster and is often diff --git a/packages/discord.js/src/util/APITypes.js b/packages/discord.js/src/util/APITypes.js index 44f7f80ff..8d8abbd9f 100644 --- a/packages/discord.js/src/util/APITypes.js +++ b/packages/discord.js/src/util/APITypes.js @@ -105,6 +105,11 @@ * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIGuildScheduledEventRecurrenceRule} */ +/** + * @external APIIncidentsData + * @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIIncidentsData} + */ + /** * @external APIInteraction * @see {@link https://discord-api-types.dev/api/discord-api-types-v10#APIInteraction} diff --git a/packages/discord.js/src/util/Transformers.js b/packages/discord.js/src/util/Transformers.js index e37fb456c..65f530b23 100644 --- a/packages/discord.js/src/util/Transformers.js +++ b/packages/discord.js/src/util/Transformers.js @@ -76,9 +76,25 @@ function _transformGuildScheduledEventRecurrenceRule(recurrenceRule) { }; } +/** + * Transforms API incidents data to a camel-cased variant. + * @param {APIIncidentsData} data The incidents data to transform + * @returns {IncidentActions} + * @ignore + */ +function _transformAPIIncidentsData(data) { + return { + invitesDisabledUntil: data.invites_disabled_until ? new Date(data.invites_disabled_until) : null, + dmsDisabledUntil: data.dms_disabled_until ? new Date(data.dms_disabled_until) : null, + dmSpamDetectedAt: data.dm_spam_detected_at ? new Date(data.dm_spam_detected_at) : null, + raidDetectedAt: data.raid_detected_at ? new Date(data.raid_detected_at) : null, + }; +} + module.exports = { toSnakeCase, _transformAPIAutoModerationAction, _transformAPIMessageInteractionMetadata, _transformGuildScheduledEventRecurrenceRule, + _transformAPIIncidentsData }; diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index bc941213f..27200eb1e 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -1510,6 +1510,7 @@ export class Guild extends AnonymousGuild { public shardId: number; public stageInstances: StageInstanceManager; public stickers: GuildStickerManager; + public incidentsData: IncidentActions | null; public get systemChannel(): TextChannel | null; public systemChannelFlags: Readonly; public systemChannelId: Snowflake | null; @@ -1543,6 +1544,7 @@ export class Guild extends AnonymousGuild { public widgetImageURL(style?: GuildWidgetStyle): string; public leave(): Promise; public disableInvites(disabled?: boolean): Promise; + public setIncidentActions(incidentActions: IncidentActionsEditOptions): Promise; public setAFKChannel(afkChannel: VoiceChannelResolvable | null, reason?: string): Promise; public setAFKTimeout(afkTimeout: number, reason?: string): Promise; public setBanner(banner: BufferResolvable | Base64Resolvable | null, reason?: string): Promise; @@ -4542,6 +4544,10 @@ export class GuildManager extends CachedManager; public fetch(options: Snowflake | FetchGuildOptions): Promise; public fetch(options?: FetchGuildsOptions): Promise>; + public setIncidentActions( + guild: GuildResolvable, + incidentActions: IncidentActionsEditOptions, + ): Promise; public widgetImageURL(guild: GuildResolvable, style?: GuildWidgetStyle): string; } @@ -6462,6 +6468,18 @@ export interface GuildOnboardingPromptOptionData { export type HexColorString = `#${string}`; +export interface IncidentActions { + invitesDisabledUntil: Date | null; + dmsDisabledUntil: Date | null; + dmSpamDetectedAt: Date | null; + raidDetectedAt: Date | null; +} + +export interface IncidentActionsEditOptions { + invitesDisabledUntil?: DateResolvable | null | undefined; + dmsDisabledUntil?: DateResolvable | null | undefined; +} + export interface IntegrationAccount { id: string | Snowflake; name: string;