diff --git a/packages/discord.js/src/client/Client.js b/packages/discord.js/src/client/Client.js index c37a6cf0a..95792ee9f 100644 --- a/packages/discord.js/src/client/Client.js +++ b/packages/discord.js/src/client/Client.js @@ -1,7 +1,6 @@ 'use strict'; const process = require('node:process'); -const { setInterval } = require('node:timers'); const { Collection } = require('@discordjs/collection'); const BaseClient = require('./BaseClient'); const ActionsManager = require('./actions/ActionsManager'); @@ -75,20 +74,6 @@ class Client extends BaseClient { this._validateOptions(); - /** - * Functions called when a cache is garbage collected or the Client is destroyed - * @type {Set} - * @private - */ - this._cleanups = new Set(); - - /** - * The finalizers used to cleanup items. - * @type {FinalizationRegistry} - * @private - */ - this._finalizers = new FinalizationRegistry(this._finalize.bind(this)); - /** * The WebSocket manager of the client * @type {WebSocketManager} @@ -182,17 +167,6 @@ class Client extends BaseClient { * @type {?Date} */ this.readyAt = null; - - if (this.options.messageSweepInterval > 0) { - process.emitWarning( - 'The message sweeping client options are deprecated, use the global sweepers instead.', - 'DeprecationWarning', - ); - this.sweepMessageInterval = setInterval( - this.sweepMessages.bind(this), - this.options.messageSweepInterval * 1_000, - ).unref(); - } } /** @@ -275,11 +249,6 @@ class Client extends BaseClient { destroy() { super.destroy(); - for (const fn of this._cleanups) fn(); - this._cleanups.clear(); - - if (this.sweepMessageInterval) clearInterval(this.sweepMessageInterval); - this.sweepers.destroy(); this.ws.destroy(); this.token = null; @@ -381,50 +350,6 @@ class Client extends BaseClient { const data = await this.api('sticker-packs').get(); return new Collection(data.sticker_packs.map(p => [p.id, new StickerPack(this, p)])); } - /** - * A last ditch cleanup function for garbage collection. - * @param {Function} options.cleanup The function called to GC - * @param {string} [options.message] The message to send after a successful GC - * @param {string} [options.name] The name of the item being GCed - * @private - */ - _finalize({ cleanup, message, name }) { - try { - cleanup(); - this._cleanups.delete(cleanup); - if (message) { - this.emit(Events.DEBUG, message); - } - } catch { - this.emit(Events.DEBUG, `Garbage collection failed on ${name ?? 'an unknown item'}.`); - } - } - - /** - * Sweeps all text-based channels' messages and removes the ones older than the max message lifetime. - * If the message has been edited, the time of the edit is used rather than the time of the original message. - * @param {number} [lifetime=this.options.messageCacheLifetime] Messages that are older than this (in seconds) - * will be removed from the caches. The default is based on {@link ClientOptions#messageCacheLifetime} - * @returns {number} Amount of messages that were removed from the caches, - * or -1 if the message cache lifetime is unlimited - * @example - * // Remove all messages older than 1800 seconds from the messages cache - * const amount = client.sweepMessages(1800); - * console.log(`Successfully removed ${amount} messages from the cache.`); - */ - sweepMessages(lifetime = this.options.messageCacheLifetime) { - if (typeof lifetime !== 'number' || isNaN(lifetime)) { - throw new TypeError('INVALID_TYPE', 'lifetime', 'number'); - } - if (lifetime <= 0) { - this.emit(Events.DEBUG, "Didn't sweep messages - lifetime is unlimited"); - return -1; - } - - const messages = this.sweepers.sweepMessages(Sweepers.outdatedMessageSweepFilter(lifetime)()); - this.emit(Events.DEBUG, `Swept ${messages} messages older than ${lifetime} seconds`); - return messages; - } /** * Obtains a guild preview from Discord, available for all guilds the bot is in and all Discoverable guilds. @@ -559,12 +484,6 @@ class Client extends BaseClient { if (typeof options.makeCache !== 'function') { throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function'); } - if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'The messageCacheLifetime', 'a number'); - } - if (typeof options.messageSweepInterval !== 'number' || isNaN(options.messageSweepInterval)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'messageSweepInterval', 'a number'); - } if (typeof options.sweepers !== 'object' || options.sweepers === null) { throw new TypeError('CLIENT_INVALID_OPTION', 'sweepers', 'an object'); } diff --git a/packages/discord.js/src/managers/CachedManager.js b/packages/discord.js/src/managers/CachedManager.js index 0f7e91407..1058285d3 100644 --- a/packages/discord.js/src/managers/CachedManager.js +++ b/packages/discord.js/src/managers/CachedManager.js @@ -1,7 +1,6 @@ 'use strict'; const DataManager = require('./DataManager'); -const { _cleanupSymbol } = require('../util/Constants'); /** * Manages the API methods of a data model with a mutable cache of instances. @@ -14,19 +13,6 @@ class CachedManager extends DataManager { Object.defineProperty(this, '_cache', { value: this.client.options.makeCache(this.constructor, this.holds) }); - let cleanup = this._cache[_cleanupSymbol]?.(); - if (cleanup) { - cleanup = cleanup.bind(this._cache); - client._cleanups.add(cleanup); - client._finalizers.register(this, { - cleanup, - message: - `Garbage collection completed on ${this.constructor.name}, ` + - `which had a ${this._cache.constructor.name} of ${this.holds.name}.`, - name: this.constructor.name, - }); - } - if (iterable) { for (const item of iterable) { this._add(item); diff --git a/packages/discord.js/src/managers/ChannelManager.js b/packages/discord.js/src/managers/ChannelManager.js index 06b25dc74..95795cca4 100644 --- a/packages/discord.js/src/managers/ChannelManager.js +++ b/packages/discord.js/src/managers/ChannelManager.js @@ -16,8 +16,8 @@ class ChannelManager extends CachedManager { super(client, Channel, iterable); const defaultCaching = this._cache.constructor.name === 'Collection' || - ((this._cache.maxSize === undefined || this._cache.maxSize === Infinity) && - (this._cache.sweepFilter === undefined || this._cache.sweepFilter.isDefault)); + this._cache.maxSize === undefined || + this._cache.maxSize === Infinity; if (!cacheWarningEmitted && !defaultCaching) { cacheWarningEmitted = true; process.emitWarning( diff --git a/packages/discord.js/src/managers/GuildChannelManager.js b/packages/discord.js/src/managers/GuildChannelManager.js index 27bd3e65c..dfb97fc25 100644 --- a/packages/discord.js/src/managers/GuildChannelManager.js +++ b/packages/discord.js/src/managers/GuildChannelManager.js @@ -22,8 +22,8 @@ class GuildChannelManager extends CachedManager { super(guild.client, GuildChannel, iterable); const defaultCaching = this._cache.constructor.name === 'Collection' || - ((this._cache.maxSize === undefined || this._cache.maxSize === Infinity) && - (this._cache.sweepFilter === undefined || this._cache.sweepFilter.isDefault)); + this._cache.maxSize === undefined || + this._cache.maxSize === Infinity; if (!cacheWarningEmitted && !defaultCaching) { cacheWarningEmitted = true; process.emitWarning( diff --git a/packages/discord.js/src/util/Constants.js b/packages/discord.js/src/util/Constants.js index 0dcf30180..aebd943e3 100644 --- a/packages/discord.js/src/util/Constants.js +++ b/packages/discord.js/src/util/Constants.js @@ -1166,8 +1166,6 @@ exports.GuildScheduledEventStatuses = createEnum([null, 'SCHEDULED', 'ACTIVE', ' exports.GuildScheduledEventEntityTypes = createEnum([null, 'STAGE_INSTANCE', 'VOICE', 'EXTERNAL']); /* eslint-enable max-len */ -exports._cleanupSymbol = Symbol('djsCleanup'); - function keyMirror(arr) { let tmp = Object.create(null); for (const value of arr) tmp[value] = value; diff --git a/packages/discord.js/src/util/LimitedCollection.js b/packages/discord.js/src/util/LimitedCollection.js index 8ebda0e34..1fa6798af 100644 --- a/packages/discord.js/src/util/LimitedCollection.js +++ b/packages/discord.js/src/util/LimitedCollection.js @@ -1,35 +1,18 @@ 'use strict'; -const { setInterval } = require('node:timers'); const { Collection } = require('@discordjs/collection'); -const { _cleanupSymbol } = require('./Constants.js'); -const Sweepers = require('./Sweepers.js'); const { TypeError } = require('../errors/DJSError.js'); -/** - * @typedef {Function} SweepFilter - * @param {LimitedCollection} collection The collection being swept - * @returns {Function|null} Return `null` to skip sweeping, otherwise a function passed to `sweep()`, - * See {@link [Collection#sweep](https://discord.js.org/#/docs/collection/main/class/Collection?scrollTo=sweep)} - * for the definition of this function. - */ - /** * Options for defining the behavior of a LimitedCollection * @typedef {Object} LimitedCollectionOptions * @property {?number} [maxSize=Infinity] The maximum size of the Collection * @property {?Function} [keepOverLimit=null] A function, which is passed the value and key of an entry, ran to decide * to keep an entry past the maximum size - * @property {?SweepFilter} [sweepFilter=null] DEPRECATED: There is no direct alternative to this, - * however most of its purpose is fulfilled by {@link Client#sweepers} - * A function ran every `sweepInterval` to determine how to sweep - * @property {?number} [sweepInterval=0] DEPRECATED: There is no direct alternative to this, - * however most of its purpose is fulfilled by {@link Client#sweepers} - * How frequently, in seconds, to sweep the collection. */ /** - * A Collection which holds a max amount of entries and sweeps periodically. + * A Collection which holds a max amount of entries. * @extends {Collection} * @param {LimitedCollectionOptions} [options={}] Options for constructing the Collection. * @param {Iterable} [iterable=null] Optional entries passed to the Map constructor. @@ -39,7 +22,7 @@ class LimitedCollection extends Collection { if (typeof options !== 'object' || options === null) { throw new TypeError('INVALID_TYPE', 'options', 'object', true); } - const { maxSize = Infinity, keepOverLimit = null, sweepInterval = 0, sweepFilter = null } = options; + const { maxSize = Infinity, keepOverLimit = null } = options; if (typeof maxSize !== 'number') { throw new TypeError('INVALID_TYPE', 'maxSize', 'number'); @@ -47,12 +30,6 @@ class LimitedCollection extends Collection { if (keepOverLimit !== null && typeof keepOverLimit !== 'function') { throw new TypeError('INVALID_TYPE', 'keepOverLimit', 'function'); } - if (typeof sweepInterval !== 'number') { - throw new TypeError('INVALID_TYPE', 'sweepInterval', 'number'); - } - if (sweepFilter !== null && typeof sweepFilter !== 'function') { - throw new TypeError('INVALID_TYPE', 'sweepFilter', 'function'); - } super(iterable); @@ -67,28 +44,6 @@ class LimitedCollection extends Collection { * @type {?Function} */ this.keepOverLimit = keepOverLimit; - - /** - * A function called every sweep interval that returns a function passed to `sweep`. - * @deprecated in favor of {@link Client#sweepers} - * @type {?SweepFilter} - */ - this.sweepFilter = sweepFilter; - - /** - * The id of the interval being used to sweep. - * @deprecated in favor of {@link Client#sweepers} - * @type {?Timeout} - */ - this.interval = - sweepInterval > 0 && sweepInterval !== Infinity && sweepFilter - ? setInterval(() => { - const sweepFn = this.sweepFilter(this); - if (sweepFn === null) return; - if (typeof sweepFn !== 'function') throw new TypeError('SWEEP_FILTER_RETURN'); - this.sweep(sweepFn); - }, sweepInterval * 1_000).unref() - : null; } set(key, value) { @@ -105,24 +60,6 @@ class LimitedCollection extends Collection { return super.set(key, value); } - /** - * Create a sweepFilter function that uses a lifetime to determine sweepability. - * @param {LifetimeFilterOptions} [options={}] The options used to generate the filter function - * @deprecated Use {@link Sweepers.filterByLifetime} instead - * @returns {SweepFilter} - */ - static filterByLifetime({ - lifetime = 14400, - getComparisonTimestamp = e => e?.createdTimestamp, - excludeFromSweep = () => false, - } = {}) { - return Sweepers.filterByLifetime({ lifetime, getComparisonTimestamp, excludeFromSweep }); - } - - [_cleanupSymbol]() { - return this.interval ? () => clearInterval(this.interval) : null; - } - static get [Symbol.species]() { return Collection; } diff --git a/packages/discord.js/src/util/Options.js b/packages/discord.js/src/util/Options.js index 685e45970..abfb3f617 100644 --- a/packages/discord.js/src/util/Options.js +++ b/packages/discord.js/src/util/Options.js @@ -39,11 +39,6 @@ const process = require('node:process'); * You can use your own function, or the {@link Options} class to customize the Collection used for the cache. * Overriding the cache used in `GuildManager`, `ChannelManager`, `GuildChannelManager`, `RoleManager`, * and `PermissionOverwriteManager` is unsupported and **will** break functionality - * @property {number} [messageCacheLifetime=0] DEPRECATED: Pass `lifetime` to `sweepers.messages` instead. - * How long a message should stay in the cache until it is considered sweepable (in seconds, 0 for forever) - * @property {number} [messageSweepInterval=0] DEPRECATED: Pass `interval` to `sweepers.messages` instead. - * How frequently to remove messages from the cache that are older than the message cache lifetime - * (in seconds, 0 for never) * @property {MessageMentionOptions} [allowedMentions] Default value for {@link MessageOptions#allowedMentions} * @property {number} [invalidRequestWarningInterval=0] The number of invalid REST requests (those that return * 401, 403, or 429) in a 10 minute window between emitted warnings (0 for no warnings). That is, if set to 500, @@ -133,8 +128,6 @@ class Options extends null { waitGuildTimeout: 15_000, shardCount: 1, makeCache: this.cacheWithLimits(this.defaultMakeCacheSettings), - messageCacheLifetime: 0, - messageSweepInterval: 0, invalidRequestWarningInterval: 0, partials: [], restRequestTimeout: 15_000, @@ -145,7 +138,7 @@ class Options extends null { failIfNotExists: true, userAgentSuffix: [], presence: {}, - sweepers: {}, + sweepers: this.defaultSweeperSettings, ws: { large_threshold: 50, compress: false, @@ -176,32 +169,14 @@ class Options extends null { * If LimitedCollectionOptions are provided for a manager, it uses those settings to form a LimitedCollection. * @returns {CacheFactory} * @example - * // Store up to 200 messages per channel and discard archived threads if they were archived more than 4 hours ago. - * // Note archived threads will remain in the guild and client caches with these settings + * // Store up to 200 messages per channel and 200 members per guild, always keeping the client member. * Options.cacheWithLimits({ * MessageManager: 200, - * ThreadManager: { - * sweepInterval: 3600, - * sweepFilter: LimitedCollection.filterByLifetime({ - * getComparisonTimestamp: e => e.archiveTimestamp, - * excludeFromSweep: e => !e.archived, - * }), + * GuildMemberManager: { + * maxSize: 200, + * keepOverLimit: (member) => member.id === client.user.id, * }, * }); - * @example - * // Sweep messages every 5 minutes, removing messages that have not been edited or created in the last 30 minutes - * Options.cacheWithLimits({ - * // Keep default thread sweeping behavior - * ...Options.defaultMakeCacheSettings, - * // Override MessageManager - * MessageManager: { - * sweepInterval: 300, - * sweepFilter: LimitedCollection.filterByLifetime({ - * lifetime: 1800, - * getComparisonTimestamp: e => e.editedTimestamp ?? e.createdTimestamp, - * }) - * } - * }); */ static cacheWithLimits(settings = {}) { const { Collection } = require('@discordjs/collection'); @@ -219,15 +194,9 @@ class Options extends null { } return new LimitedCollection({ maxSize: setting }); } - /* eslint-disable eqeqeq */ - const noSweeping = - setting.sweepFilter == null || - setting.sweepInterval == null || - setting.sweepInterval <= 0 || - setting.sweepInterval === Infinity; + /* eslint-disable-next-line eqeqeq */ const noLimit = setting.maxSize == null || setting.maxSize === Infinity; - /* eslint-enable eqeqeq */ - if (noSweeping && noLimit) { + if (noLimit) { return new Collection(); } return new LimitedCollection(setting); @@ -257,18 +226,6 @@ class Options extends null { static get defaultMakeCacheSettings() { return { MessageManager: 200, - ChannelManager: { - sweepInterval: 3600, - sweepFilter: require('./Util').archivedThreadSweepFilter(), - }, - GuildChannelManager: { - sweepInterval: 3600, - sweepFilter: require('./Util').archivedThreadSweepFilter(), - }, - ThreadManager: { - sweepInterval: 3600, - sweepFilter: require('./Util').archivedThreadSweepFilter(), - }, }; } } diff --git a/packages/discord.js/src/util/Util.js b/packages/discord.js/src/util/Util.js index 868391fad..b8f2e576b 100644 --- a/packages/discord.js/src/util/Util.js +++ b/packages/discord.js/src/util/Util.js @@ -561,18 +561,6 @@ class Util extends null { static cleanCodeBlockContent(text) { return text.replaceAll('```', '`\u200b``'); } - - /** - * Creates a sweep filter that sweeps archived threads - * @param {number} [lifetime=14400] How long a thread has to be archived to be valid for sweeping - * @deprecated When not using with `makeCache` use `Sweepers.archivedThreadSweepFilter` instead - * @returns {SweepFilter} - */ - static archivedThreadSweepFilter(lifetime = 14400) { - const filter = require('./Sweepers').archivedThreadSweepFilter(lifetime); - filter.isDefault = true; - return filter; - } } module.exports = Util; diff --git a/packages/discord.js/typings/index.d.ts b/packages/discord.js/typings/index.d.ts index f17ca96a3..c4f16ff5e 100644 --- a/packages/discord.js/typings/index.d.ts +++ b/packages/discord.js/typings/index.d.ts @@ -567,8 +567,6 @@ export class Client extends BaseClient { public generateInvite(options?: InviteGenerationOptions): string; public login(token?: string): Promise; public isReady(): this is Client; - /** @deprecated Use {@link Sweepers#sweepMessages} instead */ - public sweepMessages(lifetime?: number): number; public toJSON(): unknown; public on(event: K, listener: (...args: ClientEvents[K]) => Awaitable): this; @@ -1428,13 +1426,6 @@ export class LimitedCollection extends Collection { public constructor(options?: LimitedCollectionOptions, iterable?: Iterable); public maxSize: number; public keepOverLimit: ((value: V, key: K, collection: this) => boolean) | null; - /** @deprecated Use Global Sweepers instead */ - public interval: NodeJS.Timeout | null; - /** @deprecated Use Global Sweepers instead */ - public sweepFilter: SweepFilter | null; - - /** @deprecated Use `Sweepers.filterByLifetime` instead */ - public static filterByLifetime(options?: LifetimeFilterOptions): SweepFilter; } export type MessageCollectorOptionsParams = @@ -2417,8 +2408,6 @@ export class UserFlags extends BitField { export class Util extends null { private constructor(); - /** @deprecated When not using with `makeCache` use `Sweepers.archivedThreadSweepFilter` instead */ - public static archivedThreadSweepFilter(lifetime?: number): SweepFilter; public static basename(path: string, ext?: string): string; public static cleanContent(str: string, channel: TextBasedChannel): string; public static cloneObject(obj: unknown): unknown; @@ -3986,10 +3975,6 @@ export interface ClientOptions { shards?: number | number[] | 'auto'; shardCount?: number; makeCache?: CacheFactory; - /** @deprecated Pass the value of this property as `lifetime` to `sweepers.messages` instead. */ - messageCacheLifetime?: number; - /** @deprecated Pass the value of this property as `interval` to `sweepers.messages` instead. */ - messageSweepInterval?: number; allowedMentions?: MessageMentionOptions; invalidRequestWarningInterval?: number; partials?: PartialTypes[]; @@ -5502,10 +5487,6 @@ export type SweeperKey = keyof SweeperDefinitions; export type CollectionSweepFilter = (value: V, key: K, collection: Collection) => boolean; -export type SweepFilter = ( - collection: LimitedCollection, -) => ((value: V, key: K, collection: LimitedCollection) => boolean) | null; - export interface SweepOptions { interval: number; filter: GlobalSweepFilter; @@ -5543,10 +5524,6 @@ export type SweeperOptions = { export interface LimitedCollectionOptions { maxSize?: number; keepOverLimit?: (value: V, key: K, collection: LimitedCollection) => boolean; - /** @deprecated Use Global Sweepers instead */ - sweepFilter?: SweepFilter; - /** @deprecated Use Global Sweepers instead */ - sweepInterval?: number; } export type AnyChannel = diff --git a/packages/discord.js/typings/index.test-d.ts b/packages/discord.js/typings/index.test-d.ts index 6ad705476..30716ce4d 100644 --- a/packages/discord.js/typings/index.test-d.ts +++ b/packages/discord.js/typings/index.test-d.ts @@ -45,7 +45,6 @@ import { Intents, Interaction, InteractionCollector, - LimitedCollection, Message, MessageActionRow, MessageAttachment, @@ -107,14 +106,9 @@ const client: Client = new Client({ MessageManager: 200, // @ts-expect-error Message: 100, - ThreadManager: { - maxSize: 1000, - keepOverLimit: (x: ThreadChannel) => x.id === '123', - sweepInterval: 5000, - sweepFilter: LimitedCollection.filterByLifetime({ - getComparisonTimestamp: (x: ThreadChannel) => x.archiveTimestamp ?? 0, - excludeFromSweep: (x: ThreadChannel) => !x.archived, - }), + GuildMemberManager: { + maxSize: 200, + keepOverLimit: member => member.id === client.user?.id, }, }), });