From f8a8ac7fc64ffc4247c71830a1c5fd66c2528825 Mon Sep 17 00:00:00 2001 From: March 7th <71698422+aiko-chan-ai@users.noreply.github.com> Date: Sat, 26 Nov 2022 22:17:33 +0700 Subject: [PATCH] feat: add Message#bulkDeletable (v13) #8761 djs --- src/errors/Messages.js | 1 + src/structures/Message.js | 22 ++++++++++++++++++- src/structures/interfaces/TextBasedChannel.js | 4 ++-- src/util/Constants.js | 7 ++++++ typings/index.d.ts | 19 +++++++++++++++- 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 133411b..0364a48 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -154,6 +154,7 @@ const Messages = { INTERACTION_ALREADY_REPLIED: 'The reply to this interaction has already been sent or deferred.', INTERACTION_NOT_REPLIED: 'The reply to this interaction has not been sent or deferred.', + /** @deprecated */ INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be deleted.', COMMAND_INTERACTION_OPTION_NOT_FOUND: name => `Required option "${name}" not found.`, diff --git a/src/structures/Message.js b/src/structures/Message.js index 0c1d9ea..11ee800 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -15,7 +15,7 @@ const ReactionCollector = require('./ReactionCollector'); const { Sticker } = require('./Sticker'); const { Error } = require('../errors'); const ReactionManager = require('../managers/ReactionManager'); -const { InteractionTypes, MessageTypes, SystemMessageTypes } = require('../util/Constants'); +const { InteractionTypes, MessageTypes, SystemMessageTypes, MaxBulkDeletableMessageAge } = require('../util/Constants'); const MessageFlags = require('../util/MessageFlags'); const Permissions = require('../util/Permissions'); const SnowflakeUtil = require('../util/SnowflakeUtil'); @@ -641,6 +641,26 @@ class Message extends Base { ); } + /** + * Whether the message is bulk deletable by the client user + * @type {boolean} + * @readonly + * @example + * // Filter for bulk deletable messages + * channel.bulkDelete(messages.filter(message => message.bulkDeletable)); + */ + get bulkDeletable() { + if (!this.client.user.bot) return false; + const permissions = this.channel?.permissionsFor(this.client.user); + return ( + (this.inGuild() && + Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge && + this.deletable && + permissions?.has(Permissions.FLAGS.MANAGE_MESSAGES, false)) ?? + false + ); + } + /** * Whether the message is pinnable by the client user * @type {boolean} diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index aaa46bf..3a70462 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -6,7 +6,7 @@ const MessageCollector = require('../MessageCollector'); const MessagePayload = require('../MessagePayload'); const SnowflakeUtil = require('../../util/SnowflakeUtil'); const { Collection } = require('@discordjs/collection'); -const { InteractionTypes } = require('../../util/Constants'); +const { InteractionTypes, MaxBulkDeletableMessageAge } = require('../../util/Constants'); const { TypeError, Error } = require('../../errors'); const InteractionCollector = require('../InteractionCollector'); const { lazy } = require('../../util/Util'); @@ -314,7 +314,7 @@ class TextBasedChannel { if (Array.isArray(messages) || messages instanceof Collection) { let messageIds = messages instanceof Collection ? [...messages.keys()] : messages.map(m => m.id ?? m); if (filterOld) { - messageIds = messageIds.filter(id => Date.now() - SnowflakeUtil.timestampFrom(id) < 1_209_600_000); + messageIds = messageIds.filter(id => Date.now() - SnowflakeUtil.timestampFrom(id) < MaxBulkDeletableMessageAge); } if (messageIds.length === 0) return new Collection(); if (messageIds.length === 1) { diff --git a/src/util/Constants.js b/src/util/Constants.js index 5b9a648..643249e 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -30,6 +30,12 @@ const listUserAgent = [ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.52', ]; +/** + * Max bulk deletable message age + * @typedef {number} MaxBulkDeletableMessageAge + */ +exports.MaxBulkDeletableMessageAge = 1_209_600_000; + /** * API captcha solver * * `2captcha` - 2captcha.com @@ -1738,6 +1744,7 @@ function createEnum(keys) { * @property {SweeperKey[]} SweeperKeys The name of an item to be swept in Sweepers. * @property {SystemMessageType[]} SystemMessageTypes The types of messages that are `System`. * @property {Object} TextInputStyles The style of a text input component. + * @property {number} MaxBulkDeletableMessageAge Max bulk deletable message age * @property {string} UserAgent The user agent used for requests. * @property {Object} VerificationLevels * The value set for the verification levels for a guild. diff --git a/typings/index.d.ts b/typings/index.d.ts index c574a9c..5c62dfd 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -82,6 +82,9 @@ import { MessageTypes, ModalComponentTypes, MFALevels, + NitroType as NitroTypes, + HypeSquadType as HypeSquadTypes, + localeSetting as localeSettings, NSFWLevels, OverwriteTypes, PremiumTiers, @@ -1903,6 +1906,7 @@ export class Message extends Base { public applicationId: Snowflake | null; public attachments: Collection; public author: User; + public readonly bulkDeletable: boolean; public readonly channel: If; public channelId: Snowflake; public readonly cleanContent: string; @@ -3402,9 +3406,10 @@ export const Constants: { devDependencies: Record; [key: string]: unknown; }; + MaxBulkDeletableMessageAge: 1_209_600_000; + UserAgent: string; Endpoints: { botGateway: string; - userGateway: string; invite: (root: string, code: string, eventId?: Snowflake) => string; scheduledEvent: (root: string, guildId: Snowflake, eventId: Snowflake) => string; CDN: (root: string) => { @@ -3507,6 +3512,8 @@ export const Constants: { InteractionResponseTypes: EnumHolder; MessageComponentTypes: EnumHolder; MessageButtonStyles: EnumHolder; + ModalComponentTypes: EnumHolder; + TextInputStyles: EnumHolder; MFALevels: EnumHolder; NSFWLevels: EnumHolder; PrivacyLevels: EnumHolder; @@ -3516,7 +3523,17 @@ export const Constants: { GuildScheduledEventEntityTypes: EnumHolder; GuildScheduledEventStatuses: EnumHolder; GuildScheduledEventPrivacyLevels: EnumHolder; + VideoQualityModes: EnumHolder; + SweeperKeys: SweeperKey[]; + // Add randomUA: () => string; + captchaServices: string[]; + DMScanLevel: EnumHolder; + stickerAnimationMode: EnumHolder; + NitroType: EnumHolder; + HypeSquadType: EnumHolder; + localeSetting: EnumHolder; + userGateway: string; }; export const version: string;