From ef7ae180f44c22246110ed9c45b13662ddcc6891 Mon Sep 17 00:00:00 2001 From: Synbulat Biishev Date: Sun, 16 Oct 2022 19:05:18 +0300 Subject: [PATCH 1/8] feat: add `Message#bulkDeletable` --- packages/discord.js/src/structures/Message.js | 9 +++++++++ packages/discord.js/typings/index.d.ts | 1 + 2 files changed, 10 insertions(+) diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index 7245e004bf61..946b0406f1fa 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -611,6 +611,15 @@ class Message extends Base { ); } + /** + * Whether the message is bulk deletable by the client user + * @type {boolean} + * @readonly + */ + get bulkDeletable() { + return this.deletable && Date.now() - this.createdTimestamp < 1_209_600_000; + } + /** * Whether the message is pinnable by the client user * @type {boolean} diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 9798eb0028a7..9aa857008867 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -1692,6 +1692,7 @@ export class Message extends Base { public applicationId: Snowflake | null; public attachments: Collection; public author: User; + public get bulkDeletable(): boolean; public get channel(): If; public channelId: Snowflake; public get cleanContent(): string; From ae46ff4251204885d9025e1e4c7f33e9351ae42e Mon Sep 17 00:00:00 2001 From: Synbulat Biishev Date: Sun, 16 Oct 2022 19:26:49 +0300 Subject: [PATCH 2/8] feat: add requested changes --- packages/discord.js/src/structures/Message.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index 946b0406f1fa..dd0a6a520dfe 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -615,9 +615,12 @@ 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() { - return this.deletable && Date.now() - this.createdTimestamp < 1_209_600_000; + return this.deletable && this.inGuild() && Date.now() - this.createdTimestamp < 1_209_600_000; } /** From efadae6ff050d1c8db125c97ba7e6d2d59997a2a Mon Sep 17 00:00:00 2001 From: Synbulat Biishev Date: Sun, 16 Oct 2022 20:25:42 +0300 Subject: [PATCH 3/8] fix: add check for `ManageMessages` permission --- packages/discord.js/src/structures/Message.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index dd0a6a520dfe..929461767c6e 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -620,7 +620,13 @@ class Message extends Base { * channel.bulkDelete(messages.filter(message => message.bulkDeletable)); */ get bulkDeletable() { - return this.deletable && this.inGuild() && Date.now() - this.createdTimestamp < 1_209_600_000; + const permissions = this.channel.permissionsFor(this.client.user); + return ( + this.deletable && + this.inGuild() && + permissions.has(PermissionFlagsBits.ManageMessages) && + Date.now() - this.createdTimestamp < 1_209_600_000 + ); } /** From e363405d0b95ccac31b05da4975a79f2995f0772 Mon Sep 17 00:00:00 2001 From: Synbulat Biishev Date: Sun, 16 Oct 2022 20:42:41 +0300 Subject: [PATCH 4/8] fix: `.permissionsFor()` exist only in guild channels --- packages/discord.js/src/structures/Message.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index 929461767c6e..bc12ea2d3c10 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -620,7 +620,7 @@ class Message extends Base { * channel.bulkDelete(messages.filter(message => message.bulkDeletable)); */ get bulkDeletable() { - const permissions = this.channel.permissionsFor(this.client.user); + const permissions = this.channel?.permissionsFor(this.client.user); return ( this.deletable && this.inGuild() && From 4e2ac5ddfb032a5a142af45cf847cc90fdde1127 Mon Sep 17 00:00:00 2001 From: Synbulat Biishev Date: Thu, 27 Oct 2022 19:58:12 +0300 Subject: [PATCH 5/8] feat: apply requested changes --- packages/discord.js/src/structures/Message.js | 8 ++++---- .../src/structures/interfaces/TextBasedChannel.js | 5 ++++- packages/discord.js/src/util/Constants.js | 6 ++++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index bc12ea2d3c10..789eb91b0177 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -22,7 +22,7 @@ const { Sticker } = require('./Sticker'); const { DiscordjsError, ErrorCodes } = require('../errors'); const ReactionManager = require('../managers/ReactionManager'); const { createComponent } = require('../util/Components'); -const { NonSystemMessageTypes } = require('../util/Constants'); +const { NonSystemMessageTypes, MaxBulkDeletableMessageAge } = require('../util/Constants'); const MessageFlagsBitField = require('../util/MessageFlagsBitField'); const PermissionsBitField = require('../util/PermissionsBitField'); const { cleanContent, resolvePartialEmoji } = require('../util/Util'); @@ -622,10 +622,10 @@ class Message extends Base { get bulkDeletable() { const permissions = this.channel?.permissionsFor(this.client.user); return ( - this.deletable && this.inGuild() && - permissions.has(PermissionFlagsBits.ManageMessages) && - Date.now() - this.createdTimestamp < 1_209_600_000 + Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge && + this.deletable && + permissions?.has(PermissionFlagsBits.ManageMessages, false) ); } diff --git a/packages/discord.js/src/structures/interfaces/TextBasedChannel.js b/packages/discord.js/src/structures/interfaces/TextBasedChannel.js index 905b5bac5f92..b769f4d4af88 100644 --- a/packages/discord.js/src/structures/interfaces/TextBasedChannel.js +++ b/packages/discord.js/src/structures/interfaces/TextBasedChannel.js @@ -4,6 +4,7 @@ const { Collection } = require('@discordjs/collection'); const { DiscordSnowflake } = require('@sapphire/snowflake'); const { InteractionType, Routes } = require('discord-api-types/v10'); const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../../errors'); +const { MaxBulkDeletableMessageAge } = require('../../util/Constants'); const InteractionCollector = require('../InteractionCollector'); const MessageCollector = require('../MessageCollector'); const MessagePayload = require('../MessagePayload'); @@ -294,7 +295,9 @@ 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() - DiscordSnowflake.timestampFrom(id) < 1_209_600_000); + messageIds = messageIds.filter( + id => Date.now() - DiscordSnowflake.timestampFrom(id) < MaxBulkDeletableMessageAge, + ); } if (messageIds.length === 0) return new Collection(); if (messageIds.length === 1) { diff --git a/packages/discord.js/src/util/Constants.js b/packages/discord.js/src/util/Constants.js index 61a5d191eac7..61c2beff8b9a 100644 --- a/packages/discord.js/src/util/Constants.js +++ b/packages/discord.js/src/util/Constants.js @@ -2,6 +2,12 @@ const { ChannelType, MessageType } = require('discord-api-types/v10'); +/** + * Max bulk deletable message age + * @typedef {number} MaxBulkDeletableMessageAge + */ +exports.MaxBulkDeletableMessageAge = 1_209_600_000; + /** * The name of an item to be swept in Sweepers * * `applicationCommands` - both global and guild commands From 785d75e0e7e3b666f2df7a2258fef227aacad071 Mon Sep 17 00:00:00 2001 From: Synbulat Biishev Date: Thu, 27 Oct 2022 20:07:26 +0300 Subject: [PATCH 6/8] types: add type --- packages/discord.js/typings/index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index 9aa857008867..6b7eeb531c4a 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -3110,6 +3110,7 @@ export type NonSystemMessageType = | MessageType.ContextMenuCommand; export const Constants: { + MaxBulkDeletableMessageAge: 1_209_600_000; SweeperKeys: SweeperKey[]; NonSystemMessageTypes: NonSystemMessageType[]; TextBasedChannelTypes: TextBasedChannelTypes[]; From ecaf12dfc0d6401af78ab98ae40464494075d3e6 Mon Sep 17 00:00:00 2001 From: Synbulat Biishev Date: Fri, 28 Oct 2022 10:49:50 +0500 Subject: [PATCH 7/8] fix: do not return `undefined` --- packages/discord.js/src/structures/Message.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index 789eb91b0177..d87a4de78f9e 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -625,7 +625,8 @@ class Message extends Base { this.inGuild() && Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge && this.deletable && - permissions?.has(PermissionFlagsBits.ManageMessages, false) + permissions?.has(PermissionFlagsBits.ManageMessages, false) ?? + false ); } From aee0dc99f4fdf825ebe03d995380390ff7acf671 Mon Sep 17 00:00:00 2001 From: Synbulat Biishev Date: Fri, 28 Oct 2022 20:08:31 +0300 Subject: [PATCH 8/8] fix: add property to docs --- packages/discord.js/src/structures/Message.js | 8 ++++---- packages/discord.js/src/util/Constants.js | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/discord.js/src/structures/Message.js b/packages/discord.js/src/structures/Message.js index d87a4de78f9e..6d5bc84d3d4d 100644 --- a/packages/discord.js/src/structures/Message.js +++ b/packages/discord.js/src/structures/Message.js @@ -622,10 +622,10 @@ class Message extends Base { get bulkDeletable() { const permissions = this.channel?.permissionsFor(this.client.user); return ( - this.inGuild() && - Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge && - this.deletable && - permissions?.has(PermissionFlagsBits.ManageMessages, false) ?? + (this.inGuild() && + Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge && + this.deletable && + permissions?.has(PermissionFlagsBits.ManageMessages, false)) ?? false ); } diff --git a/packages/discord.js/src/util/Constants.js b/packages/discord.js/src/util/Constants.js index 61c2beff8b9a..ac012e5f97b0 100644 --- a/packages/discord.js/src/util/Constants.js +++ b/packages/discord.js/src/util/Constants.js @@ -121,6 +121,7 @@ exports.VoiceBasedChannelTypes = [ChannelType.GuildVoice, ChannelType.GuildStage /** * @typedef {Object} Constants Constants that can be used in an enum or object-like way. + * @property {number} MaxBulkDeletableMessageAge Max bulk deletable message age * @property {SweeperKey[]} SweeperKeys The possible names of items that can be swept in sweepers * @property {NonSystemMessageTypes} NonSystemMessageTypes The types of messages that are not deemed a system type * @property {TextBasedChannelTypes} TextBasedChannelTypes The types of channels that are text-based