Skip to content

Commit

Permalink
feat: add Message#bulkDeletable (#8760)
Browse files Browse the repository at this point in the history
* feat: add `Message#bulkDeletable`

* feat: add requested changes

* fix: add check for `ManageMessages` permission

* fix: `.permissionsFor()` exist only in guild channels

* feat: apply requested changes

* types: add type

* fix: do not return `undefined`

* fix: add property to docs

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
Syjalo and kodiakhq[bot] committed Nov 6, 2022
1 parent 68c9cb3 commit ff85481
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 2 deletions.
21 changes: 20 additions & 1 deletion packages/discord.js/src/structures/Message.js
Expand Up @@ -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');
Expand Down Expand Up @@ -611,6 +611,25 @@ 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() {
const permissions = this.channel?.permissionsFor(this.client.user);
return (
(this.inGuild() &&
Date.now() - this.createdTimestamp < MaxBulkDeletableMessageAge &&
this.deletable &&
permissions?.has(PermissionFlagsBits.ManageMessages, false)) ??
false
);
}

/**
* Whether the message is pinnable by the client user
* @type {boolean}
Expand Down
Expand Up @@ -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');
Expand Down Expand Up @@ -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) {
Expand Down
7 changes: 7 additions & 0 deletions packages/discord.js/src/util/Constants.js
Expand Up @@ -2,6 +2,12 @@

const { ChannelType, MessageType, ComponentType } = 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
Expand Down Expand Up @@ -132,6 +138,7 @@ exports.SelectMenuTypes = [

/**
* @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
Expand Down
2 changes: 2 additions & 0 deletions packages/discord.js/typings/index.d.ts
Expand Up @@ -1774,6 +1774,7 @@ export class Message<InGuild extends boolean = boolean> extends Base {
public applicationId: Snowflake | null;
public attachments: Collection<Snowflake, Attachment>;
public author: User;
public get bulkDeletable(): boolean;
public get channel(): If<InGuild, GuildTextBasedChannel, TextBasedChannel>;
public channelId: Snowflake;
public get cleanContent(): string;
Expand Down Expand Up @@ -3294,6 +3295,7 @@ export type NonSystemMessageType =
| MessageType.ContextMenuCommand;

export const Constants: {
MaxBulkDeletableMessageAge: 1_209_600_000;
SweeperKeys: SweeperKey[];
NonSystemMessageTypes: NonSystemMessageType[];
TextBasedChannelTypes: TextBasedChannelTypes[];
Expand Down

0 comments on commit ff85481

Please sign in to comment.