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 EventEmitter = require('events');
const RESTManager = require('../rest/RESTManager'); const RESTManager = require('../rest/RESTManager');
const { DefaultOptions } = require('../util/Constants'); const Options = require('../util/Options');
const Util = require('../util/Util'); const Util = require('../util/Util');
/** /**
@@ -38,7 +38,7 @@ class BaseClient extends EventEmitter {
* The options the client was instantiated with * The options the client was instantiated with
* @type {ClientOptions} * @type {ClientOptions}
*/ */
this.options = Util.mergeDefault(DefaultOptions, options); this.options = Util.mergeDefault(Options.createDefault(), options);
/** /**
* The REST manager of the client * The REST manager of the client

View File

@@ -17,9 +17,10 @@ const VoiceRegion = require('../structures/VoiceRegion');
const Webhook = require('../structures/Webhook'); const Webhook = require('../structures/Webhook');
const Widget = require('../structures/Widget'); const Widget = require('../structures/Widget');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
const { Events, DefaultOptions, InviteScopes } = require('../util/Constants'); const { Events, InviteScopes } = require('../util/Constants');
const DataResolver = require('../util/DataResolver'); const DataResolver = require('../util/DataResolver');
const Intents = require('../util/Intents'); const Intents = require('../util/Intents');
const Options = require('../util/Options');
const Permissions = require('../util/Permissions'); const Permissions = require('../util/Permissions');
const Structures = require('../util/Structures'); const Structures = require('../util/Structures');
@@ -35,14 +36,15 @@ class Client extends BaseClient {
super(Object.assign({ _tokenType: 'Bot' }, options)); super(Object.assign({ _tokenType: 'Bot' }, options));
const data = require('worker_threads').workerData ?? process.env; 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) { if ('SHARDS' in data) {
this.options.shards = JSON.parse(data.SHARDS); this.options.shards = JSON.parse(data.SHARDS);
} }
} }
if (this.options.shardCount === DefaultOptions.shardCount) { if (this.options.shardCount === defaults.shardCount) {
if ('SHARD_COUNT' in data) { if ('SHARD_COUNT' in data) {
this.options.shardCount = Number(data.SHARD_COUNT); this.options.shardCount = Number(data.SHARD_COUNT);
} else if (Array.isArray(this.options.shards)) { } 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"); 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 (options.shards && !options.shards.length) throw new RangeError('CLIENT_INVALID_PROVIDED_SHARDS');
if (typeof options.messageCacheMaxSize !== 'number' || isNaN(options.messageCacheMaxSize)) { if (typeof options.makeCache !== 'function') {
throw new TypeError('CLIENT_INVALID_OPTION', 'messageCacheMaxSize', 'a number'); throw new TypeError('CLIENT_INVALID_OPTION', 'makeCache', 'a function');
} }
if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) { if (typeof options.messageCacheLifetime !== 'number' || isNaN(options.messageCacheLifetime)) {
throw new TypeError('CLIENT_INVALID_OPTION', 'The messageCacheLifetime', 'a number'); 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_NOT_REPLIED: 'This interaction has not been deferred or replied to.',
INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be fetched or deleted.', INTERACTION_EPHEMERAL_REPLIED: 'Ephemeral responses cannot be fetched or deleted.',
INTERACTION_FETCH_EPHEMERAL: 'Ephemeral responses cannot be fetched.', 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); for (const [name, message] of Object.entries(Messages)) register(name, message);

View File

@@ -23,6 +23,7 @@ module.exports = {
RateLimitError: require('./rest/RateLimitError'), RateLimitError: require('./rest/RateLimitError'),
MessageFlags: require('./util/MessageFlags'), MessageFlags: require('./util/MessageFlags'),
Intents: require('./util/Intents'), Intents: require('./util/Intents'),
Options: require('./util/Options'),
Permissions: require('./util/Permissions'), Permissions: require('./util/Permissions'),
SnowflakeUtil: require('./util/SnowflakeUtil'), SnowflakeUtil: require('./util/SnowflakeUtil'),
Structures: require('./util/Structures'), Structures: require('./util/Structures'),

View File

@@ -1,18 +1,18 @@
'use strict'; 'use strict';
const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager'); const ApplicationCommandPermissionsManager = require('./ApplicationCommandPermissionsManager');
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const ApplicationCommand = require('../structures/ApplicationCommand'); const ApplicationCommand = require('../structures/ApplicationCommand');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
/** /**
* Manages API methods for application commands and stores their cache. * 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) { constructor(client, iterable) {
super(client, iterable, ApplicationCommand); super(client, ApplicationCommand, iterable);
/** /**
* The manager for permissions of arbitrary commands on arbitrary guilds * The manager for permissions of arbitrary commands on arbitrary guilds

View File

@@ -1,14 +1,18 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager');
const { Error, TypeError } = require('../errors'); const { Error, TypeError } = require('../errors');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Constants'); const { ApplicationCommandPermissionTypes, APIErrors } = require('../util/Constants');
/** /**
* Manages API methods for permissions of Application Commands. * Manages API methods for permissions of Application Commands.
* @extends {BaseManager}
*/ */
class ApplicationCommandPermissionsManager { class ApplicationCommandPermissionsManager extends BaseManager {
constructor(manager) { constructor(manager) {
super(manager.client);
/** /**
* The manager or command that this manager belongs to * The manager or command that this manager belongs to
* @type {ApplicationCommandManager|ApplicationCommand} * @type {ApplicationCommandManager|ApplicationCommand}
@@ -32,14 +36,6 @@ class ApplicationCommandPermissionsManager {
* @type {?Snowflake} * @type {?Snowflake}
*/ */
this.commandID = manager.id ?? null; 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'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const GuildEmoji = require('../structures/GuildEmoji'); const GuildEmoji = require('../structures/GuildEmoji');
const ReactionEmoji = require('../structures/ReactionEmoji'); const ReactionEmoji = require('../structures/ReactionEmoji');
const { parseEmoji } = require('../util/Util'); const { parseEmoji } = require('../util/Util');
/** /**
* Holds methods to resolve GuildEmojis and stores their cache. * Holds methods to resolve GuildEmojis and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class BaseGuildEmojiManager extends BaseManager { class BaseGuildEmojiManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client, iterable, GuildEmoji); super(client, GuildEmoji, iterable);
} }
/** /**

View File

@@ -1,24 +1,11 @@
'use strict'; '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 * @abstract
*/ */
class BaseManager { class BaseManager {
constructor(client, iterable, holds, cacheType = Collection, ...cacheOptions) { constructor(client) {
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 });
/** /**
* The client that instantiated this Manager * The client that instantiated this Manager
* @name BaseManager#client * @name BaseManager#client
@@ -26,55 +13,6 @@ class BaseManager {
* @readonly * @readonly
*/ */
Object.defineProperty(this, 'client', { value: client }); 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'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const Channel = require('../structures/Channel'); const Channel = require('../structures/Channel');
const { Events, ThreadChannelTypes } = require('../util/Constants'); const { Events, ThreadChannelTypes } = require('../util/Constants');
/** /**
* A manager of channels belonging to a client * A manager of channels belonging to a client
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class ChannelManager extends BaseManager { class ChannelManager extends CachedManager {
constructor(client, iterable) { 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'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { TypeError, Error } = require('../errors'); const { TypeError, Error } = require('../errors');
const GuildBan = require('../structures/GuildBan'); const GuildBan = require('../structures/GuildBan');
const GuildMember = require('../structures/GuildMember'); const GuildMember = require('../structures/GuildMember');
@@ -8,11 +8,11 @@ const Collection = require('../util/Collection');
/** /**
* Manages API methods for GuildBans and stores their cache. * Manages API methods for GuildBans and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class GuildBanManager extends BaseManager { class GuildBanManager extends CachedManager {
constructor(guild, iterable) { constructor(guild, iterable) {
super(guild.client, iterable, GuildBan); super(guild.client, GuildBan, iterable);
/** /**
* The guild this Manager belongs to * The guild this Manager belongs to

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const GuildChannel = require('../structures/GuildChannel'); const GuildChannel = require('../structures/GuildChannel');
const PermissionOverwrites = require('../structures/PermissionOverwrites'); const PermissionOverwrites = require('../structures/PermissionOverwrites');
const ThreadChannel = require('../structures/ThreadChannel'); const ThreadChannel = require('../structures/ThreadChannel');
@@ -9,11 +9,11 @@ const { ChannelTypes, ThreadChannelTypes } = require('../util/Constants');
/** /**
* Manages API methods for GuildChannels and stores their cache. * Manages API methods for GuildChannels and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class GuildChannelManager extends BaseManager { class GuildChannelManager extends CachedManager {
constructor(guild, iterable) { constructor(guild, iterable) {
super(guild.client, iterable, GuildChannel); super(guild.client, GuildChannel, iterable);
/** /**
* The guild this Manager belongs to * The guild this Manager belongs to

View File

@@ -1,13 +1,18 @@
'use strict'; 'use strict';
const DataManager = require('./DataManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const Role = require('../structures/Role');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
/** /**
* Manages API methods for roles belonging to emojis and stores their cache. * Manages API methods for roles belonging to emojis and stores their cache.
* @extends {DataManager}
*/ */
class GuildEmojiRoleManager { class GuildEmojiRoleManager extends DataManager {
constructor(emoji) { constructor(emoji) {
super(emoji.client, Role);
/** /**
* The emoji belonging to this manager * The emoji belonging to this manager
* @type {GuildEmoji} * @type {GuildEmoji}
@@ -18,12 +23,6 @@ class GuildEmojiRoleManager {
* @type {Guild} * @type {Guild}
*/ */
this.guild = emoji.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'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const Guild = require('../structures/Guild'); const Guild = require('../structures/Guild');
const GuildChannel = require('../structures/GuildChannel'); const GuildChannel = require('../structures/GuildChannel');
const GuildEmoji = require('../structures/GuildEmoji'); const GuildEmoji = require('../structures/GuildEmoji');
@@ -23,11 +23,11 @@ const { resolveColor } = require('../util/Util');
/** /**
* Manages API methods for Guilds and stores their cache. * Manages API methods for Guilds and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class GuildManager extends BaseManager { class GuildManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client, iterable, Guild); super(client, Guild, iterable);
} }
/** /**

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { Error, TypeError, RangeError } = require('../errors'); const { Error, TypeError, RangeError } = require('../errors');
const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel'); const BaseGuildVoiceChannel = require('../structures/BaseGuildVoiceChannel');
const GuildMember = require('../structures/GuildMember'); const GuildMember = require('../structures/GuildMember');
@@ -11,11 +11,12 @@ const SnowflakeUtil = require('../util/SnowflakeUtil');
/** /**
* Manages API methods for GuildMembers and stores their cache. * Manages API methods for GuildMembers and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class GuildMemberManager extends BaseManager { class GuildMemberManager extends CachedManager {
constructor(guild, iterable) { constructor(guild, iterable) {
super(guild.client, iterable, GuildMember); super(guild.client, GuildMember, iterable);
/** /**
* The guild this manager belongs to * The guild this manager belongs to
* @type {Guild} * @type {Guild}

View File

@@ -1,24 +1,29 @@
'use strict'; 'use strict';
const DataManager = require('./DataManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const Role = require('../structures/Role');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
/** /**
* Manages API methods for roles of a GuildMember and stores their cache. * Manages API methods for roles of a GuildMember and stores their cache.
* @extends {DataManager}
*/ */
class GuildMemberRoleManager { class GuildMemberRoleManager extends DataManager {
constructor(member) { constructor(member) {
super(member.client, Role);
/** /**
* The GuildMember this manager belongs to * The GuildMember this manager belongs to
* @type {GuildMember} * @type {GuildMember}
*/ */
this.member = member; this.member = member;
/** /**
* The Guild this manager belongs to * The Guild this manager belongs to
* @type {Guild} * @type {Guild}
*/ */
this.guild = member.guild; this.guild = member.guild;
Object.defineProperty(this, 'client', { value: member.client });
} }
/** /**
@@ -170,10 +175,6 @@ class GuildMemberRoleManager {
clone.member._roles = [...this.cache.keyArray()]; clone.member._roles = [...this.cache.keyArray()];
return clone; return clone;
} }
valueOf() {
return this.cache;
}
} }
module.exports = GuildMemberRoleManager; module.exports = GuildMemberRoleManager;

View File

@@ -1,19 +1,19 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const Message = require('../structures/Message'); const Message = require('../structures/Message');
const MessagePayload = require('../structures/MessagePayload'); const MessagePayload = require('../structures/MessagePayload');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
const LimitedCollection = require('../util/LimitedCollection');
/** /**
* Manages API methods for Messages and holds their cache. * Manages API methods for Messages and holds their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class MessageManager extends BaseManager { class MessageManager extends CachedManager {
constructor(channel, iterable) { 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 * The channel that the messages belong to
* @type {TextBasedChannel} * @type {TextBasedChannel}

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const PermissionOverwrites = require('../structures/PermissionOverwrites'); const PermissionOverwrites = require('../structures/PermissionOverwrites');
const Role = require('../structures/Role'); 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. * 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) { constructor(channel, iterable) {
super(channel.client, iterable, PermissionOverwrites); super(channel.client, PermissionOverwrites, iterable);
/** /**
* The channel of the permission overwrite this manager belongs to * The channel of the permission overwrite this manager belongs to

View File

@@ -1,15 +1,15 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { Presence } = require('../structures/Presence'); const { Presence } = require('../structures/Presence');
/** /**
* Manages API methods for Presences and holds their cache. * Manages API methods for Presences and holds their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class PresenceManager extends BaseManager { class PresenceManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client, iterable, Presence); super(client, Presence, iterable);
} }
/** /**

View File

@@ -1,15 +1,15 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const MessageReaction = require('../structures/MessageReaction'); const MessageReaction = require('../structures/MessageReaction');
/** /**
* Manages API methods for reactions and holds their cache. * Manages API methods for reactions and holds their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class ReactionManager extends BaseManager { class ReactionManager extends CachedManager {
constructor(message, iterable) { constructor(message, iterable) {
super(message.client, iterable, MessageReaction); super(message.client, MessageReaction, iterable);
/** /**
* The message that this manager belongs to * The message that this manager belongs to

View File

@@ -1,16 +1,18 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { Error } = require('../errors'); const { Error } = require('../errors');
const User = require('../structures/User');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
/** /**
* Manages API methods for users who reacted to a reaction and stores their cache. * Manages API methods for users who reacted to a reaction and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class ReactionUserManager extends BaseManager { class ReactionUserManager extends CachedManager {
constructor(client, iterable, reaction) { constructor(reaction, iterable) {
super(client, iterable, { name: 'User' }); super(reaction.client, User, iterable);
/** /**
* The reaction that this manager belongs to * The reaction that this manager belongs to
* @type {MessageReaction} * @type {MessageReaction}

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const Role = require('../structures/Role'); const Role = require('../structures/Role');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
@@ -9,11 +9,12 @@ const { resolveColor, setPosition } = require('../util/Util');
/** /**
* Manages API methods for roles and stores their cache. * Manages API methods for roles and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class RoleManager extends BaseManager { class RoleManager extends CachedManager {
constructor(guild, iterable) { constructor(guild, iterable) {
super(guild.client, iterable, Role); super(guild.client, Role, iterable);
/** /**
* The guild belonging to this manager * The guild belonging to this manager
* @type {Guild} * @type {Guild}

View File

@@ -1,17 +1,17 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { TypeError, Error } = require('../errors'); const { TypeError, Error } = require('../errors');
const StageInstance = require('../structures/StageInstance'); const StageInstance = require('../structures/StageInstance');
const { PrivacyLevels } = require('../util/Constants'); const { PrivacyLevels } = require('../util/Constants');
/** /**
* Manages API methods for {@link StageInstance} objects and holds their cache. * 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) { constructor(guild, iterable) {
super(guild.client, iterable, StageInstance); super(guild.client, StageInstance, iterable);
/** /**
* The guild this manager belongs to * The guild this manager belongs to

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const ThreadChannel = require('../structures/ThreadChannel'); const ThreadChannel = require('../structures/ThreadChannel');
const Collection = require('../util/Collection'); 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. * 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) { constructor(channel, iterable) {
super(channel.client, iterable, ThreadChannel); super(channel.client, ThreadChannel, iterable);
/** /**
* The channel this Manager belongs to * The channel this Manager belongs to

View File

@@ -1,17 +1,18 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const { TypeError } = require('../errors'); const { TypeError } = require('../errors');
const ThreadMember = require('../structures/ThreadMember'); const ThreadMember = require('../structures/ThreadMember');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
/** /**
* Manages API methods for ThreadMembers and stores their cache. * Manages API methods for GuildMembers and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class ThreadMemberManager extends BaseManager { class ThreadMemberManager extends CachedManager {
constructor(thread, iterable) { constructor(thread, iterable) {
super(thread.client, iterable, ThreadMember); super(thread.client, ThreadMember, iterable);
/** /**
* The thread this manager belongs to * The thread this manager belongs to
* @type {ThreadChannel} * @type {ThreadChannel}

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const GuildMember = require('../structures/GuildMember'); const GuildMember = require('../structures/GuildMember');
const Message = require('../structures/Message'); const Message = require('../structures/Message');
const ThreadMember = require('../structures/ThreadMember'); const ThreadMember = require('../structures/ThreadMember');
@@ -8,11 +8,11 @@ const User = require('../structures/User');
/** /**
* Manages API methods for users and stores their cache. * Manages API methods for users and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class UserManager extends BaseManager { class UserManager extends CachedManager {
constructor(client, iterable) { constructor(client, iterable) {
super(client, iterable, User); super(client, User, iterable);
} }
/** /**

View File

@@ -1,14 +1,16 @@
'use strict'; 'use strict';
const BaseManager = require('./BaseManager'); const CachedManager = require('./CachedManager');
const VoiceState = require('../structures/VoiceState');
/** /**
* Manages API methods for VoiceStates and stores their cache. * Manages API methods for VoiceStates and stores their cache.
* @extends {BaseManager} * @extends {CachedManager}
*/ */
class VoiceStateManager extends BaseManager { class VoiceStateManager extends CachedManager {
constructor(guild, iterable) { constructor(guild, iterable) {
super(guild.client, iterable, { name: 'VoiceState' }); super(guild.client, VoiceState, iterable);
/** /**
* The guild this manager belongs to * The guild this manager belongs to
* @type {Guild} * @type {Guild}

View File

@@ -22,6 +22,7 @@ class MessageReaction {
* @readonly * @readonly
*/ */
Object.defineProperty(this, 'client', { value: client }); Object.defineProperty(this, 'client', { value: client });
/** /**
* The message that this reaction refers to * The message that this reaction refers to
* @type {Message} * @type {Message}
@@ -38,7 +39,7 @@ class MessageReaction {
* A manager of the users that have given this reaction * A manager of the users that have given this reaction
* @type {ReactionUserManager} * @type {ReactionUserManager}
*/ */
this.users = new ReactionUserManager(client, undefined, this); this.users = new ReactionUserManager(this);
this._emoji = new ReactionEmoji(this, data.emoji); this._emoji = new ReactionEmoji(this, data.emoji);

View File

@@ -3,117 +3,6 @@
const Package = (exports.Package = require('../../package.json')); const Package = (exports.Package = require('../../package.json'));
const { Error, RangeError } = require('../errors'); 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.UserAgent = `DiscordBot (${Package.homepage.split('#')[0]}, ${Package.version}) Node.js/${process.version}`;
exports.WSCodes = { 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 { parse } = require('path');
const fetch = require('node-fetch'); 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 { Error: DiscordError, RangeError, TypeError } = require('../errors');
const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k); const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
const isObject = d => typeof d === 'object' && d !== null; const isObject = d => typeof d === 'object' && d !== null;
@@ -261,7 +262,8 @@ class Util extends null {
*/ */
static fetchRecommendedShards(token, guildsPerShard = 1000) { static fetchRecommendedShards(token, guildsPerShard = 1000) {
if (!token) throw new DiscordError('TOKEN_MISSING'); 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', method: 'GET',
headers: { Authorization: `Bot ${token.replace(/^Bot\s*/i, '')}` }, 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>; 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 { export class ClientVoiceManager {
constructor(client: Client); constructor(client: Client);
public readonly client: Client; public readonly client: Client;
@@ -608,7 +614,6 @@ declare module 'discord.js' {
devDependencies: { [key: string]: string }; devDependencies: { [key: string]: string };
[key: string]: any; [key: string]: any;
}; };
DefaultOptions: ClientOptions;
UserAgent: string | null; UserAgent: string | null;
Endpoints: { Endpoints: {
botGateway: string; botGateway: string;
@@ -2340,13 +2345,15 @@ declare module 'discord.js' {
//#region Managers //#region Managers
export abstract class BaseManager<K, Holds, R> { export abstract class BaseManager {
constructor(client: Client, iterable: Iterable<any>, holds: Constructable<Holds>, cacheType: Collection<K, Holds>); constructor(client: Client);
public holds: Constructable<Holds>;
public cache: Collection<K, Holds>;
public cacheType: Collection<K, Holds>;
public readonly 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: Holds): Holds;
public resolve(resolvable: R): Holds | null; public resolve(resolvable: R): Holds | null;
public resolveID(resolvable: Holds): K; public resolveID(resolvable: Holds): K;
@@ -2354,11 +2361,16 @@ declare module 'discord.js' {
public valueOf(): Collection<K, Holds>; 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< export class ApplicationCommandManager<
ApplicationCommandType = ApplicationCommand<{ guild: GuildResolvable }>, ApplicationCommandType = ApplicationCommand<{ guild: GuildResolvable }>,
PermissionsOptionsExtras = { guild: GuildResolvable }, PermissionsOptionsExtras = { guild: GuildResolvable },
PermissionsGuildType = null, PermissionsGuildType = null,
> extends BaseManager<Snowflake, ApplicationCommandType, ApplicationCommandResolvable> { > extends CachedManager<Snowflake, ApplicationCommandType, ApplicationCommandResolvable> {
constructor(client: Client, iterable?: Iterable<any>); constructor(client: Client, iterable?: Iterable<any>);
public permissions: ApplicationCommandPermissionsManager< public permissions: ApplicationCommandPermissionsManager<
{ command?: ApplicationCommandResolvable } & PermissionsOptionsExtras, { command?: ApplicationCommandResolvable } & PermissionsOptionsExtras,
@@ -2400,7 +2412,12 @@ declare module 'discord.js' {
private static transformCommand(command: ApplicationCommandData): unknown; 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); constructor(manager: ApplicationCommandManager | GuildApplicationCommandManager | ApplicationCommand);
public client: Client; public client: Client;
public commandID: CommandIDType; public commandID: CommandIDType;
@@ -2436,12 +2453,12 @@ declare module 'discord.js' {
private static transformPermissions(permissions: ApplicationCommandPermissionData, received?: boolean): unknown; 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>); constructor(client: Client, iterable?: Iterable<any>);
public resolveIdentifier(emoji: EmojiIdentifierResolvable): string | null; 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>); constructor(client: Client, iterable: Iterable<any>);
public fetch(id: Snowflake, options?: BaseFetchOptions): Promise<Channel | null>; 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>>; public set(commands: ApplicationCommandData[]): Promise<Collection<Snowflake, ApplicationCommand>>;
} }
export class GuildChannelManager extends BaseManager< export class GuildChannelManager extends CachedManager<
Snowflake, Snowflake,
GuildChannel | ThreadChannel, GuildChannel | ThreadChannel,
GuildChannelResolvable GuildChannelResolvable
@@ -2499,11 +2516,10 @@ declare module 'discord.js' {
public fetch(id?: Snowflake, options?: BaseFetchOptions): Promise<Collection<Snowflake, GuildEmoji>>; public fetch(id?: Snowflake, options?: BaseFetchOptions): Promise<Collection<Snowflake, GuildEmoji>>;
} }
export class GuildEmojiRoleManager { export class GuildEmojiRoleManager extends DataManager<Snowflake, Role, RoleResolvable> {
constructor(emoji: GuildEmoji); constructor(emoji: GuildEmoji);
public emoji: GuildEmoji; public emoji: GuildEmoji;
public guild: Guild; public guild: Guild;
public cache: Collection<Snowflake, Role>;
public add( public add(
roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection<Snowflake, Role>, roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection<Snowflake, Role>,
): Promise<GuildEmoji>; ): Promise<GuildEmoji>;
@@ -2511,17 +2527,16 @@ declare module 'discord.js' {
public remove( public remove(
roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection<Snowflake, Role>, roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection<Snowflake, Role>,
): Promise<GuildEmoji>; ): 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>); constructor(client: Client, iterable?: Iterable<any>);
public create(name: string, options?: GuildCreateOptions): Promise<Guild>; public create(name: string, options?: GuildCreateOptions): Promise<Guild>;
public fetch(options: Snowflake | FetchGuildOptions): Promise<Guild>; public fetch(options: Snowflake | FetchGuildOptions): Promise<Guild>;
public fetch(options?: FetchGuildsOptions): Promise<Collection<Snowflake, OAuth2Guild>>; 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>); constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild; public guild: Guild;
public ban(user: UserResolvable, options?: BanOptions): Promise<GuildMember | User | Snowflake>; 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>; 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>); constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild; public guild: Guild;
public create(user: UserResolvable, options?: BanOptions): Promise<GuildMember | User | Snowflake>; 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>; public remove(user: UserResolvable, reason?: string): Promise<User>;
} }
export class GuildMemberRoleManager { export class GuildMemberRoleManager extends DataManager<Snowflake, Role, RoleResolvable> {
constructor(member: GuildMember); constructor(member: GuildMember);
public readonly cache: Collection<Snowflake, Role>;
public readonly hoist: Role | null; public readonly hoist: Role | null;
public readonly color: Role | null; public readonly color: Role | null;
public readonly highest: Role; public readonly highest: Role;
@@ -2566,10 +2580,9 @@ declare module 'discord.js' {
roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection<Snowflake, Role>, roleOrRoles: RoleResolvable | readonly RoleResolvable[] | Collection<Snowflake, Role>,
reason?: string, reason?: string,
): Promise<GuildMember>; ): 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>); constructor(channel: TextChannel | DMChannel | ThreadChannel, iterable?: Iterable<any>);
public channel: TextBasedChannelFields; public channel: TextBasedChannelFields;
public cache: Collection<Snowflake, Message>; public cache: Collection<Snowflake, Message>;
@@ -2587,7 +2600,7 @@ declare module 'discord.js' {
public unpin(message: MessageResolvable): Promise<void>; public unpin(message: MessageResolvable): Promise<void>;
} }
export class PermissionOverwriteManager extends BaseManager< export class PermissionOverwriteManager extends CachedManager<
Snowflake, Snowflake,
PermissionOverwrites, PermissionOverwrites,
PermissionOverwriteResolvable PermissionOverwriteResolvable
@@ -2616,24 +2629,24 @@ declare module 'discord.js' {
public delete(userOrRole: RoleResolvable | UserResolvable, reason?: string): Promise<GuildChannel>; 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>); 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>); constructor(message: Message, iterable?: Iterable<any>);
public message: Message; public message: Message;
public removeAll(): Promise<Message>; public removeAll(): Promise<Message>;
} }
export class ReactionUserManager extends BaseManager<Snowflake, User, UserResolvable> { export class ReactionUserManager extends CachedManager<Snowflake, User, UserResolvable> {
constructor(client: Client, iterable: Iterable<any> | undefined, reaction: MessageReaction); constructor(reaction: MessageReaction, iterable?: Iterable<any>);
public reaction: MessageReaction; public reaction: MessageReaction;
public fetch(options?: FetchReactionUsersOptions): Promise<Collection<Snowflake, User>>; public fetch(options?: FetchReactionUsersOptions): Promise<Collection<Snowflake, User>>;
public remove(user?: UserResolvable): Promise<MessageReaction>; 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>); constructor(guild: Guild, iterable?: Iterable<any>);
public readonly everyone: Role; public readonly everyone: Role;
public readonly highest: Role; public readonly highest: Role;
@@ -2646,7 +2659,7 @@ declare module 'discord.js' {
public edit(role: RoleResolvable, options: RoleData, reason?: string): Promise<Role>; 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>); constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild; public guild: Guild;
public create(channel: StageChannel | Snowflake, options: StageInstanceCreateOptions): Promise<StageInstance>; public create(channel: StageChannel | Snowflake, options: StageInstanceCreateOptions): Promise<StageInstance>;
@@ -2655,7 +2668,7 @@ declare module 'discord.js' {
public delete(channel: StageChannel | Snowflake): Promise<void>; 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>); constructor(channel: TextChannel | NewsChannel, iterable?: Iterable<any>);
public channel: TextChannel | NewsChannel; public channel: TextChannel | NewsChannel;
public create(options: ThreadCreateOptions<AllowedThreadType>): Promise<ThreadChannel>; public create(options: ThreadCreateOptions<AllowedThreadType>): Promise<ThreadChannel>;
@@ -2666,7 +2679,7 @@ declare module 'discord.js' {
} }
export interface ThreadMemberManager export interface ThreadMemberManager
extends Omit<BaseManager<Snowflake, ThreadMember, ThreadMemberResolvable>, 'add'> {} extends Omit<CachedManager<Snowflake, ThreadMember, ThreadMemberResolvable>, 'add'> {}
export class ThreadMemberManager { export class ThreadMemberManager {
constructor(thread: ThreadChannel, iterable?: Iterable<any>); constructor(thread: ThreadChannel, iterable?: Iterable<any>);
public thread: ThreadChannel; public thread: ThreadChannel;
@@ -2676,12 +2689,12 @@ declare module 'discord.js' {
public remove(id: Snowflake | '@me', reason?: string): Promise<Snowflake>; 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>); constructor(client: Client, iterable?: Iterable<any>);
public fetch(id: Snowflake, options?: BaseFetchOptions): Promise<User>; 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>); constructor(guild: Guild, iterable?: Iterable<any>);
public guild: Guild; public guild: Guild;
} }
@@ -3008,6 +3021,8 @@ declare module 'discord.js' {
type BufferResolvable = Buffer | string; type BufferResolvable = Buffer | string;
type CacheFactory = <T>(manager: { name: string }, holds: { name: string }) => Collection<Snowflake, T>;
interface ChannelCreationOverwrites { interface ChannelCreationOverwrites {
allow?: PermissionResolvable; allow?: PermissionResolvable;
deny?: PermissionResolvable; deny?: PermissionResolvable;
@@ -3133,7 +3148,7 @@ declare module 'discord.js' {
interface ClientOptions { interface ClientOptions {
shards?: number | number[] | 'auto'; shards?: number | number[] | 'auto';
shardCount?: number; shardCount?: number;
messageCacheMaxSize?: number; makeCache?: CacheFactory;
messageCacheLifetime?: number; messageCacheLifetime?: number;
messageSweepInterval?: number; messageSweepInterval?: number;
allowedMentions?: MessageMentionOptions; allowedMentions?: MessageMentionOptions;

View File

@@ -2,6 +2,7 @@
import { import {
Client, Client,
Options,
Collection, Collection,
Intents, Intents,
Message, Message,
@@ -17,6 +18,9 @@ import {
const client: Client = new Client({ const client: Client = new Client({
intents: Intents.NON_PRIVILEGED, intents: Intents.NON_PRIVILEGED,
makeCache: Options.cacheWithLimits({
MessageManager: 200,
}),
}); });
const testGuildID = '222078108977594368'; // DJS const testGuildID = '222078108977594368'; // DJS