Skip to content

feat: Incident Actions #10727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 28, 2025
Merged
37 changes: 37 additions & 0 deletions packages/discord.js/src/managers/GuildManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const { resolveImage } = require('../util/DataResolver.js');
const { Events } = require('../util/Events.js');
const { PermissionsBitField } = require('../util/PermissionsBitField.js');
const { SystemChannelFlagsBitField } = require('../util/SystemChannelFlagsBitField.js');
const { _transformAPIIncidentsData } = require('../util/Transformers.js');
const { resolveColor } = require('../util/Util.js');

let cacheWarningEmitted = false;
Expand Down Expand Up @@ -281,6 +282,42 @@ 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. Suppling `null` to any option will disable the action.
* @typedef {Object} IncidentsActionsEditOptions
* @property {?DateResolvable} [invitesDisabledUntil] When invites should be enabled again
* @property {?DateResolvable} [dmsDisabledUntil] When direct messages should be enabled again
*/

/**
* Sets the incidents actions for a guild.
* @param {GuildResolvable} guild The guild
* @param {IncidentsActionsEditOptions} incidentActions The incidents actions to set
* @returns {Promise<IncidentActions>}
*/
async setIncidentActions(guild, { invitesDisabledUntil, dmsDisabledUntil }) {
const guildId = this.resolveId(guild);

const data = await this.client.rest.put(Routes.guildIncidentActions(guildId), {
body: {
/* eslint-disable eqeqeq */
invites_disabled_until:
invitesDisabledUntil != null ? new Date(invitesDisabledUntil).toISOString() : invitesDisabledUntil,
dms_disabled_until: dmsDisabledUntil != null ? new Date(dmsDisabledUntil).toISOString() : dmsDisabledUntil,
/* eslint-enable eqeqeq */
},
});

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
Expand Down
31 changes: 31 additions & 0 deletions packages/discord.js/src/structures/Guild.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const { StageInstanceManager } = require('../managers/StageInstanceManager.js');
const { VoiceStateManager } = require('../managers/VoiceStateManager.js');
const { resolveImage } = require('../util/DataResolver.js');
const { SystemChannelFlagsBitField } = require('../util/SystemChannelFlagsBitField.js');
const { _transformAPIIncidentsData } = require('../util/Transformers.js');
const { discordSort, getSortableGroupTypes, resolvePartialEmoji } = require('../util/Util.js');

/**
Expand Down Expand Up @@ -460,6 +461,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.
* <info>You will need to fetch the guild using {@link BaseGuild#fetch} if you want to receive
* this property.</info>
* @type {?IncidentActions}
*/
this.incidentsData = data.incidents_data ? _transformAPIIncidentsData(data.incidents_data) : null;
} else {
this.incidentsData ??= null;
}
}

/**
Expand Down Expand Up @@ -1355,6 +1377,15 @@ class Guild extends AnonymousGuild {
return this.edit({ features });
}

/**
* Sets the incidents actions for a guild.
* @param {IncidentsActionsEditOptions} incidentActions The incidents actions to set
* @returns {Promise<IncidentActions>}
*/
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
Expand Down
5 changes: 5 additions & 0 deletions packages/discord.js/src/util/APITypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
20 changes: 20 additions & 0 deletions packages/discord.js/src/util/Transformers.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,27 @@ 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)
: data.invites_disabled_until,
dmsDisabledUntil: data.dms_disabled_until ? new Date(data.dms_disabled_until) : data.dms_disabled_until,
dmSpamDetectedAt: data.dm_spam_detected_at
? new Date(data.dm_spam_detected_at)
: (data.dm_spam_detected_at ?? null),
raidDetectedAt: data.raid_detected_at ? new Date(data.raid_detected_at) : (data.raid_detected_at ?? null),
};
}

exports.toSnakeCase = toSnakeCase;
exports._transformAPIAutoModerationAction = _transformAPIAutoModerationAction;
exports._transformAPIMessageInteractionMetadata = _transformAPIMessageInteractionMetadata;
exports._transformGuildScheduledEventRecurrenceRule = _transformGuildScheduledEventRecurrenceRule;
exports._transformAPIIncidentsData = _transformAPIIncidentsData;
18 changes: 18 additions & 0 deletions packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,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<SystemChannelFlagsBitField>;
public systemChannelId: Snowflake | null;
Expand Down Expand Up @@ -1446,6 +1447,7 @@ export class Guild extends AnonymousGuild {
public widgetImageURL(style?: GuildWidgetStyle): string;
public leave(): Promise<Guild>;
public disableInvites(disabled?: boolean): Promise<Guild>;
public setIncidentActions(incidentActions: IncidentsActionsEditOptions): Promise<IncidentActions>;
public setAFKChannel(afkChannel: VoiceChannelResolvable | null, reason?: string): Promise<Guild>;
public setAFKTimeout(afkTimeout: number, reason?: string): Promise<Guild>;
public setBanner(banner: BufferResolvable | Base64Resolvable | null, reason?: string): Promise<Guild>;
Expand Down Expand Up @@ -4172,6 +4174,10 @@ export class GuildManager extends CachedManager<Snowflake, Guild, GuildResolvabl
public create(options: GuildCreateOptions): Promise<Guild>;
public fetch(options: Snowflake | FetchGuildOptions): Promise<Guild>;
public fetch(options?: FetchGuildsOptions): Promise<Collection<Snowflake, OAuth2Guild>>;
public setIncidentActions(
guild: GuildResolvable,
incidentActions: IncidentsActionsEditOptions,
): Promise<IncidentActions>;
public widgetImageURL(guild: GuildResolvable, style?: GuildWidgetStyle): string;
}

Expand Down Expand Up @@ -6064,6 +6070,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 IncidentsActionsEditOptions {
invitesDisabledUntil?: DateResolvable | null | undefined;
dmsDisabledUntil?: DateResolvable | null | undefined;
}

export interface IntegrationAccount {
id: string | Snowflake;
name: string;
Expand Down
Loading