diff --git a/src/client/Client.js b/src/client/Client.js index bb015efd6..dfbd5c37b 100644 --- a/src/client/Client.js +++ b/src/client/Client.js @@ -7,6 +7,7 @@ const ClientManager = require('./ClientManager'); const ClientDataResolver = require('./ClientDataResolver'); const WebSocketManager = require('./websocket/WebSocketManager'); const ActionsManager = require('./actions/ActionsManager'); +const Collection = require('../util/Collection'); /** * The starting point for making a Discord Bot. @@ -59,20 +60,20 @@ class Client extends EventEmitter { this.actions = new ActionsManager(this); /** - * A map of the Client's stored users - * @type {Map} + * A Collection of the Client's stored users + * @type {Collection} */ - this.users = new Map(); + this.users = new Collection(); /** - * A map of the Client's stored guilds - * @type {Map} + * A Collection of the Client's stored guilds + * @type {Collection} */ - this.guilds = new Map(); + this.guilds = new Collection(); /** - * A map of the Client's stored channels - * @type {Map} + * A Collection of the Client's stored channels + * @type {Collection} */ - this.channels = new Map(); + this.channels = new Collection(); /** * The authorization token for the logged in user/bot. * @type {?String} diff --git a/src/structures/DMChannel.js b/src/structures/DMChannel.js index 00c9b8dfb..fca9c5e1d 100644 --- a/src/structures/DMChannel.js +++ b/src/structures/DMChannel.js @@ -1,6 +1,7 @@ const Channel = require('./Channel'); const TextBasedChannel = require('./interface/TextBasedChannel'); const User = require('./User'); +const Collection = require('../util/Collection'); /** * Represents a Direct Message Channel between two users. @@ -10,7 +11,7 @@ const User = require('./User'); class DMChannel extends Channel { constructor(client, data) { super(client, data); - this.messages = new Map(); + this.messages = new Collection(); } setup(data) { diff --git a/src/structures/Guild.js b/src/structures/Guild.js index d94a22ff8..a2279cf64 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -2,6 +2,7 @@ const User = require('./User'); const GuildMember = require('./GuildMember'); const Constants = require('../util/Constants'); const Role = require('./Role'); +const Collection = require('../util/Collection'); function arraysEqual(a, b) { if (a === b) return true; @@ -30,22 +31,22 @@ class Guild { this.client = client; /** - * A Map of members that are in this Guild. The key is the member's ID, the value is the member. - * @type {Map} + * A Collection of members that are in this Guild. The key is the member's ID, the value is the member. + * @type {Collection} */ - this.members = new Map(); + this.members = new Collection(); /** - * A Map of channels that are in this Guild. The key is the channel's ID, the value is the channel. - * @type {Map} + * A Collection of channels that are in this Guild. The key is the channel's ID, the value is the channel. + * @type {Collection} */ - this.channels = new Map(); + this.channels = new Collection(); /** - * A Map of roles that are in this Guild. The key is the role's ID, the value is the role. - * @type {Map} + * A Collection of roles that are in this Guild. The key is the role's ID, the value is the role. + * @type {Collection} */ - this.roles = new Map(); + this.roles = new Collection(); if (!data) { return; diff --git a/src/structures/GuildChannel.js b/src/structures/GuildChannel.js index 55c430e70..b8f6b196a 100644 --- a/src/structures/GuildChannel.js +++ b/src/structures/GuildChannel.js @@ -3,6 +3,7 @@ const PermissionOverwrites = require('./PermissionOverwrites'); const Role = require('./Role'); const EvaluatedPermissions = require('./EvaluatedPermissions'); const Constants = require('../util/Constants'); +const Collection = require('../util/Collection'); function arraysEqual(a, b) { if (a === b) return true; @@ -53,9 +54,9 @@ class GuildChannel extends Channel { this.ow = data.permission_overwrites; /** * A map of permission overwrites in this channel for roles and users. - * @type {Map} + * @type {Collection} */ - this.permissionOverwrites = new Map(); + this.permissionOverwrites = new Collection(); if (data.permission_overwrites) { for (const overwrite of data.permission_overwrites) { this.permissionOverwrites.set(overwrite.id, new PermissionOverwrites(this, overwrite)); diff --git a/src/structures/Message.js b/src/structures/Message.js index 07833f632..5faca1987 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -1,3 +1,4 @@ +const Collection = require('../util/Collection'); /** * Represents a Message on Discord */ @@ -79,16 +80,16 @@ class Message { */ this.attachments = data.attachments; /** - * An object containing a further users or roles map + * An object containing a further users, roles or channels collections * @type {Object} - * @property {Map} mentions.users Mentioned users, maps their ID to the user object. - * @property {Map} mentions.roles Mentioned roles, maps their ID to the role object. - * @property {Map} mentions.channels Mentioned channels, maps their ID to the channel object. + * @property {Collection} mentions.users Mentioned users, maps their ID to the user object. + * @property {Collection} mentions.roles Mentioned roles, maps their ID to the role object. + * @property {Collection} mentions.channels Mentioned channels, maps their ID to the channel object. */ this.mentions = { - users: new Map(), - roles: new Map(), - channels: new Map(), + users: new Collection(), + roles: new Collection(), + channels: new Collection(), }; /** * The ID of the message (unique in the channel it was sent) @@ -174,7 +175,7 @@ class Message { if (data.id) { this.id = data.id; } - if (this.channel.guild) { + if (this.channel.guild && data.content) { const channMentionsRaw = data.content.match(/<#([0-9]{14,20})>/g) || []; for (const raw of channMentionsRaw) { const chan = this.channel.guild.channels.get(raw.match(/([0-9]{14,20})/g)[0]); diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index d64fc7e89..f1d05415e 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -1,5 +1,6 @@ const GuildChannel = require('./GuildChannel'); const TextBasedChannel = require('./interface/TextBasedChannel'); +const Collection = require('../util/Collection'); /** * Represents a Server Text Channel on Discord. @@ -10,7 +11,7 @@ class TextChannel extends GuildChannel { constructor(guild, data) { super(guild, data); - this.messages = new Map(); + this.messages = new Collection(); } setup(data) { diff --git a/src/structures/VoiceChannel.js b/src/structures/VoiceChannel.js index eef063765..ebd60ef8a 100644 --- a/src/structures/VoiceChannel.js +++ b/src/structures/VoiceChannel.js @@ -1,4 +1,5 @@ const GuildChannel = require('./GuildChannel'); +const Collection = require('../util/Collection'); /** * Represents a Server Voice Channel on Discord. @@ -9,9 +10,9 @@ class VoiceChannel extends GuildChannel { super(guild, data); /** * The members in this Voice Channel. - * @type {Map} + * @type {Collection} */ - this.members = new Map(); + this.members = new Collection(); } setup(data) { diff --git a/src/structures/interface/TextBasedChannel.js b/src/structures/interface/TextBasedChannel.js index d96fd824c..3a03d6e85 100644 --- a/src/structures/interface/TextBasedChannel.js +++ b/src/structures/interface/TextBasedChannel.js @@ -1,3 +1,5 @@ +const Collection = require('../../util/Collection'); + /** * Interface for classes that have text-channel-like features * @interface @@ -6,10 +8,10 @@ class TextBasedChannel { constructor() { /** - * A Map containing the messages sent to this channel. - * @type {Map} + * A Collection containing the messages sent to this channel. + * @type {Collection} */ - this.messages = new Map(); + this.messages = new Collection(); } /** * Send a message to this channel diff --git a/src/util/Collection.js b/src/util/Collection.js new file mode 100644 index 000000000..66a7975dd --- /dev/null +++ b/src/util/Collection.js @@ -0,0 +1,109 @@ +/** + * A utility class to help make it easier to access the data stores + * @extends {Map} + */ +class Collection extends Map { + + /** + * Returns an ordered array of the values of this collection. + * @returns {Array} + * @example + * // identical to: + * Array.from(collection.values()); + */ + array() { + return Array.from(this.values()); + } + + /** + * Returns the first item in this collection. + * @returns {Object} + * @example + * // identical to: + * Array.from(collection.values())[0]; + */ + first() { + return this.array()[0]; + } + + /** + * Returns the last item in this collection. + * @returns {Object} + */ + last() { + const arr = this.array(); + return arr[arr.length - 1]; + } + + /** + * Returns a random item from this collection. + * @returns {Object} + */ + random() { + const arr = this.array(); + return arr[Math.floor(Math.random() * arr.length)]; + } + + /** + * The length (size) of this collection. + * @readonly + */ + get length() { + return this.size; + } + + /** + * Returns an array of items where `item[key] === value` of the collection + * @param {String} key the key to filter bby + * @param {any} value the expected value + * @returns {Array} + * @example + * collection.getAll('username', 'Bob'); + */ + findAll(key, value) { + const results = []; + for (const item of this.array()) { + if (item[key] === value) { + results.push(item); + } + } + return results; + } + + /** + * Returns a single item where `item[key] === value` + * @param {String} key the key to filter bby + * @param {any} value the expected value + * @returns {Object} + * @example + * collection.get('id', '123123...'); + */ + find(key, value) { + for (const item of this.array()) { + if (item[key] === value) { + return item; + } + } + return null; + } + + /** + * Returns true if the collection has an item where `item[key] === value` + * @param {String} key the key to filter bby + * @param {any} value the expected value + * @returns {Object} + * @example + * if (collection.exists('id', '123123...')) { + * console.log('user here!'); + * } + */ + exists(key, value) { + return Boolean(this.get(key, value)); + } + + _arrayMethod(method, args) { + return Array.prototype[method].apply(this.array(), args); + } +} + +module.exports = Collection;