feat(Managers): add customizable caching for managers (#6013)

This commit is contained in:
1Computer1
2021-07-03 19:26:26 -04:00
committed by GitHub
parent ec06ba7ad0
commit 8c7cb0eff8
35 changed files with 498 additions and 316 deletions

View File

@@ -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

View File

@@ -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');

View File

@@ -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);

View File

@@ -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'),

View File

@@ -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

View File

@@ -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 });
}
/**

View File

@@ -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);
}
/**

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}
/**

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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 });
}
/**

View File

@@ -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);
}
/**

View File

@@ -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}

View File

@@ -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;

View File

@@ -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}

View File

@@ -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

View File

@@ -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);
}
/**

View File

@@ -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

View File

@@ -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}

View File

@@ -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}

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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);
}
/**

View File

@@ -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}

View File

@@ -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);

View File

@@ -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<boolean>}
*/
/**
* 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 = {

160
src/util/Options.js Normal file
View File

@@ -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<boolean>}
*/
/**
* @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<string, number>} [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;

View File

@@ -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, '')}` },
})

57
test/tester2000.js Normal file
View File

@@ -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);

83
typings/index.d.ts vendored
View File

@@ -526,6 +526,12 @@ declare module 'discord.js' {
public setUsername(username: string): Promise<this>;
}
export class Options {
public static createDefaultOptions(): ClientOptions;
public static cacheWithLimits(limits?: Record<string, number>): 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<K, Holds, R> {
constructor(client: Client, iterable: Iterable<any>, holds: Constructable<Holds>, cacheType: Collection<K, Holds>);
public holds: Constructable<Holds>;
public cache: Collection<K, Holds>;
public cacheType: Collection<K, Holds>;
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<K, Holds, R> extends BaseManager {
constructor(client: Client, holds: Constructable<Holds>);
public readonly holds: Constructable<Holds>;
public readonly cache: Collection<K, Holds>;
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<K, Holds>;
}
export abstract class CachedManager<K, Holds, R> extends DataManager<K, Holds, R> {
constructor(client: Client, holds: Constructable<Holds>);
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<Snowflake, ApplicationCommandType, ApplicationCommandResolvable> {
> extends CachedManager<Snowflake, ApplicationCommandType, ApplicationCommandResolvable> {
constructor(client: Client, iterable?: Iterable<any>);
public permissions: ApplicationCommandPermissionsManager<
{ command?: ApplicationCommandResolvable } & PermissionsOptionsExtras,
@@ -2400,7 +2412,12 @@ declare module 'discord.js' {
private static transformCommand(command: ApplicationCommandData): unknown;
}
export class ApplicationCommandPermissionsManager<BaseOptions, FetchSingleOptions, GuildType, CommandIDType> {
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<Snowflake, GuildEmoji, EmojiResolvable> {
export class BaseGuildEmojiManager extends CachedManager<Snowflake, GuildEmoji, EmojiResolvable> {
constructor(client: Client, iterable?: Iterable<any>);
public resolveIdentifier(emoji: EmojiIdentifierResolvable): string | null;
}
export class ChannelManager extends BaseManager<Snowflake, Channel, ChannelResolvable> {
export class ChannelManager extends CachedManager<Snowflake, Channel, ChannelResolvable> {
constructor(client: Client, iterable: Iterable<any>);
public fetch(id: Snowflake, options?: BaseFetchOptions): Promise<Channel | null>;
}
@@ -2457,7 +2474,7 @@ declare module 'discord.js' {
public set(commands: ApplicationCommandData[]): Promise<Collection<Snowflake, ApplicationCommand>>;
}
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<Collection<Snowflake, GuildEmoji>>;
}
export class GuildEmojiRoleManager {
export class GuildEmojiRoleManager extends DataManager<Snowflake, Role, RoleResolvable> {
constructor(emoji: GuildEmoji);
public emoji: GuildEmoji;
public guild: Guild;
public cache: Collection<Snowflake, Role>;
public add(
roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection<Snowflake, Role>,
): Promise<GuildEmoji>;
@@ -2511,17 +2527,16 @@ declare module 'discord.js' {
public remove(
roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection<Snowflake, Role>,
): Promise<GuildEmoji>;
public valueOf(): Collection<Snowflake, Role>;
}
export class GuildManager extends BaseManager<Snowflake, Guild, GuildResolvable> {
export class GuildManager extends CachedManager<Snowflake, Guild, GuildResolvable> {
constructor(client: Client, iterable?: Iterable<any>);
public create(name: string, options?: GuildCreateOptions): Promise<Guild>;
public fetch(options: Snowflake | FetchGuildOptions): Promise<Guild>;
public fetch(options?: FetchGuildsOptions): Promise<Collection<Snowflake, OAuth2Guild>>;
}
export class GuildMemberManager extends BaseManager<Snowflake, GuildMember, GuildMemberResolvable> {
export class GuildMemberManager extends CachedManager<Snowflake, GuildMember, GuildMemberResolvable> {
constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild;
public ban(user: UserResolvable, options?: BanOptions): Promise<GuildMember | User | Snowflake>;
@@ -2537,7 +2552,7 @@ declare module 'discord.js' {
public unban(user: UserResolvable, reason?: string): Promise<User>;
}
export class GuildBanManager extends BaseManager<Snowflake, GuildBan, GuildBanResolvable> {
export class GuildBanManager extends CachedManager<Snowflake, GuildBan, GuildBanResolvable> {
constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild;
public create(user: UserResolvable, options?: BanOptions): Promise<GuildMember | User | Snowflake>;
@@ -2546,9 +2561,8 @@ declare module 'discord.js' {
public remove(user: UserResolvable, reason?: string): Promise<User>;
}
export class GuildMemberRoleManager {
export class GuildMemberRoleManager extends DataManager<Snowflake, Role, RoleResolvable> {
constructor(member: GuildMember);
public readonly cache: Collection<Snowflake, Role>;
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<Snowflake, Role>,
reason?: string,
): Promise<GuildMember>;
public valueOf(): Collection<Snowflake, Role>;
}
export class MessageManager extends BaseManager<Snowflake, Message, MessageResolvable> {
export class MessageManager extends CachedManager<Snowflake, Message, MessageResolvable> {
constructor(channel: TextChannel | DMChannel | ThreadChannel, iterable?: Iterable<any>);
public channel: TextBasedChannelFields;
public cache: Collection<Snowflake, Message>;
@@ -2587,7 +2600,7 @@ declare module 'discord.js' {
public unpin(message: MessageResolvable): Promise<void>;
}
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<GuildChannel>;
}
export class PresenceManager extends BaseManager<Snowflake, Presence, PresenceResolvable> {
export class PresenceManager extends CachedManager<Snowflake, Presence, PresenceResolvable> {
constructor(client: Client, iterable?: Iterable<any>);
}
export class ReactionManager extends BaseManager<Snowflake | string, MessageReaction, MessageReactionResolvable> {
export class ReactionManager extends CachedManager<Snowflake | string, MessageReaction, MessageReactionResolvable> {
constructor(message: Message, iterable?: Iterable<any>);
public message: Message;
public removeAll(): Promise<Message>;
}
export class ReactionUserManager extends BaseManager<Snowflake, User, UserResolvable> {
constructor(client: Client, iterable: Iterable<any> | undefined, reaction: MessageReaction);
export class ReactionUserManager extends CachedManager<Snowflake, User, UserResolvable> {
constructor(reaction: MessageReaction, iterable?: Iterable<any>);
public reaction: MessageReaction;
public fetch(options?: FetchReactionUsersOptions): Promise<Collection<Snowflake, User>>;
public remove(user?: UserResolvable): Promise<MessageReaction>;
}
export class RoleManager extends BaseManager<Snowflake, Role, RoleResolvable> {
export class RoleManager extends CachedManager<Snowflake, Role, RoleResolvable> {
constructor(guild: Guild, iterable?: Iterable<any>);
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<Role>;
}
export class StageInstanceManager extends BaseManager<Snowflake, StageInstance, StageInstanceResolvable> {
export class StageInstanceManager extends CachedManager<Snowflake, StageInstance, StageInstanceResolvable> {
constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild;
public create(channel: StageChannel | Snowflake, options: StageInstanceCreateOptions): Promise<StageInstance>;
@@ -2655,7 +2668,7 @@ declare module 'discord.js' {
public delete(channel: StageChannel | Snowflake): Promise<void>;
}
export class ThreadManager<AllowedThreadType> extends BaseManager<Snowflake, ThreadChannel, ThreadChannelResolvable> {
export class ThreadManager<AllowedThreadType> extends CachedManager<Snowflake, ThreadChannel, ThreadChannelResolvable> {
constructor(channel: TextChannel | NewsChannel, iterable?: Iterable<any>);
public channel: TextChannel | NewsChannel;
public create(options: ThreadCreateOptions<AllowedThreadType>): Promise<ThreadChannel>;
@@ -2666,7 +2679,7 @@ declare module 'discord.js' {
}
export interface ThreadMemberManager
extends Omit<BaseManager<Snowflake, ThreadMember, ThreadMemberResolvable>, 'add'> {}
extends Omit<CachedManager<Snowflake, ThreadMember, ThreadMemberResolvable>, 'add'> {}
export class ThreadMemberManager {
constructor(thread: ThreadChannel, iterable?: Iterable<any>);
public thread: ThreadChannel;
@@ -2676,12 +2689,12 @@ declare module 'discord.js' {
public remove(id: Snowflake | '@me', reason?: string): Promise<Snowflake>;
}
export class UserManager extends BaseManager<Snowflake, User, UserResolvable> {
export class UserManager extends CachedManager<Snowflake, User, UserResolvable> {
constructor(client: Client, iterable?: Iterable<any>);
public fetch(id: Snowflake, options?: BaseFetchOptions): Promise<User>;
}
export class VoiceStateManager extends BaseManager<Snowflake, VoiceState, typeof VoiceState> {
export class VoiceStateManager extends CachedManager<Snowflake, VoiceState, typeof VoiceState> {
constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild;
}
@@ -3008,6 +3021,8 @@ declare module 'discord.js' {
type BufferResolvable = Buffer | string;
type CacheFactory = <T>(manager: { name: string }, holds: { name: string }) => Collection<Snowflake, T>;
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;

View File

@@ -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