From bbdbc4cfa789383b7b3dbecf5e6b8401ea2dd998 Mon Sep 17 00:00:00 2001 From: BorgerKing <38166539+RDambrosio016@users.noreply.github.com> Date: Tue, 11 Feb 2020 14:21:07 -0500 Subject: [PATCH] feat: remove datastores and implement Managers (#3696) * Initial commit: add 5 initial managers - Base manager - GuildChannelManager - MessageManager - PresenceManager - Reaction Manager - Added LimitedCollection * Add GuildEmojiManager, various fixes * Modify some managers and add guildmembermanager * Initial integration * Delete old stores * Integration part two, removed LRUCollection - Most of the integration has been finished - TODO typings - Removed LRUCollection, needless sweeping * Typings + stuff i somehow missed in ChannelManager * LimitedCollection typings/ final changes * Various jsdoc and syntactical fixes, Removed Util.mixin() * tslint fix * Grammatical and logical changes * Delete temporary file placed by mistake * Grammatical changes * Add missing type * Update jsdoc examples * fix: ChannelManager#remove should call cache#delete not cache#remove * fix recursive require * Fix missed cache in util * fix: more missed cache * Remove accidental _fetchMany change from #3645 * fix: use .cache.delete() over .remove() * fix: missing cache in ReactionCollector * fix: missed cache in client * fix: members is a collection not a manager Co-Authored-By: Sugden <28943913+NotSugden@users.noreply.github.com> * fix: various docs and cache fixes * fix: missed cache * fix: missing _roles * Final testing and debugging * LimitedCollection: return the Collection instead of undefined on .set * Add cache to BaseManager in typings * Commit fixes i forgot to stage yesterday * Update invite events * Account for new commit * fix: MessageReactionRemoveAll should call .cache.clear() * fix: add .cache at various places, correct return type * docs: remove mentions of 'store' * Add extra documented properties to typings Co-authored-by: Sugden <28943913+NotSugden@users.noreply.github.com> Co-authored-by: SpaceEEC --- docs/examples/greeting.js | 2 +- src/client/Client.js | 34 ++-- src/client/actions/Action.js | 6 +- src/client/actions/ChannelCreate.js | 2 +- src/client/actions/ChannelDelete.js | 4 +- src/client/actions/ChannelUpdate.js | 6 +- src/client/actions/GuildBanRemove.js | 2 +- .../actions/GuildChannelsPositionUpdate.js | 4 +- src/client/actions/GuildDelete.js | 8 +- src/client/actions/GuildEmojiDelete.js | 2 +- src/client/actions/GuildEmojisUpdate.js | 6 +- src/client/actions/GuildIntegrationsUpdate.js | 2 +- src/client/actions/GuildMemberRemove.js | 6 +- src/client/actions/GuildRoleCreate.js | 4 +- src/client/actions/GuildRoleDelete.js | 6 +- src/client/actions/GuildRoleUpdate.js | 4 +- .../actions/GuildRolesPositionUpdate.js | 4 +- src/client/actions/GuildUpdate.js | 2 +- src/client/actions/InviteCreate.js | 4 +- src/client/actions/InviteDelete.js | 4 +- src/client/actions/MessageCreate.js | 4 +- src/client/actions/MessageDelete.js | 2 +- src/client/actions/MessageDeleteBulk.js | 4 +- .../actions/MessageReactionRemoveAll.js | 2 +- .../actions/MessageReactionRemoveEmoji.js | 2 +- src/client/actions/PresenceUpdate.js | 8 +- src/client/actions/UserUpdate.js | 2 +- src/client/actions/VoiceStateUpdate.js | 8 +- src/client/actions/WebhooksUpdate.js | 2 +- src/client/voice/ClientVoiceManager.js | 2 +- src/client/voice/VoiceConnection.js | 2 +- src/client/websocket/WebSocketManager.js | 2 +- .../websocket/handlers/CHANNEL_PINS_UPDATE.js | 2 +- .../websocket/handlers/GUILD_BAN_ADD.js | 2 +- src/client/websocket/handlers/GUILD_CREATE.js | 2 +- .../websocket/handlers/GUILD_MEMBERS_CHUNK.js | 2 +- .../websocket/handlers/GUILD_MEMBER_ADD.js | 2 +- .../websocket/handlers/GUILD_MEMBER_UPDATE.js | 4 +- src/client/websocket/handlers/READY.js | 2 +- src/client/websocket/handlers/TYPING_START.js | 4 +- src/index.js | 29 ++-- .../DataStore.js => managers/BaseManager.js} | 59 ++++--- .../ChannelManager.js} | 73 +++------ .../GuildChannelManager.js} | 30 ++-- .../GuildEmojiManager.js} | 21 ++- .../GuildEmojiRoleManager.js} | 57 ++++--- .../GuildManager.js} | 23 ++- .../GuildMemberManager.js} | 33 ++-- .../GuildMemberRoleManager.js} | 62 +++---- src/managers/MessageManager.js | 141 ++++++++++++++++ .../PresenceManager.js} | 21 ++- .../ReactionManager.js} | 32 ++-- .../ReactionUserManager.js} | 25 ++- .../RoleStore.js => managers/RoleManager.js} | 39 +++-- .../UserStore.js => managers/UserManager.js} | 19 ++- src/managers/VoiceStateManager.js | 37 +++++ src/sharding/ShardClientUtil.js | 4 +- src/sharding/ShardingManager.js | 2 +- src/stores/MessageStore.js | 135 ---------------- src/stores/VoiceStateStore.js | 26 --- src/structures/CategoryChannel.js | 2 +- src/structures/Channel.js | 4 +- src/structures/DMChannel.js | 8 +- src/structures/Emoji.js | 2 +- src/structures/Guild.js | 85 +++++----- src/structures/GuildAuditLogs.js | 16 +- src/structures/GuildChannel.js | 8 +- src/structures/GuildEmoji.js | 16 +- src/structures/GuildMember.js | 20 +-- src/structures/Integration.js | 2 +- src/structures/Invite.js | 2 +- src/structures/Message.js | 12 +- src/structures/MessageMentions.js | 6 +- src/structures/MessageReaction.js | 18 +-- src/structures/Presence.js | 4 +- src/structures/ReactionCollector.js | 2 +- src/structures/Role.js | 2 +- src/structures/TextChannel.js | 8 +- src/structures/User.js | 10 +- src/structures/VoiceChannel.js | 2 +- src/structures/VoiceState.js | 4 +- src/structures/Webhook.js | 4 +- src/structures/interfaces/TextBasedChannel.js | 10 +- src/util/LimitedCollection.js | 29 ++++ src/util/Structures.js | 2 +- src/util/Util.js | 38 +---- typings/index.d.ts | 153 +++++++++--------- 87 files changed, 804 insertions(+), 705 deletions(-) rename src/{stores/DataStore.js => managers/BaseManager.js} (50%) rename src/{stores/ChannelStore.js => managers/ChannelManager.js} (52%) rename src/{stores/GuildChannelStore.js => managers/GuildChannelManager.js} (85%) rename src/{stores/GuildEmojiStore.js => managers/GuildEmojiManager.js} (89%) rename src/{stores/GuildEmojiRoleStore.js => managers/GuildEmojiRoleManager.js} (73%) rename src/{stores/GuildStore.js => managers/GuildManager.js} (85%) rename src/{stores/GuildMemberStore.js => managers/GuildMemberManager.js} (91%) rename src/{stores/GuildMemberRoleStore.js => managers/GuildMemberRoleManager.js} (77%) create mode 100644 src/managers/MessageManager.js rename src/{stores/PresenceStore.js => managers/PresenceManager.js} (73%) rename src/{stores/ReactionStore.js => managers/ReactionManager.js} (74%) rename src/{stores/ReactionUserStore.js => managers/ReactionUserManager.js} (77%) rename src/{stores/RoleStore.js => managers/RoleManager.js} (78%) rename src/{stores/UserStore.js => managers/UserManager.js} (80%) create mode 100644 src/managers/VoiceStateManager.js delete mode 100644 src/stores/MessageStore.js delete mode 100644 src/stores/VoiceStateStore.js create mode 100644 src/util/LimitedCollection.js diff --git a/docs/examples/greeting.js b/docs/examples/greeting.js index 8fc1dfada..314a75986 100644 --- a/docs/examples/greeting.js +++ b/docs/examples/greeting.js @@ -19,7 +19,7 @@ client.on('ready', () => { // Create an event listener for new guild members client.on('guildMemberAdd', member => { // Send the message to a designated channel on a server: - const channel = member.guild.channels.find(ch => ch.name === 'member-log'); + const channel = member.guild.channels.cache.find(ch => ch.name === 'member-log'); // Do nothing if the channel wasn't found on this server if (!channel) return; // Send the message, mentioning the member diff --git a/src/client/Client.js b/src/client/Client.js index a1ab6672e..ae7bbf8b3 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -11,10 +11,10 @@ const Webhook = require('../structures/Webhook'); const Invite = require('../structures/Invite'); const ClientApplication = require('../structures/ClientApplication'); const ShardClientUtil = require('../sharding/ShardClientUtil'); -const UserStore = require('../stores/UserStore'); -const ChannelStore = require('../stores/ChannelStore'); -const GuildStore = require('../stores/GuildStore'); -const GuildEmojiStore = require('../stores/GuildEmojiStore'); +const UserManager = require('../managers/UserManager'); +const ChannelManager = require('../managers/ChannelManager'); +const GuildManager = require('../managers/GuildManager'); +const GuildEmojiManager = require('../managers/GuildEmojiManager'); const { Events, browser, DefaultOptions } = require('../util/Constants'); const DataResolver = require('../util/DataResolver'); const Structures = require('../util/Structures'); @@ -99,25 +99,25 @@ class Client extends BaseClient { /** * All of the {@link User} objects that have been cached at any point, mapped by their IDs - * @type {UserStore} + * @type {UserManager} */ - this.users = new UserStore(this); + this.users = new UserManager(this); /** * All of the guilds the client is currently handling, mapped by their IDs - * as long as sharding isn't being used, this will be *every* guild the bot is a member of - * @type {GuildStore} + * @type {GuildManager} */ - this.guilds = new GuildStore(this); + this.guilds = new GuildManager(this); /** * All of the {@link Channel}s that the client is currently handling, mapped by their IDs - * as long as sharding isn't being used, this will be *every* channel in *every* guild the bot * is a member of. Note that DM channels will not be initially cached, and thus not be present - * in the store without their explicit fetching or use. - * @type {ChannelStore} + * in the Manager without their explicit fetching or use. + * @type {ChannelManager} */ - this.channels = new ChannelStore(this); + this.channels = new ChannelManager(this); const ClientPresence = Structures.get('ClientPresence'); /** @@ -159,13 +159,13 @@ class Client extends BaseClient { /** * All custom emojis that the client has access to, mapped by their IDs - * @type {GuildEmojiStore} + * @type {GuildEmojiManager} * @readonly */ get emojis() { - const emojis = new GuildEmojiStore({ client: this }); - for (const guild of this.guilds.values()) { - if (guild.available) for (const emoji of guild.emojis.values()) emojis.set(emoji.id, emoji); + const emojis = new GuildEmojiManager({ client: this }); + for (const guild of this.guilds.cache.values()) { + if (guild.available) for (const emoji of guild.emojis.cache.values()) emojis.cache.set(emoji.id, emoji); } return emojis; } @@ -298,11 +298,11 @@ class Client extends BaseClient { let channels = 0; let messages = 0; - for (const channel of this.channels.values()) { + for (const channel of this.channels.cache.values()) { if (!channel.messages) continue; channels++; - messages += channel.messages.sweep( + messages += channel.messages.cache.sweep( message => now - (message.editedTimestamp || message.createdTimestamp) > lifetimeMs ); } diff --git a/src/client/actions/Action.js b/src/client/actions/Action.js index ce4fd6db2..2a0bc861c 100644 --- a/src/client/actions/Action.js +++ b/src/client/actions/Action.js @@ -23,10 +23,10 @@ class GenericAction { return data; } - getPayload(data, store, id, partialType, cache) { - const existing = store.get(id); + getPayload(data, manager, id, partialType, cache) { + const existing = manager.cache.get(id); if (!existing && this.client.options.partials.includes(partialType)) { - return store.add(data, cache); + return manager.add(data, cache); } return existing; } diff --git a/src/client/actions/ChannelCreate.js b/src/client/actions/ChannelCreate.js index 6830f2ab5..fa60a0b39 100644 --- a/src/client/actions/ChannelCreate.js +++ b/src/client/actions/ChannelCreate.js @@ -6,7 +6,7 @@ const { Events } = require('../../util/Constants'); class ChannelCreateAction extends Action { handle(data) { const client = this.client; - const existing = client.channels.has(data.id); + const existing = client.channels.cache.has(data.id); const channel = client.channels.add(data); if (!existing && channel) { /** diff --git a/src/client/actions/ChannelDelete.js b/src/client/actions/ChannelDelete.js index fdfc38709..66fad20db 100644 --- a/src/client/actions/ChannelDelete.js +++ b/src/client/actions/ChannelDelete.js @@ -12,13 +12,13 @@ class ChannelDeleteAction extends Action { handle(data) { const client = this.client; - let channel = client.channels.get(data.id); + let channel = client.channels.cache.get(data.id); if (channel) { client.channels.remove(channel.id); channel.deleted = true; if (channel.messages && !(channel instanceof DMChannel)) { - for (const message of channel.messages.values()) { + for (const message of channel.messages.cache.values()) { message.deleted = true; } } diff --git a/src/client/actions/ChannelUpdate.js b/src/client/actions/ChannelUpdate.js index 7b716de07..06bb71b85 100644 --- a/src/client/actions/ChannelUpdate.js +++ b/src/client/actions/ChannelUpdate.js @@ -8,16 +8,16 @@ class ChannelUpdateAction extends Action { handle(data) { const client = this.client; - let channel = client.channels.get(data.id); + let channel = client.channels.cache.get(data.id); if (channel) { const old = channel._update(data); if (ChannelTypes[channel.type.toUpperCase()] !== data.type) { const newChannel = Channel.create(this.client, data, channel.guild); - for (const [id, message] of channel.messages) newChannel.messages.set(id, message); + for (const [id, message] of channel.messages.cache) newChannel.messages.cache.set(id, message); newChannel._typing = new Map(channel._typing); channel = newChannel; - this.client.channels.set(channel.id, channel); + this.client.channels.cache.set(channel.id, channel); } return { diff --git a/src/client/actions/GuildBanRemove.js b/src/client/actions/GuildBanRemove.js index 5a4c0a902..fc28e113f 100644 --- a/src/client/actions/GuildBanRemove.js +++ b/src/client/actions/GuildBanRemove.js @@ -6,7 +6,7 @@ const { Events } = require('../../util/Constants'); class GuildBanRemove extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); const user = client.users.add(data.user); /** * Emitted whenever a member is unbanned from a guild. diff --git a/src/client/actions/GuildChannelsPositionUpdate.js b/src/client/actions/GuildChannelsPositionUpdate.js index b21115947..a393167e6 100644 --- a/src/client/actions/GuildChannelsPositionUpdate.js +++ b/src/client/actions/GuildChannelsPositionUpdate.js @@ -6,10 +6,10 @@ class GuildChannelsPositionUpdate extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); if (guild) { for (const partialChannel of data.channels) { - const channel = guild.channels.get(partialChannel.id); + const channel = guild.channels.cache.get(partialChannel.id); if (channel) channel.rawPosition = partialChannel.position; } } diff --git a/src/client/actions/GuildDelete.js b/src/client/actions/GuildDelete.js index 193abc47c..69cd91438 100644 --- a/src/client/actions/GuildDelete.js +++ b/src/client/actions/GuildDelete.js @@ -12,9 +12,9 @@ class GuildDeleteAction extends Action { handle(data) { const client = this.client; - let guild = client.guilds.get(data.id); + let guild = client.guilds.cache.get(data.id); if (guild) { - for (const channel of guild.channels.values()) { + for (const channel of guild.channels.cache.values()) { if (channel.type === 'text') channel.stopTyping(true); } @@ -36,11 +36,11 @@ class GuildDeleteAction extends Action { }; } - for (const channel of guild.channels.values()) this.client.channels.remove(channel.id); + for (const channel of guild.channels.cache.values()) this.client.channels.remove(channel.id); if (guild.voice && guild.voice.connection) guild.voice.connection.disconnect(); // Delete guild - client.guilds.remove(guild.id); + client.guilds.cache.delete(guild.id); guild.deleted = true; /** diff --git a/src/client/actions/GuildEmojiDelete.js b/src/client/actions/GuildEmojiDelete.js index d5c973afa..42af70c12 100644 --- a/src/client/actions/GuildEmojiDelete.js +++ b/src/client/actions/GuildEmojiDelete.js @@ -5,7 +5,7 @@ const { Events } = require('../../util/Constants'); class GuildEmojiDeleteAction extends Action { handle(emoji) { - emoji.guild.emojis.remove(emoji.id); + emoji.guild.emojis.cache.delete(emoji.id); emoji.deleted = true; /** * Emitted whenever a custom emoji is deleted in a guild. diff --git a/src/client/actions/GuildEmojisUpdate.js b/src/client/actions/GuildEmojisUpdate.js index d6902d535..77119333d 100644 --- a/src/client/actions/GuildEmojisUpdate.js +++ b/src/client/actions/GuildEmojisUpdate.js @@ -4,14 +4,14 @@ const Action = require('./Action'); class GuildEmojisUpdateAction extends Action { handle(data) { - const guild = this.client.guilds.get(data.guild_id); + const guild = this.client.guilds.cache.get(data.guild_id); if (!guild || !guild.emojis) return; - const deletions = new Map(guild.emojis); + const deletions = new Map(guild.emojis.cache); for (const emoji of data.emojis) { // Determine type of emoji event - const cachedEmoji = guild.emojis.get(emoji.id); + const cachedEmoji = guild.emojis.cache.get(emoji.id); if (cachedEmoji) { deletions.delete(emoji.id); if (!cachedEmoji.equals(emoji)) { diff --git a/src/client/actions/GuildIntegrationsUpdate.js b/src/client/actions/GuildIntegrationsUpdate.js index a8e910774..4129be6ad 100644 --- a/src/client/actions/GuildIntegrationsUpdate.js +++ b/src/client/actions/GuildIntegrationsUpdate.js @@ -6,7 +6,7 @@ const { Events } = require('../../util/Constants'); class GuildIntegrationsUpdate extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); /** * Emitted whenever a guild integration is updated * @event Client#guildIntegrationsUpdate diff --git a/src/client/actions/GuildMemberRemove.js b/src/client/actions/GuildMemberRemove.js index 28aa50386..ed6c0d144 100644 --- a/src/client/actions/GuildMemberRemove.js +++ b/src/client/actions/GuildMemberRemove.js @@ -6,14 +6,14 @@ const { Events, Status } = require('../../util/Constants'); class GuildMemberRemoveAction extends Action { handle(data, shard) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); let member = null; if (guild) { member = this.getMember(data, guild); guild.memberCount--; if (member) { member.deleted = true; - guild.members.remove(member.id); + guild.members.cache.delete(member.id); /** * Emitted whenever a member leaves a guild, or is kicked. * @event Client#guildMemberRemove @@ -21,7 +21,7 @@ class GuildMemberRemoveAction extends Action { */ if (shard.status === Status.READY) client.emit(Events.GUILD_MEMBER_REMOVE, member); } - guild.voiceStates.delete(data.user.id); + guild.voiceStates.cache.delete(data.user.id); } return { guild, member }; } diff --git a/src/client/actions/GuildRoleCreate.js b/src/client/actions/GuildRoleCreate.js index 36111f064..65c64f36a 100644 --- a/src/client/actions/GuildRoleCreate.js +++ b/src/client/actions/GuildRoleCreate.js @@ -6,10 +6,10 @@ const { Events } = require('../../util/Constants'); class GuildRoleCreate extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); let role; if (guild) { - const already = guild.roles.has(data.role.id); + const already = guild.roles.cache.has(data.role.id); role = guild.roles.add(data.role); /** * Emitted whenever a role is created. diff --git a/src/client/actions/GuildRoleDelete.js b/src/client/actions/GuildRoleDelete.js index 31b13b812..ec9df8233 100644 --- a/src/client/actions/GuildRoleDelete.js +++ b/src/client/actions/GuildRoleDelete.js @@ -6,13 +6,13 @@ const { Events } = require('../../util/Constants'); class GuildRoleDeleteAction extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); let role; if (guild) { - role = guild.roles.get(data.role_id); + role = guild.roles.cache.get(data.role_id); if (role) { - guild.roles.remove(data.role_id); + guild.roles.cache.delete(data.role_id); role.deleted = true; /** * Emitted whenever a guild role is deleted. diff --git a/src/client/actions/GuildRoleUpdate.js b/src/client/actions/GuildRoleUpdate.js index bf61c7878..631746d2a 100644 --- a/src/client/actions/GuildRoleUpdate.js +++ b/src/client/actions/GuildRoleUpdate.js @@ -6,12 +6,12 @@ const { Events } = require('../../util/Constants'); class GuildRoleUpdateAction extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); if (guild) { let old = null; - const role = guild.roles.get(data.role.id); + const role = guild.roles.cache.get(data.role.id); if (role) { old = role._update(data.role); /** diff --git a/src/client/actions/GuildRolesPositionUpdate.js b/src/client/actions/GuildRolesPositionUpdate.js index f09f11436..d7abca97b 100644 --- a/src/client/actions/GuildRolesPositionUpdate.js +++ b/src/client/actions/GuildRolesPositionUpdate.js @@ -6,10 +6,10 @@ class GuildRolesPositionUpdate extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); if (guild) { for (const partialRole of data.roles) { - const role = guild.roles.get(partialRole.id); + const role = guild.roles.cache.get(partialRole.id); if (role) role.rawPosition = partialRole.position; } } diff --git a/src/client/actions/GuildUpdate.js b/src/client/actions/GuildUpdate.js index 6d7cf9b4e..c40fc0cde 100644 --- a/src/client/actions/GuildUpdate.js +++ b/src/client/actions/GuildUpdate.js @@ -7,7 +7,7 @@ class GuildUpdateAction extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.id); + const guild = client.guilds.cache.get(data.id); if (guild) { const old = guild._update(data); /** diff --git a/src/client/actions/InviteCreate.js b/src/client/actions/InviteCreate.js index 5552ea2f0..638133154 100644 --- a/src/client/actions/InviteCreate.js +++ b/src/client/actions/InviteCreate.js @@ -7,8 +7,8 @@ const { Events } = require('../../util/Constants'); class InviteCreateAction extends Action { handle(data) { const client = this.client; - const channel = client.channels.get(data.channel_id); - const guild = client.guilds.get(data.guild_id); + const channel = client.channels.cache.get(data.channel_id); + const guild = client.guilds.cache.get(data.guild_id); if (!channel && !guild) return false; const inviteData = Object.assign(data, { channel, guild }); diff --git a/src/client/actions/InviteDelete.js b/src/client/actions/InviteDelete.js index 83933d34f..92692c3e2 100644 --- a/src/client/actions/InviteDelete.js +++ b/src/client/actions/InviteDelete.js @@ -7,8 +7,8 @@ const { Events } = require('../../util/Constants'); class InviteDeleteAction extends Action { handle(data) { const client = this.client; - const channel = client.channels.get(data.channel_id); - const guild = client.guilds.get(data.guild_id); + const channel = client.channels.cache.get(data.channel_id); + const guild = client.guilds.cache.get(data.guild_id); if (!channel && !guild) return false; const inviteData = Object.assign(data, { channel, guild }); diff --git a/src/client/actions/MessageCreate.js b/src/client/actions/MessageCreate.js index 3772c4a10..ddb56ea97 100644 --- a/src/client/actions/MessageCreate.js +++ b/src/client/actions/MessageCreate.js @@ -6,9 +6,9 @@ const { Events } = require('../../util/Constants'); class MessageCreateAction extends Action { handle(data) { const client = this.client; - const channel = client.channels.get(data.channel_id); + const channel = client.channels.cache.get(data.channel_id); if (channel) { - const existing = channel.messages.get(data.id); + const existing = channel.messages.cache.get(data.id); if (existing) return { message: existing }; const message = channel.messages.add(data); const user = message.author; diff --git a/src/client/actions/MessageDelete.js b/src/client/actions/MessageDelete.js index feb118c10..7869365c9 100644 --- a/src/client/actions/MessageDelete.js +++ b/src/client/actions/MessageDelete.js @@ -11,7 +11,7 @@ class MessageDeleteAction extends Action { if (channel) { message = this.getMessage(data, channel); if (message) { - channel.messages.delete(message.id); + channel.messages.cache.delete(message.id); message.deleted = true; /** * Emitted whenever a message is deleted. diff --git a/src/client/actions/MessageDeleteBulk.js b/src/client/actions/MessageDeleteBulk.js index f80bc7c4d..00fd54b51 100644 --- a/src/client/actions/MessageDeleteBulk.js +++ b/src/client/actions/MessageDeleteBulk.js @@ -7,7 +7,7 @@ const { Events } = require('../../util/Constants'); class MessageDeleteBulkAction extends Action { handle(data) { const client = this.client; - const channel = client.channels.get(data.channel_id); + const channel = client.channels.cache.get(data.channel_id); if (channel) { const ids = data.ids; @@ -20,7 +20,7 @@ class MessageDeleteBulkAction extends Action { if (message) { message.deleted = true; messages.set(message.id, message); - channel.messages.delete(id); + channel.messages.cache.delete(id); } } diff --git a/src/client/actions/MessageReactionRemoveAll.js b/src/client/actions/MessageReactionRemoveAll.js index 0921ce50e..14b79bf08 100644 --- a/src/client/actions/MessageReactionRemoveAll.js +++ b/src/client/actions/MessageReactionRemoveAll.js @@ -13,7 +13,7 @@ class MessageReactionRemoveAll extends Action { const message = this.getMessage(data, channel); if (!message) return false; - message.reactions.clear(); + message.reactions.cache.clear(); this.client.emit(Events.MESSAGE_REACTION_REMOVE_ALL, message); return { message }; diff --git a/src/client/actions/MessageReactionRemoveEmoji.js b/src/client/actions/MessageReactionRemoveEmoji.js index ab0eaa770..143702c00 100644 --- a/src/client/actions/MessageReactionRemoveEmoji.js +++ b/src/client/actions/MessageReactionRemoveEmoji.js @@ -13,7 +13,7 @@ class MessageReactionRemoveEmoji extends Action { const reaction = this.getReaction(data, message); if (!reaction) return false; - if (!message.partial) message.reactions.delete(reaction.emoji.id || reaction.emoji.name); + if (!message.partial) message.reactions.cache.delete(reaction.emoji.id || reaction.emoji.name); /** * Emitted when a bot removes an emoji reaction from a cached message. diff --git a/src/client/actions/PresenceUpdate.js b/src/client/actions/PresenceUpdate.js index 538789e25..f74fbeb5d 100644 --- a/src/client/actions/PresenceUpdate.js +++ b/src/client/actions/PresenceUpdate.js @@ -5,7 +5,7 @@ const { Events } = require('../../util/Constants'); class PresenceUpdateAction extends Action { handle(data) { - let user = this.client.users.get(data.user.id); + let user = this.client.users.cache.get(data.user.id); if (!user && data.user.username) user = this.client.users.add(data.user); if (!user) return; @@ -13,12 +13,12 @@ class PresenceUpdateAction extends Action { if (!user.equals(data.user)) this.client.actions.UserUpdate.handle(data.user); } - const guild = this.client.guilds.get(data.guild_id); + const guild = this.client.guilds.cache.get(data.guild_id); if (!guild) return; - let oldPresence = guild.presences.get(user.id); + let oldPresence = guild.presences.cache.get(user.id); if (oldPresence) oldPresence = oldPresence._clone(); - let member = guild.members.get(user.id); + let member = guild.members.cache.get(user.id); if (!member && data.status !== 'offline') { member = guild.members.add({ user, diff --git a/src/client/actions/UserUpdate.js b/src/client/actions/UserUpdate.js index a762511f4..7279ca7dd 100644 --- a/src/client/actions/UserUpdate.js +++ b/src/client/actions/UserUpdate.js @@ -7,7 +7,7 @@ class UserUpdateAction extends Action { handle(data) { const client = this.client; - const newUser = client.users.get(data.id); + const newUser = client.users.cache.get(data.id); const oldUser = newUser._update(data); if (!oldUser.equals(newUser)) { diff --git a/src/client/actions/VoiceStateUpdate.js b/src/client/actions/VoiceStateUpdate.js index 386bf7783..b2a4b11bd 100644 --- a/src/client/actions/VoiceStateUpdate.js +++ b/src/client/actions/VoiceStateUpdate.js @@ -7,17 +7,17 @@ const VoiceState = require('../../structures/VoiceState'); class VoiceStateUpdate extends Action { handle(data) { const client = this.client; - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); if (guild) { // Update the state - const oldState = guild.voiceStates.has(data.user_id) ? - guild.voiceStates.get(data.user_id)._clone() : + const oldState = guild.voiceStates.cache.has(data.user_id) ? + guild.voiceStates.cache.get(data.user_id)._clone() : new VoiceState(guild, { user_id: data.user_id }); const newState = guild.voiceStates.add(data); // Get the member - let member = guild.members.get(data.user_id); + let member = guild.members.cache.get(data.user_id); if (member && data.member) { member._patch(data.member); } else if (data.member && data.member.user && data.member.joined_at) { diff --git a/src/client/actions/WebhooksUpdate.js b/src/client/actions/WebhooksUpdate.js index 69e28aecb..f6a515b2c 100644 --- a/src/client/actions/WebhooksUpdate.js +++ b/src/client/actions/WebhooksUpdate.js @@ -6,7 +6,7 @@ const { Events } = require('../../util/Constants'); class WebhooksUpdate extends Action { handle(data) { const client = this.client; - const channel = client.channels.get(data.channel_id); + const channel = client.channels.cache.get(data.channel_id); /** * Emitted whenever a guild text channel has its webhooks changed. * @event Client#webhookUpdate diff --git a/src/client/voice/ClientVoiceManager.js b/src/client/voice/ClientVoiceManager.js index cf7526623..fbf5d1ba1 100644 --- a/src/client/voice/ClientVoiceManager.js +++ b/src/client/voice/ClientVoiceManager.js @@ -56,7 +56,7 @@ class ClientVoiceManager { this.connections.delete(guild_id); return; } - connection.channel = this.client.channels.get(channel_id); + connection.channel = this.client.channels.cache.get(channel_id); connection.setSessionID(session_id); } diff --git a/src/client/voice/VoiceConnection.js b/src/client/voice/VoiceConnection.js index b4b1e9da1..050775b60 100644 --- a/src/client/voice/VoiceConnection.js +++ b/src/client/voice/VoiceConnection.js @@ -483,7 +483,7 @@ class VoiceConnection extends EventEmitter { onSpeaking({ user_id, speaking }) { speaking = new Speaking(speaking).freeze(); const guild = this.channel.guild; - const user = this.client.users.get(user_id); + const user = this.client.users.cache.get(user_id); const old = this._speaking.get(user_id); this._speaking.set(user_id, speaking); /** diff --git a/src/client/websocket/WebSocketManager.js b/src/client/websocket/WebSocketManager.js index 3e299c58d..02139f484 100644 --- a/src/client/websocket/WebSocketManager.js +++ b/src/client/websocket/WebSocketManager.js @@ -408,7 +408,7 @@ class WebSocketManager extends EventEmitter { if (this.client.options.fetchAllMembers) { try { - const promises = this.client.guilds.map(guild => { + const promises = this.client.guilds.cache.map(guild => { if (guild.available) return guild.members.fetch(); // Return empty promise if guild is unavailable return Promise.resolve(); diff --git a/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js b/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js index 6da643ff3..13e6f0faf 100644 --- a/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js +++ b/src/client/websocket/handlers/CHANNEL_PINS_UPDATE.js @@ -3,7 +3,7 @@ const { Events } = require('../../../util/Constants'); module.exports = (client, { d: data }) => { - const channel = client.channels.get(data.channel_id); + const channel = client.channels.cache.get(data.channel_id); const time = new Date(data.last_pin_timestamp); if (channel && !Number.isNaN(time.getTime())) { diff --git a/src/client/websocket/handlers/GUILD_BAN_ADD.js b/src/client/websocket/handlers/GUILD_BAN_ADD.js index cbb60e13c..5d4a0965c 100644 --- a/src/client/websocket/handlers/GUILD_BAN_ADD.js +++ b/src/client/websocket/handlers/GUILD_BAN_ADD.js @@ -3,7 +3,7 @@ const { Events } = require('../../../util/Constants'); module.exports = (client, { d: data }) => { - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); const user = client.users.add(data.user); /** diff --git a/src/client/websocket/handlers/GUILD_CREATE.js b/src/client/websocket/handlers/GUILD_CREATE.js index 33cc0c239..eb8974435 100644 --- a/src/client/websocket/handlers/GUILD_CREATE.js +++ b/src/client/websocket/handlers/GUILD_CREATE.js @@ -3,7 +3,7 @@ const { Events, Status } = require('../../../util/Constants'); module.exports = async (client, { d: data }, shard) => { - let guild = client.guilds.get(data.id); + let guild = client.guilds.cache.get(data.id); if (guild) { if (!guild.available && !data.unavailable) { // A newly available guild diff --git a/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js b/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js index 0738eaa67..9a0a880a7 100644 --- a/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js +++ b/src/client/websocket/handlers/GUILD_MEMBERS_CHUNK.js @@ -4,7 +4,7 @@ const { Events } = require('../../../util/Constants'); const Collection = require('../../../util/Collection'); module.exports = (client, { d: data }) => { - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); if (!guild) return; const members = new Collection(); diff --git a/src/client/websocket/handlers/GUILD_MEMBER_ADD.js b/src/client/websocket/handlers/GUILD_MEMBER_ADD.js index 796b6c376..964e3da26 100644 --- a/src/client/websocket/handlers/GUILD_MEMBER_ADD.js +++ b/src/client/websocket/handlers/GUILD_MEMBER_ADD.js @@ -3,7 +3,7 @@ const { Events, Status } = require('../../../util/Constants'); module.exports = (client, { d: data }, shard) => { - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); if (guild) { guild.memberCount++; const member = guild.members.add(data); diff --git a/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js b/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js index 9341329a3..92c9da6c5 100644 --- a/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js +++ b/src/client/websocket/handlers/GUILD_MEMBER_UPDATE.js @@ -3,9 +3,9 @@ const { Status, Events } = require('../../../util/Constants'); module.exports = (client, { d: data }, shard) => { - const guild = client.guilds.get(data.guild_id); + const guild = client.guilds.cache.get(data.guild_id); if (guild) { - const member = guild.members.get(data.user.id); + const member = guild.members.cache.get(data.user.id); if (member) { const old = member._update(data); if (shard.status === Status.READY) { diff --git a/src/client/websocket/handlers/READY.js b/src/client/websocket/handlers/READY.js index 002575023..c38b681c4 100644 --- a/src/client/websocket/handlers/READY.js +++ b/src/client/websocket/handlers/READY.js @@ -9,7 +9,7 @@ module.exports = (client, { d: data }, shard) => { if (!ClientUser) ClientUser = require('../../../structures/ClientUser'); const clientUser = new ClientUser(client, data.user); client.user = clientUser; - client.users.set(clientUser.id, clientUser); + client.users.cache.set(clientUser.id, clientUser); } for (const guild of data.guilds) { diff --git a/src/client/websocket/handlers/TYPING_START.js b/src/client/websocket/handlers/TYPING_START.js index 9df76dc75..92e0125f2 100644 --- a/src/client/websocket/handlers/TYPING_START.js +++ b/src/client/websocket/handlers/TYPING_START.js @@ -3,8 +3,8 @@ const { Events } = require('../../../util/Constants'); module.exports = (client, { d: data }) => { - const channel = client.channels.get(data.channel_id); - const user = client.users.get(data.user_id); + const channel = client.channels.cache.get(data.channel_id); + const user = client.users.cache.get(data.user_id); if (channel && user) { /** diff --git a/src/index.js b/src/index.js index 896919c40..155d2f539 100644 --- a/src/index.js +++ b/src/index.js @@ -17,7 +17,8 @@ module.exports = { Collection: require('./util/Collection'), Constants: require('./util/Constants'), DataResolver: require('./util/DataResolver'), - DataStore: require('./stores/DataStore'), + LimitedCollection: require('./util/LimitedCollection'), + BaseManager: require('./managers/BaseManager'), DiscordAPIError: require('./rest/DiscordAPIError'), HTTPError: require('./rest/HTTPError'), MessageFlags: require('./util/MessageFlags'), @@ -30,19 +31,19 @@ module.exports = { Util: Util, version: require('../package.json').version, - // Stores - ChannelStore: require('./stores/ChannelStore'), - GuildChannelStore: require('./stores/GuildChannelStore'), - GuildEmojiStore: require('./stores/GuildEmojiStore'), - GuildEmojiRoleStore: require('./stores/GuildEmojiRoleStore'), - GuildMemberStore: require('./stores/GuildMemberStore'), - GuildMemberRoleStore: require('./stores/GuildMemberRoleStore'), - GuildStore: require('./stores/GuildStore'), - ReactionUserStore: require('./stores/ReactionUserStore'), - MessageStore: require('./stores/MessageStore'), - PresenceStore: require('./stores/PresenceStore'), - RoleStore: require('./stores/RoleStore'), - UserStore: require('./stores/UserStore'), + // Managers + ChannelManager: require('./managers/ChannelManager'), + GuildChannelManager: require('./managers/GuildChannelManager'), + GuildEmojiManager: require('./managers/GuildEmojiManager'), + GuildEmojiRoleManager: require('./managers/GuildEmojiRoleManager'), + GuildMemberManager: require('./managers/GuildMemberManager'), + GuildMemberRoleManager: require('./managers/GuildMemberRoleManager'), + GuildManager: require('./managers/GuildManager'), + ReactionUserManager: require('./managers/ReactionUserManager'), + MessageManager: require('./managers/MessageManager'), + PresenceManager: require('./managers/PresenceManager'), + RoleManager: require('./managers/RoleManager'), + UserManager: require('./managers/UserManager'), // Shortcuts to Util methods discordSort: Util.discordSort, diff --git a/src/stores/DataStore.js b/src/managers/BaseManager.js similarity index 50% rename from src/stores/DataStore.js rename to src/managers/BaseManager.js index be1e93d79..b9d0be938 100644 --- a/src/stores/DataStore.js +++ b/src/managers/BaseManager.js @@ -4,44 +4,67 @@ const Collection = require('../util/Collection'); let Structures; /** - * Manages the creation, retrieval and deletion of a specific data model. - * @extends {Collection} + * Manages the API methods of a data model and holds its cache. + * @abstract */ -class DataStore extends Collection { - constructor(client, iterable, holds) { - super(); +class BaseManager { + constructor(client, iterable, holds, cacheType = Collection, ...cacheOptions) { if (!Structures) Structures = require('../util/Structures'); - Object.defineProperty(this, 'client', { value: client }); + /** + * The data structure belonging to this manager + * @name BaseManager#holds + * @type {Function} + * @private + * @readonly + */ Object.defineProperty(this, 'holds', { value: Structures.get(holds.name) || holds }); - if (iterable) for (const item of iterable) this.add(item); + + /** + * The client that instantiated this Manager + * @name BaseManager#client + * @type {Client} + * @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.get(id || data.id); + const existing = this.cache.get(id || data.id); if (existing && existing._patch && cache) existing._patch(data); if (existing) return existing; const entry = this.holds ? new this.holds(this.client, data, ...extras) : data; - if (cache) this.set(id || entry.id, entry); + if (cache) this.cache.set(id || entry.id, entry); return entry; } - remove(key) { return this.delete(key); } - /** * Resolves a data entry to a data Object. - * @param {string|Object} idOrInstance The id or instance of something in this DataStore - * @returns {?Object} An instance from this DataStore + * @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.get(idOrInstance) || null; + if (typeof idOrInstance === 'string') return this.cache.get(idOrInstance) || null; return null; } /** * Resolves a data entry to a instance ID. - * @param {string|Instance} idOrInstance The id or instance of something in this DataStore + * @param {string|Instance} idOrInstance The id or instance of something in this Manager * @returns {?Snowflake} */ resolveID(idOrInstance) { @@ -49,10 +72,6 @@ class DataStore extends Collection { if (typeof idOrInstance === 'string') return idOrInstance; return null; } - - static get [Symbol.species]() { - return Collection; - } } -module.exports = DataStore; +module.exports = BaseManager; diff --git a/src/stores/ChannelStore.js b/src/managers/ChannelManager.js similarity index 52% rename from src/stores/ChannelStore.js rename to src/managers/ChannelManager.js index 762f0e193..dcb098c13 100644 --- a/src/stores/ChannelStore.js +++ b/src/managers/ChannelManager.js @@ -1,59 +1,26 @@ 'use strict'; -const DataStore = require('./DataStore'); const Channel = require('../structures/Channel'); +const BaseManager = require('./BaseManager'); const { Events } = require('../util/Constants'); -const kLru = Symbol('LRU'); -const lruable = ['dm']; - /** - * Stores channels. - * @extends {DataStore} + * A manager of channels belonging to a client */ -class ChannelStore extends DataStore { - constructor(client, iterableOrOptions = {}, options) { - if (!options && typeof iterableOrOptions[Symbol.iterator] !== 'function') { - options = iterableOrOptions; - iterableOrOptions = undefined; - } - super(client, iterableOrOptions, Channel); - - if (options.lru) { - const lru = this[kLru] = []; - lru.add = item => { - lru.remove(item); - lru.unshift(item); - while (lru.length > options.lru) this.remove(lru[lru.length - 1]); - }; - lru.remove = item => { - const index = lru.indexOf(item); - if (index > -1) lru.splice(index, 1); - }; - } +class ChannelManager extends BaseManager { + constructor(client, iterable) { + super(client, iterable, Channel); } - get(key, peek = false) { - const item = super.get(key); - if (!item || !lruable.includes(item.type)) return item; - if (!peek && this[kLru]) this[kLru].add(key); - return item; - } - - set(key, val) { - if (this[kLru] && lruable.includes(val.type)) this[kLru].add(key); - return super.set(key, val); - } - - delete(key) { - const item = this.get(key, true); - if (!item) return false; - if (this[kLru] && lruable.includes(item.type)) this[kLru].remove(key); - return super.delete(key); - } + /** + * The cache of Channels + * @property {Collection} cache + * @memberof ChannelManager + * @instance + */ add(data, guild, cache = true) { - const existing = this.get(data.id); + const existing = this.cache.get(data.id); if (existing) { if (existing._patch && cache) existing._patch(data); if (guild) guild.channels.add(existing); @@ -67,15 +34,15 @@ class ChannelStore extends DataStore { return null; } - if (cache) this.set(channel.id, channel); + if (cache) this.cache.set(channel.id, channel); return channel; } remove(id) { - const channel = this.get(id); - if (channel.guild) channel.guild.channels.remove(id); - super.remove(id); + const channel = this.cache.get(id); + if (channel.guild) channel.guild.channels.cache.delete(id); + this.cache.delete(id); } /** @@ -88,7 +55,7 @@ class ChannelStore extends DataStore { /** * Resolves a ChannelResolvable to a Channel object. * @method resolve - * @memberof ChannelStore + * @memberof ChannelManager * @instance * @param {ChannelResolvable} channel The channel resolvable to resolve * @returns {?Channel} @@ -97,7 +64,7 @@ class ChannelStore extends DataStore { /** * Resolves a ChannelResolvable to a channel ID string. * @method resolveID - * @memberof ChannelStore + * @memberof ChannelManager * @instance * @param {ChannelResolvable} channel The channel resolvable to resolve * @returns {?Snowflake} @@ -115,7 +82,7 @@ class ChannelStore extends DataStore { * .catch(console.error); */ async fetch(id, cache = true) { - const existing = this.get(id); + const existing = this.cache.get(id); if (existing && !existing.partial) return existing; const data = await this.client.api.channels(id).get(); @@ -123,4 +90,4 @@ class ChannelStore extends DataStore { } } -module.exports = ChannelStore; +module.exports = ChannelManager; diff --git a/src/stores/GuildChannelStore.js b/src/managers/GuildChannelManager.js similarity index 85% rename from src/stores/GuildChannelStore.js rename to src/managers/GuildChannelManager.js index 552b40f00..147a0b207 100644 --- a/src/stores/GuildChannelStore.js +++ b/src/managers/GuildChannelManager.js @@ -1,24 +1,36 @@ 'use strict'; const { ChannelTypes } = require('../util/Constants'); -const DataStore = require('./DataStore'); +const BaseManager = require('./BaseManager'); const GuildChannel = require('../structures/GuildChannel'); const PermissionOverwrites = require('../structures/PermissionOverwrites'); /** - * Stores guild channels. - * @extends {DataStore} + * Manages API methods for GuildChannels and stores their cache. + * @extends {BaseManager} */ -class GuildChannelStore extends DataStore { +class GuildChannelManager extends BaseManager { constructor(guild, iterable) { super(guild.client, iterable, GuildChannel); + + /** + * The guild this Manager belongs to + * @type {Guild} + */ this.guild = guild; } + /** + * The cache of this Manager + * @property {Collection} cache + * @memberof GuildChannelManager + * @instance + */ + add(channel) { - const existing = this.get(channel.id); + const existing = this.cache.get(channel.id); if (existing) return existing; - this.set(channel.id, channel); + this.cache.set(channel.id, channel); return channel; } @@ -32,7 +44,7 @@ class GuildChannelStore extends DataStore { /** * Resolves a GuildChannelResolvable to a Channel object. * @method resolve - * @memberof GuildChannelStore + * @memberof GuildChannelManager * @instance * @param {GuildChannelResolvable} channel The GuildChannel resolvable to resolve * @returns {?Channel} @@ -41,7 +53,7 @@ class GuildChannelStore extends DataStore { /** * Resolves a GuildChannelResolvable to a channel ID string. * @method resolveID - * @memberof GuildChannelStore + * @memberof GuildChannelManager * @instance * @param {GuildChannelResolvable} channel The GuildChannel resolvable to resolve * @returns {?Snowflake} @@ -117,4 +129,4 @@ class GuildChannelStore extends DataStore { } } -module.exports = GuildChannelStore; +module.exports = GuildChannelManager; diff --git a/src/stores/GuildEmojiStore.js b/src/managers/GuildEmojiManager.js similarity index 89% rename from src/stores/GuildEmojiStore.js rename to src/managers/GuildEmojiManager.js index b992a68c9..27ac363ca 100644 --- a/src/stores/GuildEmojiStore.js +++ b/src/managers/GuildEmojiManager.js @@ -1,22 +1,33 @@ 'use strict'; const Collection = require('../util/Collection'); -const DataStore = require('./DataStore'); +const BaseManager = require('./BaseManager'); const GuildEmoji = require('../structures/GuildEmoji'); const ReactionEmoji = require('../structures/ReactionEmoji'); const DataResolver = require('../util/DataResolver'); const { TypeError } = require('../errors'); /** - * Stores guild emojis. - * @extends {DataStore} + * Manages API methods for GuildEmojis and stores their cache. + * @extends {BaseManager} */ -class GuildEmojiStore extends DataStore { +class GuildEmojiManager extends BaseManager { constructor(guild, iterable) { super(guild.client, iterable, GuildEmoji); + /** + * The guild this manager belongs to + * @type {Guild} + */ this.guild = guild; } + /** + * The cache of GuildEmojis + * @property {Collection} cache + * @memberof GuildEmojiManager + * @instance + */ + add(data, cache) { return super.add(data, cache, { extras: [this.guild] }); } @@ -114,4 +125,4 @@ class GuildEmojiStore extends DataStore { } } -module.exports = GuildEmojiStore; +module.exports = GuildEmojiManager; diff --git a/src/stores/GuildEmojiRoleStore.js b/src/managers/GuildEmojiRoleManager.js similarity index 73% rename from src/stores/GuildEmojiRoleStore.js rename to src/managers/GuildEmojiRoleManager.js index 6db2003ed..577595588 100644 --- a/src/stores/GuildEmojiRoleStore.js +++ b/src/managers/GuildEmojiRoleManager.js @@ -1,18 +1,28 @@ 'use strict'; const Collection = require('../util/Collection'); -const Util = require('../util/Util'); const { TypeError } = require('../errors'); /** - * Stores emoji roles - * @extends {Collection} + * Manages API methods for roles belonging to emojis and stores their cache. */ -class GuildEmojiRoleStore extends Collection { +class GuildEmojiRoleManager { constructor(emoji) { - super(); + /** + * The emoji belonging to this manager + * @type {GuildEmoji} + */ this.emoji = emoji; + /** + * The guild belonging to this manager + * @type {Guild} + */ this.guild = emoji.guild; + /** + * The client belonging to this manager + * @type {Client} + * @readonly + */ Object.defineProperty(this, 'client', { value: emoji.client }); } @@ -22,8 +32,17 @@ class GuildEmojiRoleStore extends Collection { * @private * @readonly */ - get _filtered() { - return this.guild.roles.filter(role => this.emoji._roles.includes(role.id)); + get _roles() { + return this.guild.roles.cache.filter(role => this.emoji._roles.includes(role.id)); + } + + /** + * The cache of roles belonging to this emoji + * @type {Collection} + * @readonly + */ + get cache() { + return this._roles; } /** @@ -41,7 +60,7 @@ class GuildEmojiRoleStore extends Collection { 'Array or Collection of Roles or Snowflakes', true)); } - const newRoles = [...new Set(roleOrRoles.concat(...this.values()))]; + const newRoles = [...new Set(roleOrRoles.concat(...this._roles.values()))]; return this.set(newRoles); } @@ -60,7 +79,7 @@ class GuildEmojiRoleStore extends Collection { 'Array or Collection of Roles or Snowflakes', true)); } - const newRoles = this.keyArray().filter(role => !roleOrRoles.includes(role)); + const newRoles = this._roles.keyArray().filter(role => !roleOrRoles.includes(role)); return this.set(newRoles); } @@ -85,32 +104,18 @@ class GuildEmojiRoleStore extends Collection { clone() { const clone = new this.constructor(this.emoji); - clone._patch(this.keyArray().slice()); + clone._patch(this._roles.keyArray().slice()); return clone; } /** - * Patches the roles for this store + * Patches the roles for this manager's cache * @param {Snowflake[]} roles The new roles * @private */ _patch(roles) { this.emoji._roles = roles; } - - *[Symbol.iterator]() { - yield* this._filtered.entries(); - } - - valueOf() { - return this._filtered; - } - - static get [Symbol.species]() { - return Collection; - } } -Util.mixin(GuildEmojiRoleStore, ['set']); - -module.exports = GuildEmojiRoleStore; +module.exports = GuildEmojiRoleManager; diff --git a/src/stores/GuildStore.js b/src/managers/GuildManager.js similarity index 85% rename from src/stores/GuildStore.js rename to src/managers/GuildManager.js index eb7090b63..5666156b5 100644 --- a/src/stores/GuildStore.js +++ b/src/managers/GuildManager.js @@ -1,6 +1,6 @@ 'use strict'; -const DataStore = require('./DataStore'); +const BaseManager = require('./BaseManager'); const DataResolver = require('../util/DataResolver'); const { Events } = require('../util/Constants'); const Guild = require('../structures/Guild'); @@ -9,14 +9,21 @@ const GuildMember = require('../structures/GuildMember'); const Role = require('../structures/Role'); /** - * Stores guilds. - * @extends {DataStore} + * Manages API methods for Guilds and stores their cache. + * @extends {BaseManager} */ -class GuildStore extends DataStore { +class GuildManager extends BaseManager { constructor(client, iterable) { super(client, iterable, Guild); } + /** + * The cache of this Manager + * @property {Collection} cache + * @memberof GuildManager + * @instance + */ + /** * Data that resolves to give a Guild object. This can be: * * A Guild object @@ -29,7 +36,7 @@ class GuildStore extends DataStore { /** * Resolves a GuildResolvable to a Guild object. * @method resolve - * @memberof GuildStore + * @memberof GuildManager * @instance * @param {GuildResolvable} guild The guild resolvable to identify * @returns {?Guild} @@ -44,7 +51,7 @@ class GuildStore extends DataStore { /** * Resolves a GuildResolvable to a Guild ID string. * @method resolveID - * @memberof GuildStore + * @memberof GuildManager * @instance * @param {GuildResolvable} guild The guild resolvable to identify * @returns {?Snowflake} @@ -70,7 +77,7 @@ class GuildStore extends DataStore { return new Promise((resolve, reject) => this.client.api.guilds.post({ data: { name, region, icon } }) .then(data => { - if (this.client.guilds.has(data.id)) return resolve(this.client.guilds.get(data.id)); + if (this.client.guilds.cache.has(data.id)) return resolve(this.client.guilds.cache.get(data.id)); const handleGuild = guild => { if (guild.id === data.id) { @@ -95,4 +102,4 @@ class GuildStore extends DataStore { } } -module.exports = GuildStore; +module.exports = GuildManager; diff --git a/src/stores/GuildMemberStore.js b/src/managers/GuildMemberManager.js similarity index 91% rename from src/stores/GuildMemberStore.js rename to src/managers/GuildMemberManager.js index 7abc81caf..b7d49f5c3 100644 --- a/src/stores/GuildMemberStore.js +++ b/src/managers/GuildMemberManager.js @@ -1,21 +1,32 @@ 'use strict'; -const DataStore = require('./DataStore'); +const BaseManager = require('./BaseManager'); const GuildMember = require('../structures/GuildMember'); const { Events, OPCodes } = require('../util/Constants'); const Collection = require('../util/Collection'); const { Error, TypeError } = require('../errors'); /** - * Stores guild members. - * @extends {DataStore} + * Manages API methods for GuildMembers and stores their cache. + * @extends {BaseManager} */ -class GuildMemberStore extends DataStore { +class GuildMemberManager extends BaseManager { constructor(guild, iterable) { super(guild.client, iterable, GuildMember); + /** + * The guild this manager belongs to + * @type {Guild} + */ this.guild = guild; } + /** + * The cache of this Manager + * @property {Collection} cache + * @memberof GuildMemberManager + * @instance + */ + add(data, cache = true) { return super.add(data, cache, { id: data.user.id, extras: [this.guild] }); } @@ -49,7 +60,7 @@ class GuildMemberStore extends DataStore { const memberResolvable = super.resolveID(member); if (memberResolvable) return memberResolvable; const userResolvable = this.client.users.resolveID(member); - return this.has(userResolvable) ? userResolvable : null; + return this.cache.has(userResolvable) ? userResolvable : null; } /** @@ -184,7 +195,7 @@ class GuildMemberStore extends DataStore { _fetchSingle({ user, cache }) { - const existing = this.get(user); + const existing = this.cache.get(user); if (existing && !existing.partial) return Promise.resolve(existing); return this.client.api.guilds(this.guild.id).members(user).get() .then(data => this.add(data, cache)); @@ -192,8 +203,8 @@ class GuildMemberStore extends DataStore { _fetchMany({ query = '', limit = 0 } = {}) { return new Promise((resolve, reject) => { - if (this.guild.memberCount === this.size && !query && !limit) { - resolve(this); + if (this.guild.memberCount === this.cache.size && !query && !limit) { + resolve(this.cache); return; } this.guild.shard.send({ @@ -211,11 +222,11 @@ class GuildMemberStore extends DataStore { for (const member of members.values()) { if (query || limit) fetchedMembers.set(member.id, member); } - if (this.guild.memberCount <= this.size || + if (this.guild.memberCount <= this.cache.size || ((query || limit) && members.size < 1000) || (limit && fetchedMembers.size >= limit)) { this.guild.client.removeListener(Events.GUILD_MEMBERS_CHUNK, handler); - resolve(query || limit ? fetchedMembers : this); + resolve(query || limit ? fetchedMembers : this.cache); } }; const timeout = this.guild.client.setTimeout(() => { @@ -227,4 +238,4 @@ class GuildMemberStore extends DataStore { } } -module.exports = GuildMemberStore; +module.exports = GuildMemberManager; diff --git a/src/stores/GuildMemberRoleStore.js b/src/managers/GuildMemberRoleManager.js similarity index 77% rename from src/stores/GuildMemberRoleStore.js rename to src/managers/GuildMemberRoleManager.js index 047b8086a..d9186f90e 100644 --- a/src/stores/GuildMemberRoleStore.js +++ b/src/managers/GuildMemberRoleManager.js @@ -1,17 +1,22 @@ 'use strict'; const Collection = require('../util/Collection'); -const Util = require('../util/Util'); const { TypeError } = require('../errors'); /** - * Stores member roles - * @extends {Collection} + * Manages API methods for roles of a GuildMember and stores their cache. */ -class GuildMemberRoleStore extends Collection { +class GuildMemberRoleManager { constructor(member) { - super(); + /** + * 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 }); } @@ -22,9 +27,18 @@ class GuildMemberRoleStore extends Collection { * @private * @readonly */ - get _filtered() { + get _roles() { const everyone = this.guild.roles.everyone; - return this.guild.roles.filter(role => this.member._roles.includes(role.id)).set(everyone.id, everyone); + return this.guild.roles.cache.filter(role => this.member._roles.includes(role.id)).set(everyone.id, everyone); + } + + /** + * The roles of this member + * @type {Collection} + * @readonly + */ + get cache() { + return this._roles; } /** @@ -33,7 +47,7 @@ class GuildMemberRoleStore extends Collection { * @readonly */ get hoist() { - const hoistedRoles = this._filtered.filter(role => role.hoist); + const hoistedRoles = this._roles.filter(role => role.hoist); if (!hoistedRoles.size) return null; return hoistedRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev); } @@ -44,7 +58,7 @@ class GuildMemberRoleStore extends Collection { * @readonly */ get color() { - const coloredRoles = this._filtered.filter(role => role.color); + const coloredRoles = this._roles.filter(role => role.color); if (!coloredRoles.size) return null; return coloredRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev); } @@ -55,7 +69,7 @@ class GuildMemberRoleStore extends Collection { * @readonly */ get highest() { - return this._filtered.reduce((prev, role) => role.comparePositionTo(prev) > 0 ? role : prev, this.first()); + return this._roles.reduce((prev, role) => role.comparePositionTo(prev) > 0 ? role : prev, this._roles.first()); } /** @@ -72,7 +86,7 @@ class GuildMemberRoleStore extends Collection { 'Array or Collection of Roles or Snowflakes', true); } - const newRoles = [...new Set(roleOrRoles.concat(...this.values()))]; + const newRoles = [...new Set(roleOrRoles.concat(...this._roles.values()))]; return this.set(newRoles, reason); } else { roleOrRoles = this.guild.roles.resolve(roleOrRoles); @@ -84,7 +98,7 @@ class GuildMemberRoleStore extends Collection { await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles.id].put({ reason }); const clone = this.member._clone(); - clone._roles = [...this.keys(), roleOrRoles.id]; + clone._roles = [...this._roles.keys(), roleOrRoles.id]; return clone; } } @@ -103,7 +117,7 @@ class GuildMemberRoleStore extends Collection { 'Array or Collection of Roles or Snowflakes', true); } - const newRoles = this.filter(role => !roleOrRoles.includes(role)); + const newRoles = this._roles.filter(role => !roleOrRoles.includes(role)); return this.set(newRoles, reason); } else { roleOrRoles = this.guild.roles.resolve(roleOrRoles); @@ -115,7 +129,7 @@ class GuildMemberRoleStore extends Collection { await this.client.api.guilds[this.guild.id].members[this.member.id].roles[roleOrRoles.id].delete({ reason }); const clone = this.member._clone(); - const newRoles = this.filter(role => role.id !== roleOrRoles.id); + const newRoles = this._roles.filter(role => role.id !== roleOrRoles.id); clone._roles = [...newRoles.keys()]; return clone; } @@ -134,7 +148,7 @@ class GuildMemberRoleStore extends Collection { * @example * // Remove all the roles from a member * guildMember.roles.set([]) - * .then(member => console.log(`Member roles is now of ${member.roles.size} size`)) + * .then(member => console.log(`Member roles is now of ${member.roles.cache.size} size`)) * .catch(console.error); */ set(roles, reason) { @@ -143,23 +157,9 @@ class GuildMemberRoleStore extends Collection { clone() { const clone = new this.constructor(this.member); - clone.member._roles = [...this.keyArray()]; + clone.member._roles = [...this._roles.keyArray()]; return clone; } - - *[Symbol.iterator]() { - yield* this._filtered.entries(); - } - - valueOf() { - return this._filtered; - } - - static get [Symbol.species]() { - return Collection; - } } -Util.mixin(GuildMemberRoleStore, ['set']); - -module.exports = GuildMemberRoleStore; +module.exports = GuildMemberRoleManager; diff --git a/src/managers/MessageManager.js b/src/managers/MessageManager.js new file mode 100644 index 000000000..19eb82b8c --- /dev/null +++ b/src/managers/MessageManager.js @@ -0,0 +1,141 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const Message = require('../structures/Message'); +const LimitedCollection = require('../util/LimitedCollection'); +const Collection = require('../util/Collection'); + +/** +* Manages API methods for Messages and holds their cache. +* @extends {BaseManager} +*/ +class MessageManager extends BaseManager { + constructor(channel, iterable) { + super(channel.client, iterable, Message, LimitedCollection, channel.client.options.messageCacheMaxSize); + /** + * The channel that the messages belong to + * @type {TextBasedChannel} + */ + this.channel = channel; + } + + /** + * The cache of Messages + * @property {LimitedCollection} cache + * @memberof MessageManager + * @instance + */ + + add(data, cache) { + return super.add(data, cache, { extras: [this.channel] }); + } + + /** + * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and + * `after` are mutually exclusive. All the parameters are optional. + * @typedef {Object} ChannelLogsQueryOptions + * @property {number} [limit=50] Number of messages to acquire + * @property {Snowflake} [before] ID of a message to get the messages that were posted before it + * @property {Snowflake} [after] ID of a message to get the messages that were posted after it + * @property {Snowflake} [around] ID of a message to get the messages that were posted around it + */ + + /** + * Gets a message, or messages, from this channel. + * The returned Collection does not contain reaction users of the messages if they were not cached. + * Those need to be fetched separately in such a case. + * @param {Snowflake|ChannelLogsQueryOptions} [message] The ID of the message to fetch, or query parameters. + * @param {boolean} [cache=true] Whether to cache the message(s) + * @returns {Promise|Promise>} + * @example + * // Get message + * channel.messages.fetch('99539446449315840') + * .then(message => console.log(message.content)) + * .catch(console.error); + * @example + * // Get messages + * channel.messages.fetch({ limit: 10 }) + * .then(messages => console.log(`Received ${messages.size} messages`)) + * .catch(console.error); + * @example + * // Get messages and filter by user ID + * channel.messages.fetch() + * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`)) + * .catch(console.error); + */ + fetch(message, cache = true) { + return typeof message === 'string' ? this._fetchId(message, cache) : this._fetchMany(message, cache); + } + + /** + * Fetches the pinned messages of this channel and returns a collection of them. + * The returned Collection does not contain any reaction data of the messages. + * Those need to be fetched separately. + * @param {boolean} [cache=true] Whether to cache the message(s) + * @returns {Promise>} + * @example + * // Get pinned messages + * channel.fetchPinned() + * .then(messages => console.log(`Received ${messages.size} messages`)) + * .catch(console.error); + */ + fetchPinned(cache = true) { + return this.client.api.channels[this.channel.id].pins.get().then(data => { + const messages = new Collection(); + for (const message of data) messages.set(message.id, this.add(message, cache)); + return messages; + }); + } + + /** + * Data that can be resolved to a Message object. This can be: + * * A Message + * * A Snowflake + * @typedef {Message|Snowflake} MessageResolvable + */ + + /** + * Resolves a MessageResolvable to a Message object. + * @method resolve + * @memberof MessageManager + * @instance + * @param {MessageResolvable} message The message resolvable to resolve + * @returns {?Message} + */ + + /** + * Resolves a MessageResolvable to a Message ID string. + * @method resolveID + * @memberof MessageManager + * @instance + * @param {MessageResolvable} message The message resolvable to resolve + * @returns {?Snowflake} + */ + + + /** + * Deletes a message, even if it's not cached. + * @param {MessageResolvable} message The message to delete + * @param {string} [reason] Reason for deleting this message, if it does not belong to the client user + */ + async delete(message, reason) { + message = this.resolveID(message); + if (message) await this.client.api.channels(this.channel.id).messages(message).delete({ reason }); + } + + async _fetchId(messageID, cache) { + const existing = this.cache.get(messageID); + if (existing && !existing.partial) return existing; + const data = await this.client.api.channels[this.channel.id].messages[messageID].get(); + return this.add(data, cache); + } + + async _fetchMany(options = {}, cache) { + const data = await this.client.api.channels[this.channel.id].messages.get({ query: options }); + const messages = new Collection(); + for (const message of data) messages.set(message.id, this.add(message, cache)); + return messages; + } +} + +module.exports = MessageManager; diff --git a/src/stores/PresenceStore.js b/src/managers/PresenceManager.js similarity index 73% rename from src/stores/PresenceStore.js rename to src/managers/PresenceManager.js index 061d3f1e9..0a38fd874 100644 --- a/src/stores/PresenceStore.js +++ b/src/managers/PresenceManager.js @@ -1,19 +1,26 @@ 'use strict'; -const DataStore = require('./DataStore'); const { Presence } = require('../structures/Presence'); +const BaseManager = require('./BaseManager'); /** - * Stores presences. - * @extends {DataStore} + * Manages API methods for Presences and holds their cache. + * @extends {BaseManager} */ -class PresenceStore extends DataStore { +class PresenceManager extends BaseManager { constructor(client, iterable) { super(client, iterable, Presence); } + /** + * The cache of Presences + * @property {Collection} cache + * @memberof PresenceManager + * @instance + */ + add(data, cache) { - const existing = this.get(data.user.id); + const existing = this.cache.get(data.user.id); return existing ? existing.patch(data) : super.add(data, cache, { id: data.user.id }); } @@ -46,8 +53,8 @@ class PresenceStore extends DataStore { const presenceResolvable = super.resolveID(presence); if (presenceResolvable) return presenceResolvable; const userResolvable = this.client.users.resolveID(presence); - return this.has(userResolvable) ? userResolvable : null; + return this.cache.has(userResolvable) ? userResolvable : null; } } -module.exports = PresenceStore; +module.exports = PresenceManager; diff --git a/src/stores/ReactionStore.js b/src/managers/ReactionManager.js similarity index 74% rename from src/stores/ReactionStore.js rename to src/managers/ReactionManager.js index 1b1fb6030..7350aebda 100644 --- a/src/stores/ReactionStore.js +++ b/src/managers/ReactionManager.js @@ -1,15 +1,20 @@ 'use strict'; -const DataStore = require('./DataStore'); const MessageReaction = require('../structures/MessageReaction'); +const BaseManager = require('./BaseManager'); /** - * Stores reactions. - * @extends {DataStore} + * Manages API methods for reactions and holds their cache. + * @extends {BaseManager} */ -class ReactionStore extends DataStore { +class ReactionManager extends BaseManager { constructor(message, iterable) { super(message.client, iterable, MessageReaction); + + /** + * The message that this manager belongs to + * @type {Message} + */ this.message = message; } @@ -17,6 +22,13 @@ class ReactionStore extends DataStore { return super.add(data, cache, { id: data.emoji.id || data.emoji.name, extras: [this.message] }); } + /** + * The reaction cache of this manager + * @property {Collection} cache + * @memberof ReactionManager + * @instance + */ + /** * Data that can be resolved to a MessageReaction object. This can be: * * A MessageReaction @@ -27,7 +39,7 @@ class ReactionStore extends DataStore { /** * Resolves a MessageReactionResolvable to a MessageReaction object. * @method resolve - * @memberof ReactionStore + * @memberof ReactionManager * @instance * @param {MessageReactionResolvable} reaction The MessageReaction to resolve * @returns {?MessageReaction} @@ -36,7 +48,7 @@ class ReactionStore extends DataStore { /** * Resolves a MessageReactionResolvable to a MessageReaction ID string. * @method resolveID - * @memberof ReactionStore + * @memberof ReactionManager * @instance * @param {MessageReactionResolvable} reaction The MessageReaction to resolve * @returns {?Snowflake} @@ -53,18 +65,18 @@ class ReactionStore extends DataStore { _partial(emoji) { const id = emoji.id || emoji.name; - const existing = this.get(id); + const existing = this.cache.get(id); return !existing || existing.partial; } async _fetchReaction(reactionEmoji, cache) { const id = reactionEmoji.id || reactionEmoji.name; - const existing = this.get(id); + const existing = this.cache.get(id); if (!this._partial(reactionEmoji)) return existing; const data = await this.client.api.channels(this.message.channel.id).messages(this.message.id).get(); if (!data.reactions || !data.reactions.some(r => (r.emoji.id || r.emoji.name) === id)) { reactionEmoji.reaction._patch({ count: 0 }); - this.message.reactions.remove(id); + this.message.reactions.cache.delete(id); return existing; } for (const reaction of data.reactions) { @@ -74,4 +86,4 @@ class ReactionStore extends DataStore { } } -module.exports = ReactionStore; +module.exports = ReactionManager; diff --git a/src/stores/ReactionUserStore.js b/src/managers/ReactionUserManager.js similarity index 77% rename from src/stores/ReactionUserStore.js rename to src/managers/ReactionUserManager.js index dc250a9fb..c82b119d1 100644 --- a/src/stores/ReactionUserStore.js +++ b/src/managers/ReactionUserManager.js @@ -1,19 +1,30 @@ 'use strict'; const Collection = require('../util/Collection'); -const DataStore = require('./DataStore'); +const BaseManager = require('./BaseManager'); const { Error } = require('../errors'); /** - * A data store to store User models who reacted to a MessageReaction. - * @extends {DataStore} + * Manages API methods for users who reacted to a reaction and stores their cache. + * @extends {BaseManager} */ -class ReactionUserStore extends DataStore { +class ReactionUserManager extends BaseManager { constructor(client, iterable, reaction) { - super(client, iterable, require('../structures/User')); + super(client, iterable, { name: 'User' }); + /** + * The reaction that this manager belongs to + * @type {MessageReaction} + */ this.reaction = reaction; } + /** + * The cache of this manager + * @property {Collection} cache + * @memberof GuildManager + * @instance + */ + /** * Fetches all the users that gave this reaction. Resolves with a collection of users, mapped by their IDs. * @param {Object} [options] Options for fetching the users @@ -30,7 +41,7 @@ class ReactionUserStore extends DataStore { const users = new Collection(); for (const rawUser of data) { const user = this.client.users.add(rawUser); - this.set(user.id, user); + this.cache.set(user.id, user); users.set(user.id, user); } return users; @@ -52,4 +63,4 @@ class ReactionUserStore extends DataStore { } } -module.exports = ReactionUserStore; +module.exports = ReactionUserManager; diff --git a/src/stores/RoleStore.js b/src/managers/RoleManager.js similarity index 78% rename from src/stores/RoleStore.js rename to src/managers/RoleManager.js index 649048be2..9eb03cf2c 100644 --- a/src/stores/RoleStore.js +++ b/src/managers/RoleManager.js @@ -1,20 +1,31 @@ 'use strict'; -const DataStore = require('./DataStore'); +const BaseManager = require('./BaseManager'); const Role = require('../structures/Role'); const { resolveColor } = require('../util/Util'); const Permissions = require('../util/Permissions'); /** - * Stores roles. - * @extends {DataStore} + * Manages API methods for roles and stores their cache. + * @extends {BaseManager} */ -class RoleStore extends DataStore { +class RoleManager extends BaseManager { constructor(guild, iterable) { super(guild.client, iterable, Role); + /** + * The guild belonging to this manager + * @type {Guild} + */ this.guild = guild; } + /** + * The role cache of this manager + * @property {Collection} cache + * @memberof RoleManager + * @instance + */ + add(data, cache) { return super.add(data, cache, { extras: [this.guild] }); } @@ -23,11 +34,11 @@ class RoleStore extends DataStore { * Obtains one or more roles from Discord, or the role cache if they're already available. * @param {Snowflake} [id] ID or IDs of the role(s) * @param {boolean} [cache=true] Whether to cache the new roles objects if it weren't already - * @returns {Promise} + * @returns {Promise} * @example * // Fetch all roles from the guild * message.guild.roles.fetch() - * .then(roles => console.log(`There are ${roles.size} roles.`)) + * .then(roles => console.log(`There are ${roles.cache.size} roles.`)) * .catch(console.error); * @example * // Fetch a single role @@ -37,14 +48,14 @@ class RoleStore extends DataStore { */ async fetch(id, cache = true) { if (id) { - const existing = this.get(id); + const existing = this.cache.get(id); if (existing) return existing; } // We cannot fetch a single role, as of this commit's date, Discord API throws with 405 const roles = await this.client.api.guilds(this.guild.id).roles.get(); for (const role of roles) this.add(role, cache); - return id ? this.get(id) || null : this; + return id ? this.cache.get(id) || null : this; } /** @@ -57,7 +68,7 @@ class RoleStore extends DataStore { /** * Resolves a RoleResolvable to a Role object. * @method resolve - * @memberof RoleStore + * @memberof RoleManager * @instance * @param {RoleResolvable} role The role resolvable to resolve * @returns {?Role} @@ -66,7 +77,7 @@ class RoleStore extends DataStore { /** * Resolves a RoleResolvable to a role ID string. * @method resolveID - * @memberof RoleStore + * @memberof RoleManager * @instance * @param {RoleResolvable} role The role resolvable to resolve * @returns {?Snowflake} @@ -116,17 +127,17 @@ class RoleStore extends DataStore { * @readonly */ get everyone() { - return this.get(this.guild.id) || null; + return this.cache.get(this.guild.id) || null; } /** - * The role with the highest position in the store + * The role with the highest position in the cache * @type {Role} * @readonly */ get highest() { - return this.reduce((prev, role) => role.comparePositionTo(prev) > 0 ? role : prev, this.first()); + return this.cache.reduce((prev, role) => role.comparePositionTo(prev) > 0 ? role : prev, this.cache.first()); } } -module.exports = RoleStore; +module.exports = RoleManager; diff --git a/src/stores/UserStore.js b/src/managers/UserManager.js similarity index 80% rename from src/stores/UserStore.js rename to src/managers/UserManager.js index 20c25d05b..8818206aa 100644 --- a/src/stores/UserStore.js +++ b/src/managers/UserManager.js @@ -1,19 +1,26 @@ 'use strict'; -const DataStore = require('./DataStore'); +const BaseManager = require('./BaseManager'); const User = require('../structures/User'); const GuildMember = require('../structures/GuildMember'); const Message = require('../structures/Message'); /** - * A data store to store User models. - * @extends {DataStore} + * Manages API methods for users and stores their cache. + * @extends {BaseManager} */ -class UserStore extends DataStore { +class UserManager extends BaseManager { constructor(client, iterable) { super(client, iterable, User); } + /** + * The cache of this manager + * @property {Collection} cache + * @memberof UserManager + * @instance + */ + /** * Data that resolves to give a User object. This can be: * * A User object @@ -52,11 +59,11 @@ class UserStore extends DataStore { * @returns {Promise} */ async fetch(id, cache = true) { - const existing = this.get(id); + const existing = this.cache.get(id); if (existing && !existing.partial) return existing; const data = await this.client.api.users(id).get(); return this.add(data, cache); } } -module.exports = UserStore; +module.exports = UserManager; diff --git a/src/managers/VoiceStateManager.js b/src/managers/VoiceStateManager.js new file mode 100644 index 000000000..755392b2d --- /dev/null +++ b/src/managers/VoiceStateManager.js @@ -0,0 +1,37 @@ +'use strict'; + +const BaseManager = require('./BaseManager'); +const VoiceState = require('../structures/VoiceState'); + +/** + * Manages API methods for VoiceStates and stores their cache. + * @extends {BaseManager} + */ +class VoiceStateManager extends BaseManager { + constructor(guild, iterable) { + super(guild.client, iterable, VoiceState); + /** + * The guild this manager belongs to + * @type {Guild} + */ + this.guild = guild; + } + + /** + * The cache of this manager + * @property {Collection} cache + * @memberof VoiceStateManager + * @instance + */ + + add(data, cache = true) { + const existing = this.cache.get(data.user_id); + if (existing) return existing._patch(data); + + const entry = new VoiceState(this.guild, data); + if (cache) this.cache.set(data.user_id, entry); + return entry; + } +} + +module.exports = VoiceStateManager; diff --git a/src/sharding/ShardClientUtil.js b/src/sharding/ShardClientUtil.js index 8ed1b9787..8b76efd6f 100644 --- a/src/sharding/ShardClientUtil.js +++ b/src/sharding/ShardClientUtil.js @@ -86,7 +86,7 @@ class ShardClientUtil { * @param {string} prop Name of the client property to get, using periods for nesting * @returns {Promise>} * @example - * client.shard.fetchClientValues('guilds.size') + * client.shard.fetchClientValues('guilds.cache.size') * .then(results => console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`)) * .catch(console.error); * @see {@link ShardingManager#fetchClientValues} @@ -114,7 +114,7 @@ class ShardClientUtil { * @param {string|Function} script JavaScript to run on each shard * @returns {Promise>} Results of the script execution * @example - * client.shard.broadcastEval('this.guilds.size') + * client.shard.broadcastEval('this.guilds.cache.size') * .then(results => console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`)) * .catch(console.error); * @see {@link ShardingManager#broadcastEval} diff --git a/src/sharding/ShardingManager.js b/src/sharding/ShardingManager.js index 7126148d0..b6818d306 100644 --- a/src/sharding/ShardingManager.js +++ b/src/sharding/ShardingManager.js @@ -229,7 +229,7 @@ class ShardingManager extends EventEmitter { * @param {string} prop Name of the client property to get, using periods for nesting * @returns {Promise>} * @example - * manager.fetchClientValues('guilds.size') + * manager.fetchClientValues('guilds.cache.size') * .then(results => console.log(`${results.reduce((prev, val) => prev + val, 0)} total guilds`)) * .catch(console.error); */ diff --git a/src/stores/MessageStore.js b/src/stores/MessageStore.js deleted file mode 100644 index 59b224f72..000000000 --- a/src/stores/MessageStore.js +++ /dev/null @@ -1,135 +0,0 @@ -'use strict'; - -const DataStore = require('./DataStore'); -const Collection = require('../util/Collection'); -const Message = require('../structures/Message'); - -/** - * Stores messages for text-based channels. - * @extends {DataStore} - */ -class MessageStore extends DataStore { - constructor(channel, iterable) { - super(channel.client, iterable, Message); - this.channel = channel; - } - - add(data, cache) { - return super.add(data, cache, { extras: [this.channel] }); - } - - set(key, value) { - const maxSize = this.client.options.messageCacheMaxSize; - if (maxSize === 0) return; - if (this.size >= maxSize && maxSize > 0) this.delete(this.firstKey()); - super.set(key, value); - } - - /** - * The parameters to pass in when requesting previous messages from a channel. `around`, `before` and - * `after` are mutually exclusive. All the parameters are optional. - * @typedef {Object} ChannelLogsQueryOptions - * @property {number} [limit=50] Number of messages to acquire - * @property {Snowflake} [before] ID of a message to get the messages that were posted before it - * @property {Snowflake} [after] ID of a message to get the messages that were posted after it - * @property {Snowflake} [around] ID of a message to get the messages that were posted around it - */ - - /** - * Gets a message, or messages, from this channel. - * The returned Collection does not contain reaction users of the messages if they were not cached. - * Those need to be fetched separately in such a case. - * @param {Snowflake|ChannelLogsQueryOptions} [message] The ID of the message to fetch, or query parameters. - * @param {boolean} [cache=true] Whether to cache the message(s) - * @returns {Promise|Promise>} - * @example - * // Get message - * channel.messages.fetch('99539446449315840') - * .then(message => console.log(message.content)) - * .catch(console.error); - * @example - * // Get messages - * channel.messages.fetch({ limit: 10 }) - * .then(messages => console.log(`Received ${messages.size} messages`)) - * .catch(console.error); - * @example - * // Get messages and filter by user ID - * channel.messages.fetch() - * .then(messages => console.log(`${messages.filter(m => m.author.id === '84484653687267328').size} messages`)) - * .catch(console.error); - */ - fetch(message, cache = true) { - return typeof message === 'string' ? this._fetchId(message, cache) : this._fetchMany(message, cache); - } - - /** - * Fetches the pinned messages of this channel and returns a collection of them. - * The returned Collection does not contain any reaction data of the messages. - * Those need to be fetched separately. - * @param {boolean} [cache=true] Whether to cache the message(s) - * @returns {Promise>} - * @example - * // Get pinned messages - * channel.fetchPinned() - * .then(messages => console.log(`Received ${messages.size} messages`)) - * .catch(console.error); - */ - fetchPinned(cache = true) { - return this.client.api.channels[this.channel.id].pins.get().then(data => { - const messages = new Collection(); - for (const message of data) messages.set(message.id, this.add(message, cache)); - return messages; - }); - } - - /** - * Data that can be resolved to a Message object. This can be: - * * A Message - * * A Snowflake - * @typedef {Message|Snowflake} MessageResolvable - */ - - /** - * Resolves a MessageResolvable to a Message object. - * @method resolve - * @memberof MessageStore - * @instance - * @param {MessageResolvable} message The message resolvable to resolve - * @returns {?Message} - */ - - /** - * Resolves a MessageResolvable to a Message ID string. - * @method resolveID - * @memberof MessageStore - * @instance - * @param {MessageResolvable} message The message resolvable to resolve - * @returns {?Snowflake} - */ - - /** - * Deletes a message, even if it's not cached. - * @param {MessageResolvable} message The message to delete - * @param {string} [reason] Reason for deleting this message, if it does not belong to the client user - */ - async remove(message, reason) { - message = this.resolveID(message); - if (message) await this.client.api.channels(this.channel.id).messages(message).delete({ reason }); - } - - async _fetchId(messageID, cache) { - const existing = this.get(messageID); - if (existing && !existing.partial) return existing; - const data = await this.client.api.channels[this.channel.id].messages[messageID].get(); - return this.add(data, cache); - } - - async _fetchMany(options = {}, cache) { - const data = await this.client.api.channels[this.channel.id].messages.get({ query: options }); - const messages = new Collection(); - for (const message of data) messages.set(message.id, this.add(message, cache)); - return messages; - } -} - -module.exports = MessageStore; diff --git a/src/stores/VoiceStateStore.js b/src/stores/VoiceStateStore.js deleted file mode 100644 index a5eaac2dc..000000000 --- a/src/stores/VoiceStateStore.js +++ /dev/null @@ -1,26 +0,0 @@ -'use strict'; - -const DataStore = require('./DataStore'); -const VoiceState = require('../structures/VoiceState'); - -/** - * Stores voice states. - * @extends {DataStore} - */ -class VoiceStateStore extends DataStore { - constructor(guild, iterable) { - super(guild.client, iterable, VoiceState); - this.guild = guild; - } - - add(data, cache = true) { - const existing = this.get(data.user_id); - if (existing) return existing._patch(data); - - const entry = new VoiceState(this.guild, data); - if (cache) this.set(data.user_id, entry); - return entry; - } -} - -module.exports = VoiceStateStore; diff --git a/src/structures/CategoryChannel.js b/src/structures/CategoryChannel.js index 60be408c6..4ac9fbbb2 100644 --- a/src/structures/CategoryChannel.js +++ b/src/structures/CategoryChannel.js @@ -13,7 +13,7 @@ class CategoryChannel extends GuildChannel { * @readonly */ get children() { - return this.guild.channels.filter(c => c.parentID === this.id); + return this.guild.channels.cache.filter(c => c.parentID === this.id); } /** diff --git a/src/structures/Channel.js b/src/structures/Channel.js index 9eade6600..d5949c3cd 100644 --- a/src/structures/Channel.js +++ b/src/structures/Channel.js @@ -100,7 +100,7 @@ class Channel extends Base { const DMChannel = Structures.get('DMChannel'); channel = new DMChannel(client, data); } else { - guild = guild || client.guilds.get(data.guild_id); + guild = guild || client.guilds.cache.get(data.guild_id); if (guild) { switch (data.type) { case ChannelTypes.TEXT: { @@ -129,7 +129,7 @@ class Channel extends Base { break; } } - if (channel) guild.channels.set(channel.id, channel); + if (channel) guild.channels.cache.set(channel.id, channel); } } return channel; diff --git a/src/structures/DMChannel.js b/src/structures/DMChannel.js index 006a9fab2..9042519a9 100644 --- a/src/structures/DMChannel.js +++ b/src/structures/DMChannel.js @@ -2,7 +2,7 @@ const Channel = require('./Channel'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); -const MessageStore = require('../stores/MessageStore'); +const MessageManager = require('../managers/MessageManager'); /** * Represents a direct message channel between two users. @@ -19,10 +19,10 @@ class DMChannel extends Channel { // Override the channel type so partials have a known type this.type = 'dm'; /** - * A collection containing the messages sent to this channel - * @type {MessageStore} + * A manager of the messages belonging to this channel + * @type {MessageManager} */ - this.messages = new MessageStore(this); + this.messages = new MessageManager(this); this._typing = new Map(); } diff --git a/src/structures/Emoji.js b/src/structures/Emoji.js index 803fc5002..5cbf913d5 100644 --- a/src/structures/Emoji.js +++ b/src/structures/Emoji.js @@ -82,7 +82,7 @@ class Emoji extends Base { * @returns {string} * @example * // Send a custom emoji from a guild: - * const emoji = guild.emojis.first(); + * const emoji = guild.emojis.cache.first(); * msg.reply(`Hello! ${emoji}`); * @example * // Send the emoji used in a reaction to the channel the reaction is part of diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 04d091b0c..d9bad7cef 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -11,12 +11,12 @@ const Util = require('../util/Util'); const DataResolver = require('../util/DataResolver'); const Snowflake = require('../util/Snowflake'); const SystemChannelFlags = require('../util/SystemChannelFlags'); -const GuildMemberStore = require('../stores/GuildMemberStore'); -const RoleStore = require('../stores/RoleStore'); -const GuildEmojiStore = require('../stores/GuildEmojiStore'); -const GuildChannelStore = require('../stores/GuildChannelStore'); -const PresenceStore = require('../stores/PresenceStore'); -const VoiceStateStore = require('../stores/VoiceStateStore'); +const GuildMemberManager = require('../managers/GuildMemberManager'); +const RoleManager = require('../managers/RoleManager'); +const GuildEmojiManager = require('../managers/GuildEmojiManager'); +const GuildChannelManager = require('../managers/GuildChannelManager'); +const PresenceManager = require('../managers/PresenceManager'); +const VoiceStateManager = require('../managers/VoiceStateManager'); const Base = require('./Base'); const { Error, TypeError } = require('../errors'); @@ -35,34 +35,34 @@ class Guild extends Base { super(client); /** - * A collection of members that are in this guild. The key is the member's ID, the value is the member - * @type {GuildMemberStore} + * A manager of the members belonging to this guild + * @type {GuildMemberManager} */ - this.members = new GuildMemberStore(this); + this.members = new GuildMemberManager(this); /** - * A collection of channels that are in this guild. The key is the channel's ID, the value is the channel - * @type {GuildChannelStore} + * A manager of the members belonging to this guild + * @type {GuildChannelManager} */ - this.channels = new GuildChannelStore(this); + this.channels = new GuildChannelManager(this); /** - * A collection of roles that are in this guild. The key is the role's ID, the value is the role - * @type {RoleStore} + * A manager of the roles belonging to this guild + * @type {RoleManager} */ - this.roles = new RoleStore(this); + this.roles = new RoleManager(this); /** - * A collection of presences in this guild - * @type {PresenceStore} + * A manager of the presences belonging to this guild + * @type {PresenceManager} */ - this.presences = new PresenceStore(this.client); + this.presences = new PresenceManager(this.client); /** - * A collection of voice states in this guild - * @type {VoiceStateStore} + * A manager of the voice states of this guild + * @type {VoiceStateManager} */ - this.voiceStates = new VoiceStateStore(this); + this.voiceStates = new VoiceStateManager(this); /** * Whether the bot has been removed from the guild @@ -321,19 +321,19 @@ class Guild extends Base { this.features = data.features || this.features || []; if (data.channels) { - this.channels.clear(); + this.channels.cache.clear(); for (const rawChannel of data.channels) { this.client.channels.add(rawChannel, this); } } if (data.roles) { - this.roles.clear(); + this.roles.cache.clear(); for (const role of data.roles) this.roles.add(role); } if (data.members) { - this.members.clear(); + this.members.cache.clear(); for (const guildUser of data.members) this.members.add(guildUser); } @@ -352,7 +352,7 @@ class Guild extends Base { } if (data.voice_states) { - this.voiceStates.clear(); + this.voiceStates.cache.clear(); for (const voiceState of data.voice_states) { this.voiceStates.add(voiceState); } @@ -360,10 +360,10 @@ class Guild extends Base { if (!this.emojis) { /** - * A collection of emojis that are in this guild. The key is the emoji's ID, the value is the emoji. - * @type {GuildEmojiStore} + * A manager of the emojis belonging to this guild + * @type {GuildEmojiManager} */ - this.emojis = new GuildEmojiStore(this); + this.emojis = new GuildEmojiManager(this); if (data.emojis) for (const emoji of data.emojis) this.emojis.add(emoji); } else if (data.emojis) { this.client.actions.GuildEmojisUpdate.handle({ @@ -463,7 +463,7 @@ class Guild extends Base { * @readonly */ get owner() { - return this.members.get(this.ownerID) || (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) ? + return this.members.cache.get(this.ownerID) || (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) ? this.members.add({ user: { id: this.ownerID } }, true) : null); } @@ -474,7 +474,7 @@ class Guild extends Base { * @readonly */ get afkChannel() { - return this.client.channels.get(this.afkChannelID) || null; + return this.client.channels.cache.get(this.afkChannelID) || null; } /** @@ -483,7 +483,7 @@ class Guild extends Base { * @readonly */ get systemChannel() { - return this.client.channels.get(this.systemChannelID) || null; + return this.client.channels.cache.get(this.systemChannelID) || null; } /** @@ -492,7 +492,7 @@ class Guild extends Base { * @readonly */ get widgetChannel() { - return this.client.channels.get(this.widgetChannelID) || null; + return this.client.channels.cache.get(this.widgetChannelID) || null; } /** @@ -501,7 +501,7 @@ class Guild extends Base { * @readonly */ get embedChannel() { - return this.client.channels.get(this.embedChannelID) || null; + return this.client.channels.cache.get(this.embedChannelID) || null; } /** @@ -510,9 +510,10 @@ class Guild extends Base { * @readonly */ get me() { - return this.members.get(this.client.user.id) || (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) ? - this.members.add({ user: { id: this.client.user.id } }, true) : - null); + return this.members.cache.get(this.client.user.id) || + (this.client.options.partials.includes(PartialTypes.GUILD_MEMBER) ? + this.members.add({ user: { id: this.client.user.id } }, true) : + null); } /** @@ -521,7 +522,7 @@ class Guild extends Base { * @readonly */ get voice() { - return this.voiceStates.get(this.client.user.id); + return this.voiceStates.cache.get(this.client.user.id); } /** @@ -716,7 +717,7 @@ class Guild extends Base { fetchEmbed() { return this.client.api.guilds(this.id).embed.get().then(data => ({ enabled: data.enabled, - channel: data.channel_id ? this.channels.get(data.channel_id) : null, + channel: data.channel_id ? this.channels.cache.get(data.channel_id) : null, })); } @@ -763,7 +764,7 @@ class Guild extends Base { addMember(user, options) { user = this.client.users.resolveID(user); if (!user) return Promise.reject(new TypeError('INVALID_TYPE', 'user', 'UserResolvable')); - if (this.members.has(user)) return Promise.resolve(this.members.get(user)); + if (this.members.cache.has(user)) return Promise.resolve(this.members.cache.get(user)); options.access_token = options.accessToken; if (options.roles) { const roles = []; @@ -988,7 +989,7 @@ class Guild extends Base { * @returns {Promise} * @example * // Edit the guild owner - * guild.setOwner(guild.members.first()) + * guild.setOwner(guild.members.cache.first()) * .then(updated => console.log(`Updated the guild owner to ${updated.owner.displayName}`)) * .catch(console.error); */ @@ -1203,7 +1204,7 @@ class Guild extends Base { * @private */ _sortedRoles() { - return Util.discordSort(this.roles); + return Util.discordSort(this.roles.cache); } /** @@ -1214,7 +1215,7 @@ class Guild extends Base { */ _sortedChannels(channel) { const category = channel.type === ChannelTypes.CATEGORY; - return Util.discordSort(this.channels.filter(c => + return Util.discordSort(this.channels.cache.filter(c => c.type === channel.type && (category || c.parent === channel.parent) )); } diff --git a/src/structures/GuildAuditLogs.js b/src/structures/GuildAuditLogs.js index d1185bdd9..a6327f35c 100644 --- a/src/structures/GuildAuditLogs.js +++ b/src/structures/GuildAuditLogs.js @@ -285,7 +285,7 @@ class GuildAuditLogsEntry { */ this.executor = guild.client.options.partials.includes(PartialTypes.USER) ? guild.client.users.add({ id: data.user_id }) : - guild.client.users.get(data.user_id); + guild.client.users.cache.get(data.user_id); /** * An entry in the audit log representing a specific change. @@ -321,7 +321,7 @@ class GuildAuditLogsEntry { } else if (data.action_type === Actions.MESSAGE_DELETE) { this.extra = { count: data.options.count, - channel: guild.channels.get(data.options.channel_id), + channel: guild.channels.cache.get(data.options.channel_id), }; } else if (data.action_type === Actions.MESSAGE_BULK_DELETE) { this.extra = { @@ -330,11 +330,11 @@ class GuildAuditLogsEntry { } else { switch (data.options.type) { case 'member': - this.extra = guild.members.get(data.options.id); + this.extra = guild.members.cache.get(data.options.id); if (!this.extra) this.extra = { id: data.options.id }; break; case 'role': - this.extra = guild.roles.get(data.options.id); + this.extra = guild.roles.cache.get(data.options.id); if (!this.extra) this.extra = { id: data.options.id, name: data.options.role_name }; break; default: @@ -357,9 +357,9 @@ class GuildAuditLogsEntry { } else if (targetType === Targets.USER) { this.target = guild.client.options.partials.includes(PartialTypes.USER) ? guild.client.users.add({ id: data.target_id }) : - guild.client.users.get(data.target_id); + guild.client.users.cache.get(data.target_id); } else if (targetType === Targets.GUILD) { - this.target = guild.client.guilds.get(data.target_id); + this.target = guild.client.guilds.cache.get(data.target_id); } else if (targetType === Targets.WEBHOOK) { this.target = logs.webhooks.get(data.target_id) || new Webhook(guild.client, @@ -386,9 +386,9 @@ class GuildAuditLogsEntry { } }); } else if (targetType === Targets.MESSAGE) { - this.target = guild.client.users.get(data.target_id); + this.target = guild.client.users.cache.get(data.target_id); } else { - this.target = guild[`${targetType.toLowerCase()}s`].get(data.target_id) || { id: data.target_id }; + this.target = guild[`${targetType.toLowerCase()}s`].cache.get(data.target_id) || { id: data.target_id }; } } diff --git a/src/structures/GuildChannel.js b/src/structures/GuildChannel.js index fa1f84d43..649e5b222 100644 --- a/src/structures/GuildChannel.js +++ b/src/structures/GuildChannel.js @@ -72,7 +72,7 @@ class GuildChannel extends Channel { * @readonly */ get parent() { - return this.guild.channels.get(this.parentID) || null; + return this.guild.channels.cache.get(this.parentID) || null; } /** @@ -118,7 +118,7 @@ class GuildChannel extends Channel { if (!verified) member = this.guild.members.resolve(member); if (!member) return []; - roles = roles || member.roles; + roles = roles || member.roles.cache; const roleOverwrites = []; let memberOverwrites; let everyoneOverwrites; @@ -149,7 +149,7 @@ class GuildChannel extends Channel { memberPermissions(member) { if (member.id === this.guild.ownerID) return new Permissions(Permissions.ALL).freeze(); - const roles = member.roles; + const roles = member.roles.cache; const permissions = new Permissions(roles.map(role => role.permissions)); if (permissions.has(Permissions.FLAGS.ADMINISTRATOR)) return new Permissions(Permissions.ALL).freeze(); @@ -274,7 +274,7 @@ class GuildChannel extends Channel { */ get members() { const members = new Collection(); - for (const member of this.guild.members.values()) { + for (const member of this.guild.members.cache.values()) { if (this.permissionsFor(member).has('VIEW_CHANNEL', false)) { members.set(member.id, member); } diff --git a/src/structures/GuildEmoji.js b/src/structures/GuildEmoji.js index a7620911d..64138b86b 100644 --- a/src/structures/GuildEmoji.js +++ b/src/structures/GuildEmoji.js @@ -1,6 +1,6 @@ 'use strict'; -const GuildEmojiRoleStore = require('../stores/GuildEmojiRoleStore'); +const GuildEmojiRoleManager = require('../managers/GuildEmojiRoleManager'); const Permissions = require('../util/Permissions'); const { Error } = require('../errors'); const Emoji = require('./Emoji'); @@ -79,12 +79,12 @@ class GuildEmoji extends Emoji { } /** - * A collection of roles this emoji is active for (empty if all), mapped by role ID - * @type {GuildEmojiRoleStore} + * A manager for roles this emoji is active for. + * @type {GuildEmojiRoleManager} * @readonly */ get roles() { - return new GuildEmojiRoleStore(this); + return new GuildEmojiRoleManager(this); } /** @@ -168,15 +168,15 @@ class GuildEmoji extends Emoji { other.name === this.name && other.managed === this.managed && other.requiresColons === this.requiresColons && - other.roles.size === this.roles.size && - other.roles.every(role => this.roles.has(role.id)) + other.roles.cache.size === this.roles.cache.size && + other.roles.cache.every(role => this.roles.cache.has(role.id)) ); } else { return ( other.id === this.id && other.name === this.name && - other.roles.length === this.roles.size && - other.roles.every(role => this.roles.has(role)) + other.roles.length === this.roles.cache.size && + other.roles.every(role => this.roles.cache.has(role)) ); } } diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index 8a8b8bb6a..b832f9593 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -3,7 +3,7 @@ const TextBasedChannel = require('./interfaces/TextBasedChannel'); const Role = require('./Role'); const Permissions = require('../util/Permissions'); -const GuildMemberRoleStore = require('../stores/GuildMemberRoleStore'); +const GuildMemberRoleManager = require('../managers/GuildMemberRoleManager'); const Base = require('./Base'); const VoiceState = require('./VoiceState'); const { Presence } = require('./Presence'); @@ -101,12 +101,12 @@ class GuildMember extends Base { } /** - * A collection of roles that are applied to this member, mapped by the role ID - * @type {GuildMemberRoleStore} + * A manager for the roles belonging to this member + * @type {GuildMemberRoleManager} * @readonly */ get roles() { - return new GuildMemberRoleStore(this); + return new GuildMemberRoleManager(this); } /** @@ -115,8 +115,8 @@ class GuildMember extends Base { * @readonly */ get lastMessage() { - const channel = this.guild.channels.get(this.lastMessageChannelID); - return (channel && channel.messages.get(this.lastMessageID)) || null; + const channel = this.guild.channels.cache.get(this.lastMessageChannelID); + return (channel && channel.messages.cache.get(this.lastMessageID)) || null; } /** @@ -125,7 +125,7 @@ class GuildMember extends Base { * @readonly */ get voice() { - return this.guild.voiceStates.get(this.id) || new VoiceState(this.guild, { user_id: this.id }); + return this.guild.voiceStates.cache.get(this.id) || new VoiceState(this.guild, { user_id: this.id }); } /** @@ -152,7 +152,7 @@ class GuildMember extends Base { * @readonly */ get presence() { - return this.guild.presences.get(this.id) || new Presence(this.client, { + return this.guild.presences.cache.get(this.id) || new Presence(this.client, { user: { id: this.id, }, @@ -205,7 +205,7 @@ class GuildMember extends Base { */ get permissions() { if (this.user.id === this.guild.ownerID) return new Permissions(Permissions.ALL).freeze(); - return new Permissions(this.roles.map(role => role.permissions)).freeze(); + return new Permissions(this.roles.cache.map(role => role.permissions)).freeze(); } /** @@ -261,7 +261,7 @@ class GuildMember extends Base { */ hasPermission(permission, { checkAdmin = true, checkOwner = true } = {}) { if (checkOwner && this.user.id === this.guild.ownerID) return true; - return this.roles.some(r => r.permissions.has(permission, checkAdmin)); + return this.roles.cache.some(r => r.permissions.has(permission, checkAdmin)); } /** diff --git a/src/structures/Integration.js b/src/structures/Integration.js index 5ff760dc7..8fef7aa22 100644 --- a/src/structures/Integration.js +++ b/src/structures/Integration.js @@ -56,7 +56,7 @@ class Integration extends Base { * The role that this integration uses for subscribers * @type {Role} */ - this.role = this.guild.roles.get(data.role_id); + this.role = this.guild.roles.cache.get(data.role_id); /** * The user for this integration diff --git a/src/structures/Invite.js b/src/structures/Invite.js index 4d048b3f1..88b62b4a9 100644 --- a/src/structures/Invite.js +++ b/src/structures/Invite.js @@ -117,7 +117,7 @@ class Invite extends Base { */ get deletable() { const guild = this.guild; - if (!guild || !this.client.guilds.has(guild.id)) return false; + if (!guild || !this.client.guilds.cache.has(guild.id)) return false; if (!guild.me) throw new Error('GUILD_UNCACHED_ME'); return this.channel.permissionsFor(this.client.user).has(Permissions.FLAGS.MANAGE_CHANNELS, false) || guild.me.permissions.has(Permissions.FLAGS.MANAGE_GUILD); diff --git a/src/structures/Message.js b/src/structures/Message.js index b09f1d348..adc4ec834 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -7,7 +7,7 @@ const ReactionCollector = require('./ReactionCollector'); const ClientApplication = require('./ClientApplication'); const Util = require('../util/Util'); const Collection = require('../util/Collection'); -const ReactionStore = require('../stores/ReactionStore'); +const ReactionManager = require('../managers/ReactionManager'); const { MessageTypes } = require('../util/Constants'); const Permissions = require('../util/Permissions'); const Base = require('./Base'); @@ -126,10 +126,10 @@ class Message extends Base { this.editedTimestamp = data.edited_timestamp ? new Date(data.edited_timestamp).getTime() : null; /** - * A collection of reactions to this message, mapped by the reaction ID - * @type {ReactionStore} + * A manager of the reactions belonging to this message + * @type {ReactionManager} */ - this.reactions = new ReactionStore(this); + this.reactions = new ReactionManager(this); if (data.reactions && data.reactions.length > 0) { for (const reaction of data.reactions) { this.reactions.add(reaction); @@ -452,7 +452,7 @@ class Message extends Base { * .catch(console.error); * @example * // React to a message with a custom emoji - * message.react(message.guild.emojis.get('123456789012345678')) + * message.react(message.guild.emojis.cache.get('123456789012345678')) * .then(console.log) * .catch(console.error); */ @@ -484,7 +484,7 @@ class Message extends Base { */ delete({ timeout = 0, reason } = {}) { if (timeout <= 0) { - return this.channel.messages.remove(this.id, reason).then(() => this); + return this.channel.messages.delete(this.id, reason).then(() => this); } else { return new Promise(resolve => { this.client.setTimeout(() => { diff --git a/src/structures/MessageMentions.js b/src/structures/MessageMentions.js index ab2d5c81b..6079dc23b 100644 --- a/src/structures/MessageMentions.js +++ b/src/structures/MessageMentions.js @@ -71,7 +71,7 @@ class MessageMentions { } else { this.roles = new Collection(); for (const mention of roles) { - const role = message.channel.guild.roles.get(mention); + const role = message.channel.guild.roles.cache.get(mention); if (role) this.roles.set(role.id, role); } } @@ -156,7 +156,7 @@ class MessageMentions { this._channels = new Collection(); let matches; while ((matches = this.constructor.CHANNELS_PATTERN.exec(this._content)) !== null) { - const chan = this.client.channels.get(matches[1]); + const chan = this.client.channels.cache.get(matches[1]); if (chan) this._channels.set(chan.id, chan); } return this._channels; @@ -175,7 +175,7 @@ class MessageMentions { has(data, { ignoreDirect = false, ignoreRoles = false, ignoreEveryone = false } = {}) { if (!ignoreEveryone && this.everyone) return true; if (!ignoreRoles && data instanceof GuildMember) { - for (const role of this.roles.values()) if (data.roles.has(role.id)) return true; + for (const role of this.roles.values()) if (data.roles.cache.has(role.id)) return true; } if (!ignoreDirect) { diff --git a/src/structures/MessageReaction.js b/src/structures/MessageReaction.js index 5cb9210fb..150288a55 100644 --- a/src/structures/MessageReaction.js +++ b/src/structures/MessageReaction.js @@ -3,7 +3,7 @@ const GuildEmoji = require('./GuildEmoji'); const Util = require('../util/Util'); const ReactionEmoji = require('./ReactionEmoji'); -const ReactionUserStore = require('../stores/ReactionUserStore'); +const ReactionUserManager = require('../managers/ReactionUserManager'); /** * Represents a reaction to a message. @@ -35,10 +35,10 @@ class MessageReaction { this.me = data.me; /** - * The users that have given this reaction, mapped by their ID - * @type {ReactionUserStore} + * A manager of the users that have given this reaction + * @type {ReactionUserManager} */ - this.users = new ReactionUserStore(client, undefined, this); + this.users = new ReactionUserManager(client, undefined, this); this._emoji = new ReactionEmoji(this, data.emoji); @@ -75,7 +75,7 @@ class MessageReaction { if (this._emoji instanceof GuildEmoji) return this._emoji; // Check to see if the emoji has become known to the client if (this._emoji.id) { - const emojis = this.message.client.emojis; + const emojis = this.message.client.emojis.cache; if (emojis.has(this._emoji.id)) { const emoji = emojis.get(this._emoji.id); this._emoji = emoji; @@ -108,18 +108,18 @@ class MessageReaction { _add(user) { if (this.partial) return; - this.users.set(user.id, user); + this.users.cache.set(user.id, user); if (!this.me || user.id !== this.message.client.user.id || this.count === 0) this.count++; if (!this.me) this.me = user.id === this.message.client.user.id; } _remove(user) { if (this.partial) return; - this.users.delete(user.id); + this.users.cache.delete(user.id); if (!this.me || user.id !== this.message.client.user.id) this.count--; if (user.id === this.message.client.user.id) this.me = false; - if (this.count <= 0 && this.users.size === 0) { - this.message.reactions.remove(this.emoji.id || this.emoji.name); + if (this.count <= 0 && this.users.cache.size === 0) { + this.message.reactions.cache.delete(this.emoji.id || this.emoji.name); } } } diff --git a/src/structures/Presence.js b/src/structures/Presence.js index 12888f1c6..fdc038a5c 100644 --- a/src/structures/Presence.js +++ b/src/structures/Presence.js @@ -66,7 +66,7 @@ class Presence { * @readonly */ get user() { - return this.client.users.get(this.userID) || null; + return this.client.users.cache.get(this.userID) || null; } /** @@ -75,7 +75,7 @@ class Presence { * @readonly */ get member() { - return this.guild.members.get(this.userID) || null; + return this.guild.members.cache.get(this.userID) || null; } patch(data) { diff --git a/src/structures/ReactionCollector.js b/src/structures/ReactionCollector.js index e7d5aeb16..65b604cdf 100644 --- a/src/structures/ReactionCollector.js +++ b/src/structures/ReactionCollector.js @@ -74,7 +74,7 @@ class ReactionCollector extends Collector { this.on('remove', (reaction, user) => { this.total--; - if (!this.collected.some(r => r.users.has(user.id))) this.users.delete(user.id); + if (!this.collected.some(r => r.users.cache.has(user.id))) this.users.delete(user.id); }); } diff --git a/src/structures/Role.js b/src/structures/Role.js index 9c7ebd494..cedaa5456 100644 --- a/src/structures/Role.js +++ b/src/structures/Role.js @@ -117,7 +117,7 @@ class Role extends Base { * @readonly */ get members() { - return this.guild.members.filter(m => m.roles.has(this.id)); + return this.guild.members.cache.filter(m => m.roles.cache.has(this.id)); } /** diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index 66212e70a..10b5d7cb3 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -5,7 +5,7 @@ const Webhook = require('./Webhook'); const TextBasedChannel = require('./interfaces/TextBasedChannel'); const Collection = require('../util/Collection'); const DataResolver = require('../util/DataResolver'); -const MessageStore = require('../stores/MessageStore'); +const MessageManager = require('../managers/MessageManager'); /** * Represents a guild text channel on Discord. @@ -20,10 +20,10 @@ class TextChannel extends GuildChannel { constructor(guild, data) { super(guild, data); /** - * A collection containing the messages sent to this channel - * @type {MessageStore} + * A manager of the messages sent to this channel + * @type {MessageManager} */ - this.messages = new MessageStore(this); + this.messages = new MessageManager(this); this._typing = new Map(); } diff --git a/src/structures/User.js b/src/structures/User.js index 7ae67451b..ae3017052 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -119,8 +119,8 @@ class User extends Base { * @readonly */ get lastMessage() { - const channel = this.client.channels.get(this.lastMessageChannelID); - return (channel && channel.messages.get(this.lastMessageID)) || null; + const channel = this.client.channels.cache.get(this.lastMessageChannelID); + return (channel && channel.messages.cache.get(this.lastMessageID)) || null; } /** @@ -129,8 +129,8 @@ class User extends Base { * @readonly */ get presence() { - for (const guild of this.client.guilds.values()) { - if (guild.presences.has(this.id)) return guild.presences.get(this.id); + for (const guild of this.client.guilds.cache.values()) { + if (guild.presences.cache.has(this.id)) return guild.presences.cache.get(this.id); } return new Presence(this.client, { user: { id: this.id } }); } @@ -209,7 +209,7 @@ class User extends Base { * @readonly */ get dmChannel() { - return this.client.channels.find(c => c.type === 'dm' && c.recipient.id === this.id) || null; + return this.client.channels.cache.find(c => c.type === 'dm' && c.recipient.id === this.id) || null; } /** diff --git a/src/structures/VoiceChannel.js b/src/structures/VoiceChannel.js index 15f2e496f..6b3850dfb 100644 --- a/src/structures/VoiceChannel.js +++ b/src/structures/VoiceChannel.js @@ -34,7 +34,7 @@ class VoiceChannel extends GuildChannel { */ get members() { const coll = new Collection(); - for (const state of this.guild.voiceStates.values()) { + for (const state of this.guild.voiceStates.cache.values()) { if (state.channelID === this.id && state.member) { coll.set(state.id, state.member); } diff --git a/src/structures/VoiceState.js b/src/structures/VoiceState.js index c60f6ac65..3ad02dea1 100644 --- a/src/structures/VoiceState.js +++ b/src/structures/VoiceState.js @@ -72,7 +72,7 @@ class VoiceState extends Base { * @readonly */ get member() { - return this.guild.members.get(this.id) || null; + return this.guild.members.cache.get(this.id) || null; } /** @@ -81,7 +81,7 @@ class VoiceState extends Base { * @readonly */ get channel() { - return this.guild.channels.get(this.channelID) || null; + return this.guild.channels.cache.get(this.channelID) || null; } /** diff --git a/src/structures/Webhook.js b/src/structures/Webhook.js index 9b6b43c8f..b29607040 100644 --- a/src/structures/Webhook.js +++ b/src/structures/Webhook.js @@ -70,7 +70,7 @@ class Webhook { * The owner of the webhook * @type {?User|Object} */ - this.owner = this.client.users ? this.client.users.get(data.user.id) : data.user; + this.owner = this.client.users ? this.client.users.cache.get(data.user.id) : data.user; } else { this.owner = null; } @@ -154,7 +154,7 @@ class Webhook { query: { wait: true }, auth: false, }).then(d => { - const channel = this.client.channels ? this.client.channels.get(d.channel_id) : undefined; + const channel = this.client.channels ? this.client.channels.cache.get(d.channel_id) : undefined; if (!channel) return d; return channel.messages.add(d, false); }); diff --git a/src/structures/interfaces/TextBasedChannel.js b/src/structures/interfaces/TextBasedChannel.js index a6b183753..4106e00df 100644 --- a/src/structures/interfaces/TextBasedChannel.js +++ b/src/structures/interfaces/TextBasedChannel.js @@ -13,10 +13,10 @@ const APIMessage = require('../APIMessage'); class TextBasedChannel { constructor() { /** - * A collection containing the messages sent to this channel - * @type {MessageStore} + * A manager of the messages sent to this channel + * @type {MessageManager} */ - this.messages = new MessageStore(this); + this.messages = new MessageManager(this); /** * The ID of the last message in the channel, if one was sent @@ -37,7 +37,7 @@ class TextBasedChannel { * @readonly */ get lastMessage() { - return this.messages.get(this.lastMessageID) || null; + return this.messages.cache.get(this.lastMessageID) || null; } /** @@ -350,4 +350,4 @@ class TextBasedChannel { module.exports = TextBasedChannel; // Fixes Circular -const MessageStore = require('../../stores/MessageStore'); +const MessageManager = require('../../managers/MessageManager'); diff --git a/src/util/LimitedCollection.js b/src/util/LimitedCollection.js new file mode 100644 index 000000000..5719a9fa6 --- /dev/null +++ b/src/util/LimitedCollection.js @@ -0,0 +1,29 @@ +'use strict'; + +const Collection = require('./Collection.js'); + +/** + * A Collection which holds a max amount of entries. The first key is deleted if the Collection has + * reached max size. + * @extends {Collection} + * @param {number} [maxSize=0] The maximum size of the Collection + * @param {Iterable} [iterable=null] Optional entries passed to the Map constructor. + */ +class LimitedCollection extends Collection { + constructor(maxSize = 0, iterable = null) { + super(iterable); + /** + * The max size of the Collection. + * @type {number} + */ + this.maxSize = maxSize; + } + + set(key, value) { + if (this.maxSize === 0) return this; + if (this.size >= this.maxSize && !this.has(key)) this.delete(this.firstKey()); + return super.set(key, value); + } +} + +module.exports = LimitedCollection; diff --git a/src/util/Structures.js b/src/util/Structures.js index 02529d26d..c4061f62c 100644 --- a/src/util/Structures.js +++ b/src/util/Structures.js @@ -1,7 +1,7 @@ 'use strict'; /** - * Allows for the extension of built-in Discord.js structures that are instantiated by {@link DataStore DataStores}. + * Allows for the extension of built-in Discord.js structures that are instantiated by {@link BaseManager Managers}. */ class Structures { constructor() { diff --git a/src/util/Util.js b/src/util/Util.js index 6087f3c24..da4bff449 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -528,25 +528,25 @@ class Util { .replace(/<@!?[0-9]+>/g, input => { const id = input.replace(/<|!|>|@/g, ''); if (message.channel.type === 'dm') { - const user = message.client.users.get(id); + const user = message.client.users.cache.get(id); return user ? `@${user.username}` : input; } - const member = message.channel.guild.members.get(id); + const member = message.channel.guild.members.cache.get(id); if (member) { return `@${member.displayName}`; } else { - const user = message.client.users.get(id); + const user = message.client.users.cache.get(id); return user ? `@${user.username}` : input; } }) .replace(/<#[0-9]+>/g, input => { - const channel = message.client.channels.get(input.replace(/<|#|>/g, '')); + const channel = message.client.channels.cache.get(input.replace(/<|#|>/g, '')); return channel ? `#${channel.name}` : input; }) .replace(/<@&[0-9]+>/g, input => { if (message.channel.type === 'dm') return input; - const role = message.guild.roles.get(input.replace(/<|@|>|&/g, '')); + const role = message.guild.roles.cache.get(input.replace(/<|@|>|&/g, '')); return role ? `@${role.name}` : input; }); } @@ -571,34 +571,6 @@ class Util { setTimeout(resolve, ms); }); } - - /** - * Adds methods from collections and maps onto the provided store - * @param {DataStore} store The store to mixin - * @param {string[]} ignored The properties to ignore - * @private - */ - /* eslint-disable func-names */ - static mixin(store, ignored) { - const Collection = require('./Collection'); - Object.getOwnPropertyNames(Collection.prototype) - .concat(Object.getOwnPropertyNames(Map.prototype)).forEach(prop => { - if (ignored.includes(prop)) return; - if (prop === 'size') { - Object.defineProperty(store.prototype, prop, { - get: function() { - return this._filtered[prop]; - }, - }); - return; - } - const func = Collection.prototype[prop]; - if (prop === 'constructor' || typeof func !== 'function') return; - store.prototype[prop] = function(...args) { - return func.apply(this._filtered, args); - }; - }); - } } module.exports = Util; diff --git a/typings/index.d.ts b/typings/index.d.ts index 07f725bce..a0f40ce97 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -151,16 +151,16 @@ declare module 'discord.js' { private _eval(script: string): any; private _validateOptions(options?: ClientOptions): void; - public channels: ChannelStore; - public readonly emojis: GuildEmojiStore; - public guilds: GuildStore; + public channels: ChannelManager; + public readonly emojis: GuildEmojiManager; + public guilds: GuildManager; public readyAt: Date | null; public readonly readyTimestamp: number | null; public shard: ShardClientUtil | null; public token: string | null; public readonly uptime: number | null; public user: ClientUser | null; - public users: UserStore; + public users: UserManager; public voice: ClientVoiceManager | null; public ws: WebSocketManager; public destroy(): void; @@ -328,16 +328,6 @@ declare module 'discord.js' { public setUsername(username: string): Promise; } - export class Collection extends BaseCollection { - public flatMap(fn: (value: V, key: K, collection: this) => Collection): Collection; - public flatMap(fn: (this: This, value: V, key: K, collection: this) => Collection, thisArg: This): Collection; - public flatMap(fn: (value: V, key: K, collection: this) => Collection, thisArg?: unknown): Collection; - public mapValues(fn: (value: V, key: K, collection: this) => T): Collection; - public mapValues(fn: (this: This, value: V, key: K, collection: this) => T, thisArg: This): Collection; - public mapValues(fn: (value: V, key: K, collection: this) => T, thisArg?: unknown): Collection; - public toJSON(): object; - } - export abstract class Collector extends EventEmitter { constructor(client: Client, filter: CollectorFilter, options?: CollectorOptions); private _timeout: NodeJS.Timer | null; @@ -648,7 +638,7 @@ declare module 'discord.js' { export class DMChannel extends TextBasedChannel(Channel) { constructor(client: Client, data?: object); - public messages: MessageStore; + public messages: MessageManager; public recipient: User; public readonly partial: false; public fetch(): Promise; @@ -680,7 +670,7 @@ declare module 'discord.js' { public applicationID: Snowflake; public available: boolean; public banner: string | null; - public channels: GuildChannelStore; + public channels: GuildChannelManager; public readonly createdAt: Date; public readonly createdTimestamp: number; public defaultMessageNotifications: DefaultMessageNotifications | number; @@ -689,7 +679,7 @@ declare module 'discord.js' { public embedChannel: GuildChannel | null; public embedChannelID: Snowflake | null; public embedEnabled: boolean; - public emojis: GuildEmojiStore; + public emojis: GuildEmojiManager; public explicitContentFilter: number; public features: GuildFeatures[]; public icon: string | null; @@ -701,7 +691,7 @@ declare module 'discord.js' { public maximumPresences: number | null; public readonly me: GuildMember | null; public memberCount: number; - public members: GuildMemberStore; + public members: GuildMemberManager; public mfaLevel: number; public name: string; public readonly nameAcronym: string; @@ -710,9 +700,9 @@ declare module 'discord.js' { public readonly partnered: boolean; public premiumSubscriptionCount: number | null; public premiumTier: PremiumTier; - public presences: PresenceStore; + public presences: PresenceManager; public region: string; - public roles: RoleStore; + public roles: RoleManager; public readonly shard: WebSocketShard; public shardID: number; public splash: string | null; @@ -723,7 +713,7 @@ declare module 'discord.js' { public verificationLevel: number; public readonly verified: boolean; public readonly voice: VoiceState | null; - public readonly voiceStates: VoiceStateStore; + public readonly voiceStates: VoiceStateManager; public readonly widgetChannel: TextChannel | null; public widgetChannelID: Snowflake | null; public widgetEnabled: boolean | null; @@ -847,7 +837,7 @@ declare module 'discord.js' { public id: Snowflake; public managed: boolean; public requiresColons: boolean; - public roles: GuildEmojiRoleStore; + public roles: GuildEmojiRoleManager; public readonly url: string; public delete(reason?: string): Promise; public edit(data: GuildEmojiEditData, reason?: string): Promise; @@ -875,7 +865,7 @@ declare module 'discord.js' { public readonly premiumSince: Date | null; public premiumSinceTimestamp: number | null; public readonly presence: Presence; - public roles: GuildMemberRoleStore; + public roles: GuildMemberRoleManager; public user: User; public readonly voice: VoiceState; public ban(options?: BanOptions): Promise; @@ -978,7 +968,7 @@ declare module 'discord.js' { public readonly partial: false; public readonly pinnable: boolean; public pinned: boolean; - public reactions: ReactionStore; + public reactions: ReactionManager; public system: boolean; public tts: boolean; public type: MessageType; @@ -1113,7 +1103,7 @@ declare module 'discord.js' { public me: boolean; public message: Message; public readonly partial: boolean; - public users: ReactionUserStore; + public users: ReactionUserManager; public remove(): Promise; public fetch(): Promise; public toJSON(): object; @@ -1397,7 +1387,7 @@ declare module 'discord.js' { export class TextChannel extends TextBasedChannel(GuildChannel) { constructor(guild: Guild, data?: object); - public messages: MessageStore; + public messages: MessageManager; public nsfw: boolean; public rateLimitPerUser: number; public topic: string | null; @@ -1409,7 +1399,7 @@ declare module 'discord.js' { export class NewsChannel extends TextBasedChannel(GuildChannel) { constructor(guild: Guild, data?: object); - public messages: MessageStore; + public messages: MessageManager; public nsfw: boolean; public topic: string | null; public createWebhook(name: string, options?: { avatar?: BufferResolvable | Base64Resolvable, reason?: string }): Promise; @@ -1749,64 +1739,64 @@ declare module 'discord.js' { //#endregion -//#region Stores +//#region Collections - export class ChannelStore extends DataStore { - constructor(client: Client, iterable: Iterable, options?: { lru: boolean }); - constructor(client: Client, options?: { lru: boolean }); - public fetch(id: Snowflake, cache?: boolean): Promise; - } - - export class DataStore, R = any> extends Collection { - constructor(client: Client, iterable: Iterable, holds: VConstructor); - public client: Client; - public holds: VConstructor; - public add(data: any, cache?: boolean, { id, extras }?: { id: K, extras: any[] }): V; - public remove(key: K): void; - public resolve(resolvable: R): V | null; - public resolveID(resolvable: R): K | null; - // Don't worry about those bunch of ts-ignores here, this is intended https://github.com/microsoft/TypeScript/issues/1213 - // @ts-ignore - public filter(fn: (value: V, key: K, collection: this) => boolean): Collection; - // @ts-ignore - public filter(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): Collection; - // @ts-ignore - public filter(fn: (value: V, key: K, collection: this) => boolean, thisArg?: unknown): Collection; - // @ts-ignore - public partition(fn: (value: V, key: K, collection: this) => boolean): [Collection, Collection]; - // @ts-ignore - public partition(fn: (this: T, value: V, key: K, collection: this) => boolean, thisArg: T): [Collection, Collection]; - // @ts-ignore - public partition(fn: (value: V, key: K, collection: this) => boolean, thisArg?: unknown): [Collection, Collection]; + export class Collection extends BaseCollection { public flatMap(fn: (value: V, key: K, collection: this) => Collection): Collection; public flatMap(fn: (this: This, value: V, key: K, collection: this) => Collection, thisArg: This): Collection; public flatMap(fn: (value: V, key: K, collection: this) => Collection, thisArg?: unknown): Collection; public mapValues(fn: (value: V, key: K, collection: this) => T): Collection; public mapValues(fn: (this: This, value: V, key: K, collection: this) => T, thisArg: This): Collection; public mapValues(fn: (value: V, key: K, collection: this) => T, thisArg?: unknown): Collection; - // @ts-ignore - public clone(): Collection; - // @ts-ignore - public concat(...collections: Collection[]): Collection; - // @ts-ignore - public sorted(compareFunction: (firstValue: V, secondValue: V, firstKey: K, secondKey: K) => number): Collection; + public toJSON(): object; } - export class GuildEmojiRoleStore extends OverridableDataStore { + export class LimitedCollection extends Collection { + public constructor(maxSize: number, iterable: Iterable); + public maxSize: number; + } + +//#endregion + +//#region Managers + + export class ChannelManager extends BaseManager { + constructor(client: Client, iterable: Iterable); + public fetch(id: Snowflake, cache?: boolean): Promise; + } + + export abstract class BaseManager { + constructor(client: Client, iterable: Iterable, holds: Constructable, cacheType: Collection); + public holds: Constructable; + public cache: Collection; + public cacheType: Collection; + public readonly client: Client; + public add(data: any, cache?: boolean, { id, extras }?: { id: K, extras: any[] }): Holds; + public remove(key: K): void; + public resolve(resolvable: R): Holds | null; + public resolveID(resolvable: R): K | null; + } + + export class GuildEmojiRoleManager { constructor(emoji: GuildEmoji); + public emoji: GuildEmoji; + public guild: Guild; + public cache: Collection; public add(roleOrRoles: RoleResolvable | RoleResolvable[] | Collection): Promise; public set(roles: RoleResolvable[] | Collection): Promise; public remove(roleOrRoles: RoleResolvable | RoleResolvable[] | Collection): Promise; } - export class GuildEmojiStore extends DataStore { + export class GuildEmojiManager extends BaseManager { constructor(guild: Guild, iterable?: Iterable); + public guild: Guild; public create(attachment: BufferResolvable | Base64Resolvable, name: string, options?: GuildEmojiCreateOptions): Promise; public resolveIdentifier(emoji: EmojiIdentifierResolvable): string | null; } - export class GuildChannelStore extends DataStore { + export class GuildChannelManager extends BaseManager { constructor(guild: Guild, iterable?: Iterable); + public guild: Guild; public create(name: string, options: GuildCreateChannelOptions & { type: 'voice' }): Promise; public create(name: string, options: GuildCreateChannelOptions & { type: 'category' }): Promise; public create(name: string, options?: GuildCreateChannelOptions & { type?: 'text' }): Promise; @@ -1814,78 +1804,87 @@ declare module 'discord.js' { } // Hacky workaround because changing the signature of an overridden method errors - class OverridableDataStore, R = any> extends DataStore { + class OverridableManager extends BaseManager { public add(data: any, cache: any): any; public set(key: any): any; } - export class GuildMemberRoleStore extends OverridableDataStore { + export class GuildMemberRoleManager extends OverridableManager { constructor(member: GuildMember); public readonly hoist: Role | null; public readonly color: Role | null; public readonly highest: Role; + public member: GuildMember; + public guild: Guild; public add(roleOrRoles: RoleResolvable | RoleResolvable[] | Collection, reason?: string): Promise; public set(roles: RoleResolvable[] | Collection, reason?: string): Promise; public remove(roleOrRoles: RoleResolvable | RoleResolvable[] | Collection, reason?: string): Promise; } - export class GuildMemberStore extends DataStore { + export class GuildMemberManager extends BaseManager { constructor(guild: Guild, iterable?: Iterable); + public guild: Guild; public ban(user: UserResolvable, options?: BanOptions): Promise; public fetch(options: UserResolvable | FetchMemberOptions): Promise; - public fetch(): Promise; + public fetch(): Promise; public fetch(options: FetchMembersOptions): Promise>; public prune(options: GuildPruneMembersOptions & { dry?: false, count: false }): Promise; public prune(options?: GuildPruneMembersOptions): Promise; public unban(user: UserResolvable, reason?: string): Promise; } - export class GuildStore extends DataStore { + export class GuildManager extends BaseManager { constructor(client: Client, iterable?: Iterable); public create(name: string, options?: { region?: string, icon: BufferResolvable | Base64Resolvable | null }): Promise; } - export class MessageStore extends DataStore { + export class MessageManager extends BaseManager { constructor(channel: TextChannel | DMChannel, iterable?: Iterable); + public channel: TextBasedChannelFields; + public cache: LimitedCollection; public fetch(message: Snowflake, cache?: boolean): Promise; public fetch(options?: ChannelLogsQueryOptions, cache?: boolean): Promise>; public fetchPinned(cache?: boolean): Promise>; - public remove(message: MessageResolvable, reason?: string): Promise; + public delete(message: MessageResolvable, reason?: string): Promise; } - export class PresenceStore extends DataStore { + export class PresenceManager extends BaseManager { constructor(client: Client, iterable?: Iterable); } - export class ReactionStore extends DataStore { + export class ReactionManager extends BaseManager { constructor(message: Message, iterable?: Iterable); + public message: Message; public removeAll(): Promise; } - export class ReactionUserStore extends DataStore { + export class ReactionUserManager extends BaseManager { constructor(client: Client, iterable: Iterable | undefined, reaction: MessageReaction); + public reaction: MessageReaction; public fetch(options?: { limit?: number, after?: Snowflake, before?: Snowflake }): Promise>; public remove(user?: UserResolvable): Promise; } - export class RoleStore extends DataStore { + export class RoleManager extends BaseManager { constructor(guild: Guild, iterable?: Iterable); public readonly everyone: Role | null; public readonly highest: Role; + public guild: Guild; public create(options?: { data?: RoleData, reason?: string }): Promise; public fetch(id: Snowflake, cache?: boolean): Promise; public fetch(id?: Snowflake, cache?: boolean): Promise; } - export class UserStore extends DataStore { + export class UserManager extends BaseManager { constructor(client: Client, iterable?: Iterable); public fetch(id: Snowflake, cache?: boolean): Promise; } - export class VoiceStateStore extends DataStore { + export class VoiceStateManager extends BaseManager { constructor(guild: Guild, iterable?: Iterable); + public guild: Guild; } //#endregion