diff --git a/src/client/BaseClient.js b/src/client/BaseClient.js index bf3f3b9c81f6..f501b3a9db7a 100644 --- a/src/client/BaseClient.js +++ b/src/client/BaseClient.js @@ -2,7 +2,7 @@ const EventEmitter = require('events'); const RESTManager = require('../rest/RESTManager'); -const { DefaultOptions } = require('../util/Constants'); +const Options = require('../util/Options'); const Util = require('../util/Util'); /** @@ -38,7 +38,7 @@ class BaseClient extends EventEmitter { * The options the client was instantiated with * @type {ClientOptions} */ - this.options = Util.mergeDefault(DefaultOptions, options); + this.options = Util.mergeDefault(Options.createDefault(), options); /** * The REST manager of the client diff --git a/src/client/Client.js b/src/client/Client.js index 3da2f439fc75..763379d85b63 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -17,9 +17,10 @@ const VoiceRegion = require('../structures/VoiceRegion'); const Webhook = require('../structures/Webhook'); const Widget = require('../structures/Widget'); const Collection = require('../util/Collection'); -const { Events, DefaultOptions, InviteScopes } = require('../util/Constants'); +const { Events, InviteScopes } = require('../util/Constants'); const DataResolver = require('../util/DataResolver'); const Intents = require('../util/Intents'); +const Options = require('../util/Options'); const Permissions = require('../util/Permissions'); const Structures = require('../util/Structures'); @@ -35,14 +36,15 @@ class Client extends BaseClient { super(Object.assign({ _tokenType: 'Bot' }, options)); const data = require('worker_threads').workerData ?? process.env; + const defaults = Options.createDefault(); - if (this.options.shards === DefaultOptions.shards) { + if (this.options.shards === defaults.shards) { if ('SHARDS' in data) { this.options.shards = JSON.parse(data.SHARDS); } } - if (this.options.shardCount === DefaultOptions.shardCount) { + if (this.options.shardCount === defaults.shardCount) { if ('SHARD_COUNT' in data) { this.options.shardCount = Number(data.SHARD_COUNT); } else if (Array.isArray(this.options.shards)) { @@ -468,8 +470,8 @@ class Client extends BaseClient { throw new TypeError('CLIENT_INVALID_OPTION', 'shards', "'auto', a number or array of numbers"); } if (options.shards && !options.shards.length) throw new RangeError('CLIENT_INVALID_PROVIDED_SHARDS'); - if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) { - throw new TypeError('CLIENT_INVALID_OPTION', 'messageCacheMaxSize', 'a number'); + 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'); diff --git a/src/errors/Messages.js b/src/errors/Messages.js index 00290a1b632c..30a5e9b9ae16 100644 --- a/src/errors/Messages.js +++ b/src/errors/Messages.js @@ -127,6 +127,8 @@ const Messages = { INTERACTION_NOT_REPLIED: 'This interaction has not been deferred or replied to.', INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be fetched or deleted.', INTERACTION_FETCH_EPHEMERAL: 'Ephemeral responses cannot be fetched.', + + NOT_IMPLEMENTED: (what, name) => `Method ${what} not implemented on ${name}.`, }; for (const [name, message] of Object.entries(Messages)) register(name, message); diff --git a/src/index.js b/src/index.js index 02aa7da1f0e7..2ccdff2749a5 100644 --- a/src/index.js +++ b/src/index.js @@ -23,6 +23,7 @@ module.exports = { RateLimitError: require('./rest/RateLimitError'), MessageFlags: require('./util/MessageFlags'), Intents: require('./util/Intents'), + Options: require('./util/Options'), Permissions: require('./util/Permissions'), SnowflakeUtil: require('./util/SnowflakeUtil'), Structures: require('./util/Structures'), diff --git a/src/managers/ApplicationCommandManager.js b/src/managers/ApplicationCommandManager.js index 864db7f090fd..6b47a29729f7 100644 --- a/src/managers/ApplicationCommandManager.js +++ b/src/managers/ApplicationCommandManager.js @@ -1,18 +1,18 @@ 'use strict'; const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager'); -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const ApplicationCommand = require('../structures/ApplicationCommand'); const Collection = require('../util/Collection'); /** * Manages API methods for application commands and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class ApplicationCommandManager extends BaseManager { +class ApplicationCommandManager extends CachedManager { constructor(client, iterable) { - super(client, iterable, ApplicationCommand); + super(client, ApplicationCommand, iterable); /** * The manager for permissions of arbitrary commands on arbitrary guilds diff --git a/src/managers/ApplicationCommandPermissionsManager.js b/src/managers/ApplicationCommandPermissionsManager.js index bf10454efb31..79643c11539f 100644 --- a/src/managers/ApplicationCommandPermissionsManager.js +++ b/src/managers/ApplicationCommandPermissionsManager.js @@ -1,14 +1,18 @@ 'use strict'; +const BaseManager = require('./BaseManager'); const { Error, TypeError } = require('../errors'); const Collection = require('../util/Collection'); const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Constants'); /** * Manages API methods for permissions of Application Commands. + * @extends {BaseManager} */ -class ApplicationCommandPermissionsManager { +class ApplicationCommandPermissionsManager extends BaseManager { constructor(manager) { + super(manager.client); + /** * The manager or command that this manager belongs to * @type {ApplicationCommandManager|ApplicationCommand} @@ -32,14 +36,6 @@ class ApplicationCommandPermissionsManager { * @type {?Snowflake} */ this.commandID = manager.id ?? null; - - /** - * The client that instantiated this Manager - * @name ApplicationCommandPermissionsManager#client - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: manager.client }); } /** diff --git a/src/managers/BaseGuildEmojiManager.js b/src/managers/BaseGuildEmojiManager.js index dad3e7368b02..5a39a2bfea1d 100644 --- a/src/managers/BaseGuildEmojiManager.js +++ b/src/managers/BaseGuildEmojiManager.js @@ -1,17 +1,17 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const GuildEmoji = require('../structures/GuildEmoji'); const ReactionEmoji = require('../structures/ReactionEmoji'); const { parseEmoji } = require('../util/Util'); /** * Holds methods to resolve GuildEmojis and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class BaseGuildEmojiManager extends BaseManager { +class BaseGuildEmojiManager extends CachedManager { constructor(client, iterable) { - super(client, iterable, GuildEmoji); + super(client, GuildEmoji, iterable); } /** diff --git a/src/managers/BaseManager.js b/src/managers/BaseManager.js index ee326386910d..06514011a1a1 100644 --- a/src/managers/BaseManager.js +++ b/src/managers/BaseManager.js @@ -1,24 +1,11 @@ 'use strict'; -const Collection = require('../util/Collection'); -let Structures; - /** - * Manages the API methods of a data model and holds its cache. + * Manages the API methods of a data model. * @abstract */ class BaseManager { - constructor(client, iterable, holds, cacheType = Collection, ...cacheOptions) { - if (!Structures) Structures = require('../util/Structures'); - /** - * The data structure belonging to this manager - * @name BaseManager#holds - * @type {Function} - * @private - * @readonly - */ - Object.defineProperty(this, 'holds', { value: Structures.get(holds.name) ?? holds }); - + constructor(client) { /** * The client that instantiated this Manager * @name BaseManager#client @@ -26,55 +13,6 @@ class BaseManager { * @readonly */ Object.defineProperty(this, 'client', { value: client }); - - /** - * The type of Collection of the Manager - * @type {Collection} - */ - this.cacheType = cacheType; - - /** - * Holds the cache for the data model - * @type {Collection} - */ - this.cache = new cacheType(...cacheOptions); - if (iterable) for (const i of iterable) this.add(i); - } - - add(data, cache = true, { id, extras = [] } = {}) { - const existing = this.cache.get(id ?? data.id); - if (cache) existing?._patch(data); - if (existing) return existing; - - const entry = this.holds ? new this.holds(this.client, data, ...extras) : data; - if (cache) this.cache.set(id ?? entry.id, entry); - return entry; - } - - /** - * Resolves a data entry to a data Object. - * @param {string|Object} idOrInstance The id or instance of something in this Manager - * @returns {?Object} An instance from this Manager - */ - resolve(idOrInstance) { - if (idOrInstance instanceof this.holds) return idOrInstance; - if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) ?? null; - return null; - } - - /** - * Resolves a data entry to an instance ID. - * @param {string|Object} idOrInstance The id or instance of something in this Manager - * @returns {?Snowflake} - */ - resolveID(idOrInstance) { - if (idOrInstance instanceof this.holds) return idOrInstance.id; - if (typeof idOrInstance === 'string') return idOrInstance; - return null; - } - - valueOf() { - return this.cache; } } diff --git a/src/managers/CachedManager.js b/src/managers/CachedManager.js new file mode 100644 index 000000000000..78ecfc6e120c --- /dev/null +++ b/src/managers/CachedManager.js @@ -0,0 +1,43 @@ +'use strict'; + +const DataManager = require('./DataManager'); + +/** + * Manages the API methods of a data model with a mutable cache of instances. + * @extends {DataManager} + * @abstract + */ +class CachedManager extends DataManager { + constructor(client, holds, iterable) { + super(client, holds); + + Object.defineProperty(this, '_cache', { value: this.client.options.makeCache(this.constructor, this.holds) }); + + if (iterable) { + for (const item of iterable) { + this.add(item); + } + } + } + + /** + * The cache of items for this manager. + * @type {Collection} + * @abstract + */ + get cache() { + return this._cache; + } + + add(data, cache = true, { id, extras = [] } = {}) { + const existing = this.cache.get(id ?? data.id); + if (cache) existing?._patch(data); + if (existing) return existing; + + const entry = this.holds ? new this.holds(this.client, data, ...extras) : data; + if (cache) this.cache.set(id ?? entry.id, entry); + return entry; + } +} + +module.exports = CachedManager; diff --git a/src/managers/ChannelManager.js b/src/managers/ChannelManager.js index 953cea454b49..e4e94fddc167 100644 --- a/src/managers/ChannelManager.js +++ b/src/managers/ChannelManager.js @@ -1,16 +1,16 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const Channel = require('../structures/Channel'); const { Events, ThreadChannelTypes } = require('../util/Constants'); /** * A manager of channels belonging to a client - * @extends {BaseManager} + * @extends {CachedManager} */ -class ChannelManager extends BaseManager { +class ChannelManager extends CachedManager { constructor(client, iterable) { - super(client, iterable, Channel); + super(client, Channel, iterable); } /** diff --git a/src/managers/DataManager.js b/src/managers/DataManager.js new file mode 100644 index 000000000000..cd304d956347 --- /dev/null +++ b/src/managers/DataManager.js @@ -0,0 +1,65 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const { Error } = require('../errors'); + +let Structures; + +/** + * Manages the API methods of a data model along with a collection of instances. + * @extends {BaseManager} + * @abstract + */ +class DataManager extends BaseManager { + constructor(client, holds) { + super(client); + + if (!Structures) Structures = require('../util/Structures'); + + /** + * The data structure belonging to this manager. + * @name DataManager#holds + * @type {Function} + * @private + * @readonly + */ + Object.defineProperty(this, 'holds', { value: Structures.get(holds.name) ?? holds }); + } + + /** + * The cache of items for this manager. + * @type {Collection} + * @abstract + */ + get cache() { + throw new Error('NOT_IMPLEMENTED', 'get cache', this.constructor.name); + } + + /** + * Resolves a data entry to a data Object. + * @param {string|Object} idOrInstance The id or instance of something in this Manager + * @returns {?Object} An instance from this Manager + */ + resolve(idOrInstance) { + if (idOrInstance instanceof this.holds) return idOrInstance; + if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) ?? null; + return null; + } + + /** + * Resolves a data entry to an instance ID. + * @param {string|Object} idOrInstance The id or instance of something in this Manager + * @returns {?Snowflake} + */ + resolveID(idOrInstance) { + if (idOrInstance instanceof this.holds) return idOrInstance.id; + if (typeof idOrInstance === 'string') return idOrInstance; + return null; + } + + valueOf() { + return this.cache; + } +} + +module.exports = DataManager; diff --git a/src/managers/GuildBanManager.js b/src/managers/GuildBanManager.js index 735aeeb5b1a3..b0e106045fd0 100644 --- a/src/managers/GuildBanManager.js +++ b/src/managers/GuildBanManager.js @@ -1,6 +1,6 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { TypeError, Error } = require('../errors'); const GuildBan = require('../structures/GuildBan'); const GuildMember = require('../structures/GuildMember'); @@ -8,11 +8,11 @@ const Collection = require('../util/Collection'); /** * Manages API methods for GuildBans and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class GuildBanManager extends BaseManager { +class GuildBanManager extends CachedManager { constructor(guild, iterable) { - super(guild.client, iterable, GuildBan); + super(guild.client, GuildBan, iterable); /** * The guild this Manager belongs to diff --git a/src/managers/GuildChannelManager.js b/src/managers/GuildChannelManager.js index d7c0601230bc..5f94495a5471 100644 --- a/src/managers/GuildChannelManager.js +++ b/src/managers/GuildChannelManager.js @@ -1,6 +1,6 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const GuildChannel = require('../structures/GuildChannel'); const PermissionOverwrites = require('../structures/PermissionOverwrites'); const ThreadChannel = require('../structures/ThreadChannel'); @@ -9,11 +9,11 @@ const { ChannelTypes, ThreadChannelTypes } = require('../util/Constants'); /** * Manages API methods for GuildChannels and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class GuildChannelManager extends BaseManager { +class GuildChannelManager extends CachedManager { constructor(guild, iterable) { - super(guild.client, iterable, GuildChannel); + super(guild.client, GuildChannel, iterable); /** * The guild this Manager belongs to diff --git a/src/managers/GuildEmojiRoleManager.js b/src/managers/GuildEmojiRoleManager.js index 55cddec85998..3b4b8710471b 100644 --- a/src/managers/GuildEmojiRoleManager.js +++ b/src/managers/GuildEmojiRoleManager.js @@ -1,13 +1,18 @@ 'use strict'; +const DataManager = require('./DataManager'); const { TypeError } = require('../errors'); +const Role = require('../structures/Role'); const Collection = require('../util/Collection'); /** * Manages API methods for roles belonging to emojis and stores their cache. + * @extends {DataManager} */ -class GuildEmojiRoleManager { +class GuildEmojiRoleManager extends DataManager { constructor(emoji) { + super(emoji.client, Role); + /** * The emoji belonging to this manager * @type {GuildEmoji} @@ -18,12 +23,6 @@ class GuildEmojiRoleManager { * @type {Guild} */ this.guild = emoji.guild; - /** - * The client belonging to this manager - * @type {Client} - * @readonly - */ - Object.defineProperty(this, 'client', { value: emoji.client }); } /** diff --git a/src/managers/GuildManager.js b/src/managers/GuildManager.js index 86df25bd8429..0dbc75ba1c2f 100644 --- a/src/managers/GuildManager.js +++ b/src/managers/GuildManager.js @@ -1,6 +1,6 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const Guild = require('../structures/Guild'); const GuildChannel = require('../structures/GuildChannel'); const GuildEmoji = require('../structures/GuildEmoji'); @@ -23,11 +23,11 @@ const { resolveColor } = require('../util/Util'); /** * Manages API methods for Guilds and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class GuildManager extends BaseManager { +class GuildManager extends CachedManager { constructor(client, iterable) { - super(client, iterable, Guild); + super(client, Guild, iterable); } /** diff --git a/src/managers/GuildMemberManager.js b/src/managers/GuildMemberManager.js index 629891a653cb..11bbb40e6152 100644 --- a/src/managers/GuildMemberManager.js +++ b/src/managers/GuildMemberManager.js @@ -1,6 +1,6 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { Error, TypeError, RangeError } = require('../errors'); const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel'); const GuildMember = require('../structures/GuildMember'); @@ -11,11 +11,12 @@ const SnowflakeUtil = require('../util/SnowflakeUtil'); /** * Manages API methods for GuildMembers and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class GuildMemberManager extends BaseManager { +class GuildMemberManager extends CachedManager { constructor(guild, iterable) { - super(guild.client, iterable, GuildMember); + super(guild.client, GuildMember, iterable); + /** * The guild this manager belongs to * @type {Guild} diff --git a/src/managers/GuildMemberRoleManager.js b/src/managers/GuildMemberRoleManager.js index 1faa59e00ba1..90c13adf77f4 100644 --- a/src/managers/GuildMemberRoleManager.js +++ b/src/managers/GuildMemberRoleManager.js @@ -1,24 +1,29 @@ 'use strict'; +const DataManager = require('./DataManager'); const { TypeError } = require('../errors'); +const Role = require('../structures/Role'); const Collection = require('../util/Collection'); /** * Manages API methods for roles of a GuildMember and stores their cache. + * @extends {DataManager} */ -class GuildMemberRoleManager { +class GuildMemberRoleManager extends DataManager { constructor(member) { + super(member.client, Role); + /** * The GuildMember this manager belongs to * @type {GuildMember} */ this.member = member; + /** * The Guild this manager belongs to * @type {Guild} */ this.guild = member.guild; - Object.defineProperty(this, 'client', { value: member.client }); } /** @@ -170,10 +175,6 @@ class GuildMemberRoleManager { clone.member._roles = [...this.cache.keyArray()]; return clone; } - - valueOf() { - return this.cache; - } } module.exports = GuildMemberRoleManager; diff --git a/src/managers/MessageManager.js b/src/managers/MessageManager.js index 78395bbf4234..dbee780fea6b 100644 --- a/src/managers/MessageManager.js +++ b/src/managers/MessageManager.js @@ -1,19 +1,19 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const Message = require('../structures/Message'); const MessagePayload = require('../structures/MessagePayload'); const Collection = require('../util/Collection'); -const LimitedCollection = require('../util/LimitedCollection'); /** * Manages API methods for Messages and holds their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class MessageManager extends BaseManager { +class MessageManager extends CachedManager { constructor(channel, iterable) { - super(channel.client, iterable, Message, LimitedCollection, channel.client.options.messageCacheMaxSize); + super(channel.client, Message, iterable); + /** * The channel that the messages belong to * @type {TextBasedChannel} diff --git a/src/managers/PermissionOverwriteManager.js b/src/managers/PermissionOverwriteManager.js index bd619828124d..9d43102fd3eb 100644 --- a/src/managers/PermissionOverwriteManager.js +++ b/src/managers/PermissionOverwriteManager.js @@ -1,6 +1,6 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const PermissionOverwrites = require('../structures/PermissionOverwrites'); const Role = require('../structures/Role'); @@ -9,11 +9,11 @@ const { OverwriteTypes } = require('../util/Constants'); /** * Manages API methods for guild channel permission overwrites and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class PermissionOverwriteManager extends BaseManager { +class PermissionOverwriteManager extends CachedManager { constructor(channel, iterable) { - super(channel.client, iterable, PermissionOverwrites); + super(channel.client, PermissionOverwrites, iterable); /** * The channel of the permission overwrite this manager belongs to diff --git a/src/managers/PresenceManager.js b/src/managers/PresenceManager.js index 1835e84b2700..f1f366e7b405 100644 --- a/src/managers/PresenceManager.js +++ b/src/managers/PresenceManager.js @@ -1,15 +1,15 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { Presence } = require('../structures/Presence'); /** * Manages API methods for Presences and holds their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class PresenceManager extends BaseManager { +class PresenceManager extends CachedManager { constructor(client, iterable) { - super(client, iterable, Presence); + super(client, Presence, iterable); } /** diff --git a/src/managers/ReactionManager.js b/src/managers/ReactionManager.js index afafc6e84da6..ad58eb77d1ba 100644 --- a/src/managers/ReactionManager.js +++ b/src/managers/ReactionManager.js @@ -1,15 +1,15 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const MessageReaction = require('../structures/MessageReaction'); /** * Manages API methods for reactions and holds their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class ReactionManager extends BaseManager { +class ReactionManager extends CachedManager { constructor(message, iterable) { - super(message.client, iterable, MessageReaction); + super(message.client, MessageReaction, iterable); /** * The message that this manager belongs to diff --git a/src/managers/ReactionUserManager.js b/src/managers/ReactionUserManager.js index 4e4e294be2d0..68dd07eda312 100644 --- a/src/managers/ReactionUserManager.js +++ b/src/managers/ReactionUserManager.js @@ -1,16 +1,18 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { Error } = require('../errors'); +const User = require('../structures/User'); const Collection = require('../util/Collection'); /** * Manages API methods for users who reacted to a reaction and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class ReactionUserManager extends BaseManager { - constructor(client, iterable, reaction) { - super(client, iterable, { name: 'User' }); +class ReactionUserManager extends CachedManager { + constructor(reaction, iterable) { + super(reaction.client, User, iterable); + /** * The reaction that this manager belongs to * @type {MessageReaction} diff --git a/src/managers/RoleManager.js b/src/managers/RoleManager.js index 42074aa32807..8f2e4cd64dfc 100644 --- a/src/managers/RoleManager.js +++ b/src/managers/RoleManager.js @@ -1,6 +1,6 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const Role = require('../structures/Role'); const Collection = require('../util/Collection'); @@ -9,11 +9,12 @@ const { resolveColor, setPosition } = require('../util/Util'); /** * Manages API methods for roles and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class RoleManager extends BaseManager { +class RoleManager extends CachedManager { constructor(guild, iterable) { - super(guild.client, iterable, Role); + super(guild.client, Role, iterable); + /** * The guild belonging to this manager * @type {Guild} diff --git a/src/managers/StageInstanceManager.js b/src/managers/StageInstanceManager.js index 3ae0d8f17f0b..ec11eac12ec0 100644 --- a/src/managers/StageInstanceManager.js +++ b/src/managers/StageInstanceManager.js @@ -1,17 +1,17 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { TypeError, Error } = require('../errors'); const StageInstance = require('../structures/StageInstance'); const { PrivacyLevels } = require('../util/Constants'); /** * Manages API methods for {@link StageInstance} objects and holds their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class StageInstanceManager extends BaseManager { +class StageInstanceManager extends CachedManager { constructor(guild, iterable) { - super(guild.client, iterable, StageInstance); + super(guild.client, StageInstance, iterable); /** * The guild this manager belongs to diff --git a/src/managers/ThreadManager.js b/src/managers/ThreadManager.js index 7587c9ba31cb..41f68349a3ef 100644 --- a/src/managers/ThreadManager.js +++ b/src/managers/ThreadManager.js @@ -1,6 +1,6 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const ThreadChannel = require('../structures/ThreadChannel'); const Collection = require('../util/Collection'); @@ -8,11 +8,11 @@ const { ChannelTypes } = require('../util/Constants'); /** * Manages API methods for {@link ThreadChannel} objects and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class ThreadManager extends BaseManager { +class ThreadManager extends CachedManager { constructor(channel, iterable) { - super(channel.client, iterable, ThreadChannel); + super(channel.client, ThreadChannel, iterable); /** * The channel this Manager belongs to diff --git a/src/managers/ThreadMemberManager.js b/src/managers/ThreadMemberManager.js index d66e88f66c62..2352dad67ead 100644 --- a/src/managers/ThreadMemberManager.js +++ b/src/managers/ThreadMemberManager.js @@ -1,17 +1,18 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const { TypeError } = require('../errors'); const ThreadMember = require('../structures/ThreadMember'); const Collection = require('../util/Collection'); /** - * Manages API methods for ThreadMembers and stores their cache. - * @extends {BaseManager} + * Manages API methods for GuildMembers and stores their cache. + * @extends {CachedManager} */ -class ThreadMemberManager extends BaseManager { +class ThreadMemberManager extends CachedManager { constructor(thread, iterable) { - super(thread.client, iterable, ThreadMember); + super(thread.client, ThreadMember, iterable); + /** * The thread this manager belongs to * @type {ThreadChannel} diff --git a/src/managers/UserManager.js b/src/managers/UserManager.js index e39105dd83d0..bcf481744383 100644 --- a/src/managers/UserManager.js +++ b/src/managers/UserManager.js @@ -1,6 +1,6 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); const GuildMember = require('../structures/GuildMember'); const Message = require('../structures/Message'); const ThreadMember = require('../structures/ThreadMember'); @@ -8,11 +8,11 @@ const User = require('../structures/User'); /** * Manages API methods for users and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class UserManager extends BaseManager { +class UserManager extends CachedManager { constructor(client, iterable) { - super(client, iterable, User); + super(client, User, iterable); } /** diff --git a/src/managers/VoiceStateManager.js b/src/managers/VoiceStateManager.js index 35045f7b435d..d33c5ba72d1f 100644 --- a/src/managers/VoiceStateManager.js +++ b/src/managers/VoiceStateManager.js @@ -1,14 +1,16 @@ 'use strict'; -const BaseManager = require('./BaseManager'); +const CachedManager = require('./CachedManager'); +const VoiceState = require('../structures/VoiceState'); /** * Manages API methods for VoiceStates and stores their cache. - * @extends {BaseManager} + * @extends {CachedManager} */ -class VoiceStateManager extends BaseManager { +class VoiceStateManager extends CachedManager { constructor(guild, iterable) { - super(guild.client, iterable, { name: 'VoiceState' }); + super(guild.client, VoiceState, iterable); + /** * The guild this manager belongs to * @type {Guild} diff --git a/src/structures/MessageReaction.js b/src/structures/MessageReaction.js index 4744cbef65a2..1648d6c8b7e1 100644 --- a/src/structures/MessageReaction.js +++ b/src/structures/MessageReaction.js @@ -22,6 +22,7 @@ class MessageReaction { * @readonly */ Object.defineProperty(this, 'client', { value: client }); + /** * The message that this reaction refers to * @type {Message} @@ -38,7 +39,7 @@ class MessageReaction { * A manager of the users that have given this reaction * @type {ReactionUserManager} */ - this.users = new ReactionUserManager(client, undefined, this); + this.users = new ReactionUserManager(this); this._emoji = new ReactionEmoji(this, data.emoji); diff --git a/src/util/Constants.js b/src/util/Constants.js index 3c4325879ad9..bb779f50037d 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -3,117 +3,6 @@ const Package = (exports.Package = require('../../package.json')); const { Error, RangeError } = require('../errors'); -/** - * Rate limit data - * @typedef {Object} RateLimitData - * @property {number} timeout Time until this rate limit ends, in ms - * @property {number} limit The maximum amount of requests of this endpoint - * @property {string} method The http method of this request - * @property {string} path The path of the request relative to the HTTP endpoint - * @property {string} route The route of the request relative to the HTTP endpoint - * @property {boolean} global Whether this is a global rate limit - */ - -/** - * Whether this rate limit should throw an Error - * @typedef {Function} RateLimitQueueFilter - * @param {RateLimitData} rateLimitData The data of this rate limit - * @returns {boolean|Promise} - */ - -/** - * Options for a client. - * @typedef {Object} ClientOptions - * @property {number|number[]|string} [shards] ID of the shard to run, or an array of shard IDs. If not specified, - * the client will spawn {@link ClientOptions#shardCount} shards. If set to `auto`, it will fetch the - * recommended amount of shards from Discord and spawn that amount - * @property {number} [shardCount=1] The total amount of shards used by all processes of this bot - * (e.g. recommended shard count, shard count of the ShardingManager) - * @property {number} [messageCacheMaxSize=200] Maximum number of messages to cache per channel - * (-1 or Infinity for unlimited - don't do this without message sweeping, otherwise memory usage will climb - * indefinitely) - * @property {number} [messageCacheLifetime=0] How long a message should stay in the cache until it is considered - * sweepable (in seconds, 0 for forever) - * @property {number} [messageSweepInterval=0] 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, - * warnings will be emitted at invalid request number 500, 1000, 1500, and so on. - * @property {PartialType[]} [partials] Structures allowed to be partial. This means events can be emitted even when - * they're missing all the data for a particular structure. See the "Partial Structures" topic on the - * [guide](https://discordjs.guide/popular-topics/partials.html) for some - * important usage information, as partials require you to put checks in place when handling data. - * @property {number} [restWsBridgeTimeout=5000] Maximum time permitted between REST responses and their - * corresponding websocket events - * @property {number} [restTimeOffset=500] Extra time in milliseconds to wait before continuing to make REST - * requests (higher values will reduce rate-limiting errors on bad connections) - * @property {number} [restRequestTimeout=15000] Time to wait before cancelling a REST request, in milliseconds - * @property {number} [restSweepInterval=60] How frequently to delete inactive request buckets, in seconds - * (or 0 for never) - * @property {number} [restGlobalRateLimit=0] How many requests to allow sending per second (0 for unlimited, 50 for - * the standard global limit used by Discord) - * @property {string[]|RateLimitQueueFilter} [rejectOnRateLimit] Decides how rate limits and pre-emptive throttles - * should be handled. If this option is an array containing the prefix of the request route (e.g. /channels to match any - * route starting with /channels, such as /channels/222197033908436994/messages) or a function returning true, a - * {@link RateLimitError} will be thrown. Otherwise the request will be queued for later - * @property {number} [retryLimit=1] How many times to retry on 5XX errors (Infinity for indefinite amount of retries) - * @property {PresenceData} [presence={}] Presence data to use upon login - * @property {IntentsResolvable} intents Intents to enable for this connection - * @property {WebsocketOptions} [ws] Options for the WebSocket - * @property {HTTPOptions} [http] HTTP options - */ -exports.DefaultOptions = { - shardCount: 1, - messageCacheMaxSize: 200, - messageCacheLifetime: 0, - messageSweepInterval: 0, - invalidRequestWarningInterval: 0, - partials: [], - restWsBridgeTimeout: 5000, - restRequestTimeout: 15000, - restGlobalRateLimit: 0, - retryLimit: 1, - restTimeOffset: 500, - restSweepInterval: 60, - presence: {}, - - /** - * WebSocket options (these are left as snake_case to match the API) - * @typedef {Object} WebsocketOptions - * @property {number} [large_threshold=50] Number of members in a guild after which offline users will no longer be - * sent in the initial guild member list, must be between 50 and 250 - */ - ws: { - large_threshold: 50, - compress: false, - properties: { - $os: process.platform, - $browser: 'discord.js', - $device: 'discord.js', - }, - version: 9, - }, - - /** - * HTTP options - * @typedef {Object} HTTPOptions - * @property {number} [version=9] API version to use - * @property {string} [api='https://discord.com/api'] Base url of the API - * @property {string} [cdn='https://cdn.discordapp.com'] Base url of the CDN - * @property {string} [invite='https://discord.gg'] Base url of invites - * @property {string} [template='https://discord.new'] Base url of templates - * @property {Object} [headers] Additional headers to send for all API requests - */ - http: { - version: 9, - api: 'https://discord.com/api', - cdn: 'https://cdn.discordapp.com', - invite: 'https://discord.gg', - template: 'https://discord.new', - }, -}; - exports.UserAgent = `DiscordBot (${Package.homepage.split('#')[0]}, ${Package.version}) Node.js/${process.version}`; exports.WSCodes = { diff --git a/src/util/Options.js b/src/util/Options.js new file mode 100644 index 000000000000..dc8e77810d26 --- /dev/null +++ b/src/util/Options.js @@ -0,0 +1,160 @@ +'use strict'; + +/** + * Rate limit data + * @typedef {Object} RateLimitData + * @property {number} timeout Time until this rate limit ends, in ms + * @property {number} limit The maximum amount of requests of this endpoint + * @property {string} method The http method of this request + * @property {string} path The path of the request relative to the HTTP endpoint + * @property {string} route The route of the request relative to the HTTP endpoint + * @property {boolean} global Whether this is a global rate limit + */ + +/** + * Whether this rate limit should throw an Error + * @typedef {Function} RateLimitQueueFilter + * @param {RateLimitData} rateLimitData The data of this rate limit + * @returns {boolean|Promise} + */ + +/** + * @typedef {Function} CacheFactory + * @param {Function} manager The manager class the cache is being requested from. + * @param {Function} holds The class that the cache will hold. + * @returns {Collection} Cache instance that follows collection interface. + */ + +/** + * Options for a client. + * @typedef {Object} ClientOptions + * @property {number|number[]|string} [shards] ID of the shard to run, or an array of shard IDs. If not specified, + * the client will spawn {@link ClientOptions#shardCount} shards. If set to `auto`, it will fetch the + * recommended amount of shards from Discord and spawn that amount + * @property {number} [shardCount=1] The total amount of shards used by all processes of this bot + * (e.g. recommended shard count, shard count of the ShardingManager) + * @property {CacheFactory} [makeCache] Function to create a cache. + * (-1 or Infinity for unlimited - don't do this without message sweeping, otherwise memory usage will climb + * indefinitely) + * @property {number} [messageCacheLifetime=0] How long a message should stay in the cache until it is considered + * sweepable (in seconds, 0 for forever) + * @property {number} [messageSweepInterval=0] 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, + * warnings will be emitted at invalid request number 500, 1000, 1500, and so on. + * @property {PartialType[]} [partials] Structures allowed to be partial. This means events can be emitted even when + * they're missing all the data for a particular structure. See the "Partial Structures" topic on the + * [guide](https://discordjs.guide/popular-topics/partials.html) for some + * important usage information, as partials require you to put checks in place when handling data. + * @property {number} [restWsBridgeTimeout=5000] Maximum time permitted between REST responses and their + * corresponding websocket events + * @property {number} [restTimeOffset=500] Extra time in milliseconds to wait before continuing to make REST + * requests (higher values will reduce rate-limiting errors on bad connections) + * @property {number} [restRequestTimeout=15000] Time to wait before cancelling a REST request, in milliseconds + * @property {number} [restSweepInterval=60] How frequently to delete inactive request buckets, in seconds + * (or 0 for never) + * @property {number} [restGlobalRateLimit=0] How many requests to allow sending per second (0 for unlimited, 50 for + * the standard global limit used by Discord) + * @property {string[]|RateLimitQueueFilter} [rejectOnRateLimit] Decides how rate limits and pre-emptive throttles + * should be handled. If this option is an array containing the prefix of the request route (e.g. /channels to match any + * route starting with /channels, such as /channels/222197033908436994/messages) or a function returning true, a + * {@link RateLimitError} will be thrown. Otherwise the request will be queued for later + * @property {number} [retryLimit=1] How many times to retry on 5XX errors (Infinity for indefinite amount of retries) + * @property {PresenceData} [presence={}] Presence data to use upon login + * @property {IntentsResolvable} intents Intents to enable for this connection + * @property {WebsocketOptions} [ws] Options for the WebSocket + * @property {HTTPOptions} [http] HTTP options + */ + +/** + * WebSocket options (these are left as snake_case to match the API) + * @typedef {Object} WebsocketOptions + * @property {number} [large_threshold=50] Number of members in a guild after which offline users will no longer be + * sent in the initial guild member list, must be between 50 and 250 + */ + +/** + * HTTP options + * @typedef {Object} HTTPOptions + * @property {number} [version=9] API version to use + * @property {string} [api='https://discord.com/api'] Base url of the API + * @property {string} [cdn='https://cdn.discordapp.com'] Base url of the CDN + * @property {string} [invite='https://discord.gg'] Base url of invites + * @property {string} [template='https://discord.new'] Base url of templates + * @property {Object} [headers] Additional headers to send for all API requests + */ + +/** + * Contains various utilities for client options. + */ +class Options extends null { + /** + * The default client options. + * @returns {ClientOptions} + */ + static createDefault() { + return { + shardCount: 1, + makeCache: this.cacheWithLimits({ MessageManager: 200 }), + messageCacheLifetime: 0, + messageSweepInterval: 0, + invalidRequestWarningInterval: 0, + partials: [], + restWsBridgeTimeout: 5000, + restRequestTimeout: 15000, + restGlobalRateLimit: 0, + retryLimit: 1, + restTimeOffset: 500, + restSweepInterval: 60, + presence: {}, + ws: { + large_threshold: 50, + compress: false, + properties: { + $os: process.platform, + $browser: 'discord.js', + $device: 'discord.js', + }, + version: 9, + }, + http: { + version: 9, + api: 'https://discord.com/api', + cdn: 'https://cdn.discordapp.com', + invite: 'https://discord.gg', + template: 'https://discord.new', + }, + }; + } + + /** + * Create a cache factory using predefined limits. + * @param {Record} [limits={}] Limits for structures. + * @returns {CacheFactory} + */ + static cacheWithLimits(limits = {}) { + const Collection = require('./Collection'); + const LimitedCollection = require('./LimitedCollection'); + + return manager => { + const limit = limits[manager.name]; + if (limit === null || limit === undefined || limit === Infinity) { + return new Collection(); + } + return new LimitedCollection(limit); + }; + } + + /** + * Create a cache factory that always caches everything. + * @returns {CacheFactory} + */ + static cacheEverything() { + const Collection = require('./Collection'); + return () => new Collection(); + } +} + +module.exports = Options; diff --git a/src/util/Util.js b/src/util/Util.js index e891182daa92..9e0ce6b12b1d 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -2,7 +2,8 @@ const { parse } = require('path'); const fetch = require('node-fetch'); -const { Colors, DefaultOptions, Endpoints } = require('./Constants'); +const { Colors, Endpoints } = require('./Constants'); +const Options = require('./Options'); const { Error: DiscordError, RangeError, TypeError } = require('../errors'); const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k); const isObject = d => typeof d === 'object' && d !== null; @@ -261,7 +262,8 @@ class Util extends null { */ static fetchRecommendedShards(token, guildsPerShard = 1000) { if (!token) throw new DiscordError('TOKEN_MISSING'); - return fetch(`${DefaultOptions.http.api}/v${DefaultOptions.http.version}${Endpoints.botGateway}`, { + const defaults = Options.createDefault(); + return fetch(`${defaults.http.api}/v${defaults.http.version}${Endpoints.botGateway}`, { method: 'GET', headers: { Authorization: `Bot ${token.replace(/^Bot\s*/i, '')}` }, }) diff --git a/test/tester2000.js b/test/tester2000.js new file mode 100644 index 000000000000..3d52b81fbe04 --- /dev/null +++ b/test/tester2000.js @@ -0,0 +1,57 @@ +'use strict'; + +const { token, prefix, owner } = require('./auth.js'); +const { Client, Options, Intents, Formatters } = require('../src'); + +// eslint-disable-next-line no-console +const log = (...args) => console.log(process.uptime().toFixed(3), ...args); + +const client = new Client({ + intents: Intents.ALL, + makeCache: Options.cacheWithLimits({ + MessageManager: 10, + PresenceManager: 10, + }), +}); + +client.on('debug', log); +client.on('ready', () => { + log('READY', client.user.tag, client.user.id); +}); +client.on('rateLimit', log); +client.on('error', console.error); + +const commands = { + eval: message => { + if (message.author.id !== owner) return; + let res; + try { + res = eval(message.content); + if (typeof res !== 'string') res = require('util').inspect(res); + } catch (err) { + // eslint-disable-next-line no-console + console.error(err.stack); + res = err.message; + } + message.channel.send(Formatters.codeBlock(res)); + }, + ping: message => message.channel.send('pong'), +}; + +client.on('message', message => { + if (!message.content.startsWith(prefix) || message.author.bot) return; + + message.content = message.content.replace(prefix, '').trim().split(' '); + const command = message.content.shift(); + message.content = message.content.join(' '); + + // eslint-disable-next-line no-console + console.log('COMMAND', command, message.content); + + if (command in commands) commands[command](message); +}); + +client.login(token); + +// eslint-disable-next-line no-console +process.on('unhandledRejection', console.error); diff --git a/typings/index.d.ts b/typings/index.d.ts index 423be4069515..3d2a68890e45 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -526,6 +526,12 @@ declare module 'discord.js' { public setUsername(username: string): Promise; } + export class Options { + public static createDefaultOptions(): ClientOptions; + public static cacheWithLimits(limits?: Record): CacheFactory; + public static cacheEverything(): CacheFactory; + } + export class ClientVoiceManager { constructor(client: Client); public readonly client: Client; @@ -608,7 +614,6 @@ declare module 'discord.js' { devDependencies: { [key: string]: string }; [key: string]: any; }; - DefaultOptions: ClientOptions; UserAgent: string | null; Endpoints: { botGateway: string; @@ -2340,13 +2345,15 @@ declare module 'discord.js' { //#region Managers - export abstract class BaseManager { - constructor(client: Client, iterable: Iterable, holds: Constructable, cacheType: Collection); - public holds: Constructable; - public cache: Collection; - public cacheType: Collection; + export abstract class BaseManager { + constructor(client: Client); public readonly client: Client; - public add(data: any, cache?: boolean, { id, extras }?: { id: K; extras: any[] }): Holds; + } + + export abstract class DataManager extends BaseManager { + constructor(client: Client, holds: Constructable); + public readonly holds: Constructable; + public readonly cache: Collection; public resolve(resolvable: Holds): Holds; public resolve(resolvable: R): Holds | null; public resolveID(resolvable: Holds): K; @@ -2354,11 +2361,16 @@ declare module 'discord.js' { public valueOf(): Collection; } + export abstract class CachedManager extends DataManager { + constructor(client: Client, holds: Constructable); + public add(data: any, cache?: boolean, { id, extras }?: { id: K; extras: any[] }): Holds; + } + export class ApplicationCommandManager< ApplicationCommandType = ApplicationCommand<{ guild: GuildResolvable }>, PermissionsOptionsExtras = { guild: GuildResolvable }, PermissionsGuildType = null, - > extends BaseManager { + > extends CachedManager { constructor(client: Client, iterable?: Iterable); public permissions: ApplicationCommandPermissionsManager< { command?: ApplicationCommandResolvable } & PermissionsOptionsExtras, @@ -2400,7 +2412,12 @@ declare module 'discord.js' { private static transformCommand(command: ApplicationCommandData): unknown; } - export class ApplicationCommandPermissionsManager { + export class ApplicationCommandPermissionsManager< + BaseOptions, + FetchSingleOptions, + GuildType, + CommandIDType, + > extends BaseManager { constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand); public client: Client; public commandID: CommandIDType; @@ -2436,12 +2453,12 @@ declare module 'discord.js' { private static transformPermissions(permissions: ApplicationCommandPermissionData, received?: boolean): unknown; } - export class BaseGuildEmojiManager extends BaseManager { + export class BaseGuildEmojiManager extends CachedManager { constructor(client: Client, iterable?: Iterable); public resolveIdentifier(emoji: EmojiIdentifierResolvable): string | null; } - export class ChannelManager extends BaseManager { + export class ChannelManager extends CachedManager { constructor(client: Client, iterable: Iterable); public fetch(id: Snowflake, options?: BaseFetchOptions): Promise; } @@ -2457,7 +2474,7 @@ declare module 'discord.js' { public set(commands: ApplicationCommandData[]): Promise>; } - export class GuildChannelManager extends BaseManager< + export class GuildChannelManager extends CachedManager< Snowflake, GuildChannel | ThreadChannel, GuildChannelResolvable @@ -2499,11 +2516,10 @@ declare module 'discord.js' { public fetch(id?: Snowflake, options?: BaseFetchOptions): Promise>; } - export class GuildEmojiRoleManager { + export class GuildEmojiRoleManager extends DataManager { constructor(emoji: GuildEmoji); public emoji: GuildEmoji; public guild: Guild; - public cache: Collection; public add( roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection, ): Promise; @@ -2511,17 +2527,16 @@ declare module 'discord.js' { public remove( roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection, ): Promise; - public valueOf(): Collection; } - export class GuildManager extends BaseManager { + export class GuildManager extends CachedManager { constructor(client: Client, iterable?: Iterable); public create(name: string, options?: GuildCreateOptions): Promise; public fetch(options: Snowflake | FetchGuildOptions): Promise; public fetch(options?: FetchGuildsOptions): Promise>; } - export class GuildMemberManager extends BaseManager { + export class GuildMemberManager extends CachedManager { constructor(guild: Guild, iterable?: Iterable); public guild: Guild; public ban(user: UserResolvable, options?: BanOptions): Promise; @@ -2537,7 +2552,7 @@ declare module 'discord.js' { public unban(user: UserResolvable, reason?: string): Promise; } - export class GuildBanManager extends BaseManager { + export class GuildBanManager extends CachedManager { constructor(guild: Guild, iterable?: Iterable); public guild: Guild; public create(user: UserResolvable, options?: BanOptions): Promise; @@ -2546,9 +2561,8 @@ declare module 'discord.js' { public remove(user: UserResolvable, reason?: string): Promise; } - export class GuildMemberRoleManager { + export class GuildMemberRoleManager extends DataManager { constructor(member: GuildMember); - public readonly cache: Collection; public readonly hoist: Role | null; public readonly color: Role | null; public readonly highest: Role; @@ -2566,10 +2580,9 @@ declare module 'discord.js' { roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection, reason?: string, ): Promise; - public valueOf(): Collection; } - export class MessageManager extends BaseManager { + export class MessageManager extends CachedManager { constructor(channel: TextChannel | DMChannel | ThreadChannel, iterable?: Iterable); public channel: TextBasedChannelFields; public cache: Collection; @@ -2587,7 +2600,7 @@ declare module 'discord.js' { public unpin(message: MessageResolvable): Promise; } - export class PermissionOverwriteManager extends BaseManager< + export class PermissionOverwriteManager extends CachedManager< Snowflake, PermissionOverwrites, PermissionOverwriteResolvable @@ -2616,24 +2629,24 @@ declare module 'discord.js' { public delete(userOrRole: RoleResolvable | UserResolvable, reason?: string): Promise; } - export class PresenceManager extends BaseManager { + export class PresenceManager extends CachedManager { constructor(client: Client, iterable?: Iterable); } - export class ReactionManager extends BaseManager { + export class ReactionManager extends CachedManager { constructor(message: Message, iterable?: Iterable); public message: Message; public removeAll(): Promise; } - export class ReactionUserManager extends BaseManager { - constructor(client: Client, iterable: Iterable | undefined, reaction: MessageReaction); + export class ReactionUserManager extends CachedManager { + constructor(reaction: MessageReaction, iterable?: Iterable); public reaction: MessageReaction; public fetch(options?: FetchReactionUsersOptions): Promise>; public remove(user?: UserResolvable): Promise; } - export class RoleManager extends BaseManager { + export class RoleManager extends CachedManager { constructor(guild: Guild, iterable?: Iterable); public readonly everyone: Role; public readonly highest: Role; @@ -2646,7 +2659,7 @@ declare module 'discord.js' { public edit(role: RoleResolvable, options: RoleData, reason?: string): Promise; } - export class StageInstanceManager extends BaseManager { + export class StageInstanceManager extends CachedManager { constructor(guild: Guild, iterable?: Iterable); public guild: Guild; public create(channel: StageChannel | Snowflake, options: StageInstanceCreateOptions): Promise; @@ -2655,7 +2668,7 @@ declare module 'discord.js' { public delete(channel: StageChannel | Snowflake): Promise; } - export class ThreadManager extends BaseManager { + export class ThreadManager extends CachedManager { constructor(channel: TextChannel | NewsChannel, iterable?: Iterable); public channel: TextChannel | NewsChannel; public create(options: ThreadCreateOptions): Promise; @@ -2666,7 +2679,7 @@ declare module 'discord.js' { } export interface ThreadMemberManager - extends Omit, 'add'> {} + extends Omit, 'add'> {} export class ThreadMemberManager { constructor(thread: ThreadChannel, iterable?: Iterable); public thread: ThreadChannel; @@ -2676,12 +2689,12 @@ declare module 'discord.js' { public remove(id: Snowflake | '@me', reason?: string): Promise; } - export class UserManager extends BaseManager { + export class UserManager extends CachedManager { constructor(client: Client, iterable?: Iterable); public fetch(id: Snowflake, options?: BaseFetchOptions): Promise; } - export class VoiceStateManager extends BaseManager { + export class VoiceStateManager extends CachedManager { constructor(guild: Guild, iterable?: Iterable); public guild: Guild; } @@ -3008,6 +3021,8 @@ declare module 'discord.js' { type BufferResolvable = Buffer | string; + type CacheFactory = (manager: { name: string }, holds: { name: string }) => Collection; + interface ChannelCreationOverwrites { allow?: PermissionResolvable; deny?: PermissionResolvable; @@ -3133,7 +3148,7 @@ declare module 'discord.js' { interface ClientOptions { shards?: number | number[] | 'auto'; shardCount?: number; - messageCacheMaxSize?: number; + makeCache?: CacheFactory; messageCacheLifetime?: number; messageSweepInterval?: number; allowedMentions?: MessageMentionOptions; diff --git a/typings/index.ts b/typings/index.ts index a11c13659974..b7939ca4f4e4 100644 --- a/typings/index.ts +++ b/typings/index.ts @@ -2,6 +2,7 @@ import { Client, + Options, Collection, Intents, Message, @@ -17,6 +18,9 @@ import { const client: Client = new Client({ intents: Intents.NON_PRIVILEGED, + makeCache: Options.cacheWithLimits({ + MessageManager: 200, + }), }); const testGuildID = '222078108977594368'; // DJS