diff --git a/src/client/websocket/packets/handlers/TypingStart.js b/src/client/websocket/packets/handlers/TypingStart.js index faaa49865..438641068 100644 --- a/src/client/websocket/packets/handlers/TypingStart.js +++ b/src/client/websocket/packets/handlers/TypingStart.js @@ -9,21 +9,13 @@ class TypingStartHandler extends AbstractHandler { const user = client.users.get(data.user_id); const timestamp = new Date(data.timestamp * 1000); - function tooLate() { - return client.setTimeout(() => { - client.emit(Constants.Events.TYPING_STOP, channel, user, channel.typingMap[user.id]); - delete channel.typingMap[user.id]; - }, 6000); - } - if (channel && user) { - if (channel.typingMap[user.id]) { - // already typing, renew - const mapping = channel.typingMap[user.id]; - mapping.lastTimestamp = timestamp; - mapping.resetTimeout(tooLate()); + if (channel._typing.has(user.id)) { + const typing = channel._typing.get(user.id); + typing.lastTimestamp = timestamp; + typing.resetTimeout(tooLate(channel, user)); } else { - channel.typingMap[user.id] = new TypingData(timestamp, timestamp, tooLate()); + channel._typing.set(user.id, new TypingData(timestamp, timestamp, tooLate(channel, user))); client.emit(Constants.Events.TYPING_START, channel, user); } } @@ -47,6 +39,13 @@ class TypingData { } } +function tooLate(channel, user) { + return channel.client.setTimeout(() => { + channel.client.emit(Constants.Events.TYPING_STOP, channel, user, channel._typing[user.id]); + delete channel._typing[user.id]; + }, 6000); +} + /** * Emitted whenever a user starts typing in a channel * @event Client#typingStart diff --git a/src/structures/Channel.js b/src/structures/Channel.js index 8d174c2e1..311c7be40 100644 --- a/src/structures/Channel.js +++ b/src/structures/Channel.js @@ -8,8 +8,8 @@ class Channel { * @type {Client} */ this.client = client; - this.typingMap = {}; - this.typingTimeouts = []; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + /** * The type of the channel, either: * * `dm` - a DM channel @@ -19,6 +19,8 @@ class Channel { * @type {string} */ this.type = null; + + this._typing = new Map(); if (data) this.setup(data); } diff --git a/src/structures/ClientUser.js b/src/structures/ClientUser.js index 487e8c540..049498ac9 100644 --- a/src/structures/ClientUser.js +++ b/src/structures/ClientUser.js @@ -7,11 +7,13 @@ const User = require('./User'); class ClientUser extends User { setup(data) { super.setup(data); + /** * Whether or not this account has been verified * @type {boolean} */ this.verified = data.verified; + /** * The email of this account * @type {string} diff --git a/src/structures/DMChannel.js b/src/structures/DMChannel.js index 0ef597473..6f10fcbbd 100644 --- a/src/structures/DMChannel.js +++ b/src/structures/DMChannel.js @@ -16,18 +16,15 @@ class DMChannel extends Channel { setup(data) { super.setup(data); - const recipient = this.client.users.get(data.recipients[0].id) || new User(this.client, data.recipients[0]); + /** * The recipient on the other end of the DM * @type {User} */ - this.recipient = recipient; - /** - * The ID of the last sent message, if available - * @type {?string} - */ - this.lastMessageID = data.last_message_id; + this.recipient = this.client.users.get(data.recipients[0].id) || new User(this.client, data.recipients[0]); + this.type = 'dm'; + this.lastMessageID = data.last_message_id; } /** diff --git a/src/structures/Emoji.js b/src/structures/Emoji.js index 08f1b20ce..626b5e44d 100644 --- a/src/structures/Emoji.js +++ b/src/structures/Emoji.js @@ -11,11 +11,14 @@ class Emoji { * @type {Client} */ this.client = guild.client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + /** * The Guild this emoji is part of * @type {Guild} */ this.guild = guild; + this.setup(data); } @@ -25,22 +28,26 @@ class Emoji { * @type {string} */ this.id = data.id; + /** * The name of the Emoji * @type {string} */ this.name = data.name; - this.roleIDS = data.roles; + /** * Whether or not this emoji requires colons surrounding it * @type {boolean} */ this.requiresColons = data.require_colons; + /** * Whether this emoji is managed by an external service * @type {boolean} */ this.managed = data.managed; + + this._roles = data.roles; } /** @@ -59,8 +66,8 @@ class Emoji { */ get roles() { const roles = new Collection(); - for (const role of this.roleIDS) { - if (this.guild.roles.get(role)) roles.set(role, this.guild.roles.get(role)); + for (const role of this._roles) { + if (this.guild.roles.has(role)) roles.set(role, this.guild.roles.get(role)); } return roles; } diff --git a/src/structures/EvaluatedPermissions.js b/src/structures/EvaluatedPermissions.js index 0c66cd437..7d5dd7323 100644 --- a/src/structures/EvaluatedPermissions.js +++ b/src/structures/EvaluatedPermissions.js @@ -10,6 +10,7 @@ class EvaluatedPermissions { * @type {GuildMember} */ this.member = member; + /** * A number representing the packed permissions. * @private diff --git a/src/structures/GroupDMChannel.js b/src/structures/GroupDMChannel.js index bea1ab4c3..aad39d547 100644 --- a/src/structures/GroupDMChannel.js +++ b/src/structures/GroupDMChannel.js @@ -36,25 +36,27 @@ class GroupDMChannel extends Channel { this.messages = new Collection(); } - equals(other) { - const equal = other && - this.id === other.id && - this.name === other.name && - this.icon === other.icon && - this.owner.id === other.owner_id; - - if (equal) { - const thisIDs = this.recipients.array().map(r => r.id); - const otherIDs = other.recipients.map(r => r.id); - return arraysEqual(thisIDs, otherIDs); - } - - return equal; - } - setup(data) { super.setup(data); + /** + * The name of this Group DM, can be null if one isn't set. + * @type {string} + */ + this.name = data.name; + + /** + * A hash of the Group DM icon. + * @type {string} + */ + this.icon = data.icon; + + /** + * The owner of this Group DM. + * @type {User} + */ + this.owner = this.client.users.get(data.owner_id); + if (!this.recipients) { /** * A collection of the recipients of this DM, mapped by their ID. @@ -70,32 +72,25 @@ class GroupDMChannel extends Channel { } } - /** - * The name of this Group DM, can be null if one isn't set. - * @type {string} - */ - this.name = data.name; - /** - * The ID of this Group DM Channel. - * @type {string} - */ this.id = data.id; - /** - * A hash of the Group DM icon. - * @type {string} - */ - this.icon = data.icon; - /** - * The ID of the last message in the channel, if one was sent. - * @type {?string} - */ - this.lastMessageID = data.last_message_id; - /** - * The owner of this Group DM. - * @type {User} - */ - this.owner = this.client.users.get(data.owner_id); this.type = 'group'; + this.lastMessageID = data.last_message_id; + } + + equals(other) { + const equal = other && + this.id === other.id && + this.name === other.name && + this.icon === other.icon && + this.owner.id === other.owner_id; + + if (equal) { + const thisIDs = this.recipients.array().map(r => r.id); + const otherIDs = other.recipients.map(r => r.id); + return arraysEqual(thisIDs, otherIDs); + } + + return equal; } sendMessage() { diff --git a/src/structures/Guild.js b/src/structures/Guild.js index 72c8ca336..ca17f2a49 100644 --- a/src/structures/Guild.js +++ b/src/structures/Guild.js @@ -19,6 +19,7 @@ class Guild { * @type {Client} */ this.client = client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); /** * A Collection of members that are in this Guild. The key is the member's ID, the value is the member. @@ -39,13 +40,13 @@ class Guild { this.roles = new Collection(); if (!data) return; - if (data.unavailable) { /** * Whether the Guild is available to access. If it is not available, it indicates a server outage. * @type {boolean} */ this.available = false; + /** * The Unique ID of the Guild, useful for comparisons. * @type {string} @@ -57,6 +58,138 @@ class Guild { } } + /** + * Sets up the Guild + * @param {*} data The raw data of the guild + * @private + */ + setup(data) { + /** + * The name of the guild + * @type {string} + */ + this.name = data.name; + + /** + * The hash of the guild icon, or null if there is no icon. + * @type {?string} + */ + this.icon = data.icon; + + /** + * The hash of the guild splash image, or null if no splash (VIP only) + * @type {?string} + */ + this.splash = data.splash; + + /** + * The region the guild is located in + * @type {string} + */ + this.region = data.region; + + /** + * The full amount of members in this Guild as of `READY` + * @type {number} + */ + this.memberCount = data.member_count; + + /** + * Whether the guild is "large" (has more than 250 members) + * @type {boolean} + */ + this.large = data.large; + + /** + * An array of guild features. + * @type {Object[]} + */ + this.features = data.features; + + /** + * An array of guild emojis. + * @type {Object[]} + */ + this.emojis = new Collection(); + for (const emoji of data.emojis) this.emojis.set(emoji.id, new Emoji(this, emoji)); + + /** + * The time in seconds before a user is counted as "away from keyboard". + * @type {?number} + */ + this.afkTimeout = data.afk_timeout; + + /** + * The ID of the voice channel where AFK members are moved. + * @type {?string} + */ + this.afkChannelID = data.afk_channel_id; + + /** + * Whether embedded images are enabled on this guild. + * @type {boolean} + */ + this.embedEnabled = data.embed_enabled; + + /** + * The verification level of the guild. + * @type {number} + */ + this.verificationLevel = data.verification_level; + + this.id = data.id; + this.available = !data.unavailable; + this.features = data.features || []; + this._joinDate = new Date(data.joined_at).getTime(); + + if (data.members) { + this.members.clear(); + for (const guildUser of data.members) this._addMember(guildUser); + } + + if (data.owner_id) this.ownerID = data.owner_id; + + if (data.channels) { + this.channels.clear(); + for (const channel of data.channels) this.client.dataManager.newChannel(channel, this); + } + + if (data.roles) { + this.roles.clear(); + for (const role of data.roles) { + const newRole = new Role(this, role); + this.roles.set(newRole.id, newRole); + } + } + + if (data.presences) { + for (const presence of data.presences) { + const user = this.client.users.get(presence.user.id); + if (user) { + user.status = presence.status; + user.game = presence.game; + } + } + } + + this._rawVoiceStates = new Collection(); + if (data.voice_states) { + for (const voiceState of data.voice_states) { + this._rawVoiceStates.set(voiceState.user_id, voiceState); + const member = this.members.get(voiceState.user_id); + if (member) { + member.serverMute = voiceState.mute; + member.serverDeaf = voiceState.deaf; + member.selfMute = voiceState.self_mute; + member.selfDeaf = voiceState.self_deaf; + member.voiceSessionID = voiceState.session_id; + member.voiceChannelID = voiceState.channel_id; + this.channels.get(voiceState.channel_id).members.set(member.user.id, member); + } + } + } + } + _checkChunks() { if (this._fetchWaiter) { if (this.members.size === this.memberCount) { @@ -83,6 +216,7 @@ class Guild { member.voiceChannelID = voiceState.channel_id; this.channels.get(voiceState.channel_id).members.set(member.user.id, member); } + /** * Emitted whenever a user joins a guild. * @event Client#guildMemberAdd @@ -201,127 +335,6 @@ class Guild { } } - /** - * Sets up the Guild - * @param {*} data The raw data of the guild - * @private - */ - setup(data) { - this.id = data.id; - this.available = !data.unavailable; - /** - * The hash of the guild splash image, or null if no splash (VIP only) - * @type {?string} - */ - this.splash = data.splash; - /** - * The region the guild is located in - * @type {string} - */ - this.region = data.region; - /** - * The name of the guild - * @type {string} - */ - this.name = data.name; - /** - * The full amount of members in this Guild as of `READY` - * @type {number} - */ - this.memberCount = data.member_count; - /** - * Whether the guild is "large" (has more than 250 members) - * @type {boolean} - */ - this.large = data.large; - this._joinDate = new Date(data.joined_at).getTime(); - /** - * The hash of the guild icon, or null if there is no icon. - * @type {?string} - */ - this.icon = data.icon; - /** - * An array of guild features. - * @type {Object[]} - */ - this.features = data.features; - /** - * An array of guild emojis. - * @type {Object[]} - */ - this.emojis = new Collection(); - for (const emoji of data.emojis) this.emojis.set(emoji.id, new Emoji(this, emoji)); - /** - * The time in seconds before a user is counted as "away from keyboard". - * @type {?number} - */ - this.afkTimeout = data.afk_timeout; - /** - * The ID of the voice channel where AFK members are moved. - * @type {?string} - */ - this.afkChannelID = data.afk_channel_id; - /** - * Whether embedded images are enabled on this guild. - * @type {boolean} - */ - this.embedEnabled = data.embed_enabled; - /** - * The verification level of the guild. - * @type {number} - */ - this.verificationLevel = data.verification_level; - this.features = data.features || []; - - if (data.members) { - this.members.clear(); - for (const guildUser of data.members) this._addMember(guildUser); - } - - if (data.owner_id) this.ownerID = data.owner_id; - - if (data.channels) { - this.channels.clear(); - for (const channel of data.channels) this.client.dataManager.newChannel(channel, this); - } - - if (data.roles) { - this.roles.clear(); - for (const role of data.roles) { - const newRole = new Role(this, role); - this.roles.set(newRole.id, newRole); - } - } - - if (data.presences) { - for (const presence of data.presences) { - const user = this.client.users.get(presence.user.id); - if (user) { - user.status = presence.status; - user.game = presence.game; - } - } - } - - this._rawVoiceStates = new Collection(); - - if (data.voice_states) { - for (const voiceState of data.voice_states) { - this._rawVoiceStates.set(voiceState.user_id, voiceState); - const member = this.members.get(voiceState.user_id); - if (member) { - member.serverMute = voiceState.mute; - member.serverDeaf = voiceState.deaf; - member.selfMute = voiceState.self_mute; - member.selfDeaf = voiceState.self_deaf; - member.voiceSessionID = voiceState.session_id; - member.voiceChannelID = voiceState.channel_id; - this.channels.get(voiceState.channel_id).members.set(member.user.id, member); - } - } - } - } - /** * The time the guild was created * @readonly diff --git a/src/structures/GuildChannel.js b/src/structures/GuildChannel.js index 86ba354b5..aa2bfd739 100644 --- a/src/structures/GuildChannel.js +++ b/src/structures/GuildChannel.js @@ -13,6 +13,7 @@ const arraysEqual = require('../util/ArraysEqual'); class GuildChannel extends Channel { constructor(guild, data) { super(guild.client, data); + /** * The guild the channel is in * @type {Guild} @@ -22,17 +23,19 @@ class GuildChannel extends Channel { setup(data) { super.setup(data); - /** - * The position of the channel in the list. - * @type {number} - */ - this.position = data.position; + /** * The name of the Guild Channel * @type {string} */ this.name = data.name; - this.ow = data.permission_overwrites; + + /** + * The position of the channel in the list. + * @type {number} + */ + this.position = data.position; + /** * A map of permission overwrites in this channel for roles and users. * @type {Collection} @@ -53,11 +56,11 @@ class GuildChannel extends Channel { */ equals(channel) { let equal = channel && + this.id === channel.id && this.type === channel.type && this.topic === channel.topic && this.position === channel.position && - this.name === channel.name && - this.id === channel.id; + this.name === channel.name; if (equal) { if (channel.permission_overwrites) { diff --git a/src/structures/GuildMember.js b/src/structures/GuildMember.js index dad29af91..873c66311 100644 --- a/src/structures/GuildMember.js +++ b/src/structures/GuildMember.js @@ -13,64 +13,76 @@ class GuildMember { * @type {Client} */ this.client = guild.client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + /** * The guild that this member is part of * @type {Guild} */ this.guild = guild; + /** * The user that this guild member instance Represents * @type {User} */ this.user = {}; + this._roles = []; if (data) this.setup(data); } setup(data) { - this.user = data.user; /** * Whether this member is deafened server-wide * @type {boolean} */ this.serverDeaf = data.deaf; + /** * Whether this member is muted server-wide * @type {boolean} */ this.serverMute = data.mute; + /** * Whether this member is self-muted * @type {boolean} */ this.selfMute = data.self_mute; + /** * Whether this member is self-deafened * @type {boolean} */ this.selfDeaf = data.self_deaf; + /** * The voice session ID of this member, if any * @type {?string} */ this.voiceSessionID = data.session_id; + /** * The voice channel ID of this member, if any * @type {?string} */ this.voiceChannelID = data.channel_id; - this._joinDate = new Date(data.joined_at).getTime(); + /** * Whether this meember is speaking * @type {?boolean} */ this.speaking = this.speaking; + /** * The nickname of this Guild Member, if they have one * @type {?string} */ this.nickname = data.nick; + + this.user = data.user; this._roles = data.roles; + this._joinDate = new Date(data.joined_at).getTime(); } /** diff --git a/src/structures/Invite.js b/src/structures/Invite.js index 1c09e6425..977d72ee0 100644 --- a/src/structures/Invite.js +++ b/src/structures/Invite.js @@ -32,22 +32,24 @@ class Invite { * @type {Client} */ this.client = client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + this.setup(data); } setup(data) { /** - * The maximum age of the invite, in seconds - * @type {?number} + * The Guild the invite is for. If this Guild is already known, this will be a Guild object. If the Guild is + * unknown, this will be a Partial Guild. + * @type {Guild|PartialGuild} */ - this.maxAge = data.max_age; + this.guild = this.client.guilds.get(data.guild.id) || new PartialGuild(this.client, data.guild); /** * The code for this invite * @type {string} */ this.code = data.code; - this._creationDate = new Date(data.created_at).getTime(); /** * Whether or not this invite is temporary @@ -55,6 +57,12 @@ class Invite { */ this.temporary = data.temporary; + /** + * The maximum age of the invite, in seconds + * @type {?number} + */ + this.maxAge = data.max_age; + /** * How many times this invite has been used * @type {number} @@ -73,19 +81,22 @@ class Invite { */ this.inviter = this.client.dataManager.newUser(data.inviter); - /** - * The Guild the invite is for. If this Guild is already known, this will be a Guild object. If the Guild is - * unknown, this will be a Partial Guild. - * @type {Guild|PartialGuild} - */ - this.guild = this.client.guilds.get(data.guild.id) || new PartialGuild(this.client, data.guild); - /** * The Channel the invite is for. If this Channel is already known, this will be a GuildChannel object. * If the Channel is unknown, this will be a Partial Guild Channel. * @type {GuildChannel|PartialGuildChannel} */ - this.channels = this.client.channels.get(data.channel.id) || new PartialGuildChannel(this.client, data.channel); + this.channel = this.client.channels.get(data.channel.id) || new PartialGuildChannel(this.client, data.channel); + + this._createdAt = new Date(data.created_at).getTime(); + } + + /** + * The creation date of the invite + * @type {Date} + */ + get createdAt() { + return new Date(this._createdAt); } /** @@ -93,7 +104,7 @@ class Invite { * @type {Date} */ get creationDate() { - return new Date(this._creationDate); + return new Date(this._createdAt); } /** diff --git a/src/structures/Message.js b/src/structures/Message.js index 039e992bc..e7d6ce797 100644 --- a/src/structures/Message.js +++ b/src/structures/Message.js @@ -7,76 +7,91 @@ const Collection = require('../util/Collection'); */ class Message { constructor(channel, data, client) { - this._type = 'message'; + /** + * The client that instantiated the Message + * @type {Client} + */ + this.client = client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + /** * The channel that the message was sent in * @type {TextChannel|DMChannel|GroupDMChannel} */ this.channel = channel; - if (channel.guild) { - /** - * If the message was sent in a guild, this will be the guild the message was sent in - * @type {?Guild} - */ - this.guild = channel.guild; - } - /** - * The client that instantiated the Message - * @type {Client} + * If the message was sent in a guild, this will be the guild the message was sent in + * @type {?Guild} */ - this.client = client; + this.guild = channel.guild || null; + if (data) this.setup(data); } setup(data) { /** - * Whether or not this message is pinned - * @type {boolean} + * The ID of the message (unique in the channel it was sent) + * @type {string} */ - this.pinned = data.pinned; - /** - * The author of the message - * @type {User} - */ - this.author = this.client.dataManager.newUser(data.author); - if (this.guild) { - /** - * Represents the Author of the message as a Guild Member. Only available if the message comes from a Guild - * where the author is still a member. - * @type {GuildMember} - */ - this.member = this.guild.member(this.author); - } + this.id = data.id; + /** * The content of the message * @type {string} */ this.content = data.content; - this._timestamp = new Date(data.timestamp).getTime(); - this._editedTimestamp = data.edited_timestamp ? new Date(data.edited_timestamp).getTime() : null; + + /** + * The author of the message + * @type {User} + */ + this.author = this.client.dataManager.newUser(data.author); + + /** + * Represents the Author of the message as a Guild Member. Only available if the message comes from a Guild + * where the author is still a member. + * @type {GuildMember} + */ + this.member = this.guild ? this.guild.member(this.author) || null : null; + + /** + * Whether or not this message is pinned + * @type {boolean} + */ + this.pinned = data.pinned; + /** * Whether or not the message was Text-To-Speech * @type {boolean} */ this.tts = data.tts; + /** * A random number used for checking message delivery * @type {string} */ this.nonce = data.nonce; + + /** + * Whether or not this message was sent by Discord, not actually a user (e.g. pin notifications) + * @type {boolean} + */ + this.system = data.type === 6; + /** * A list of embeds in the message - e.g. YouTube Player * @type {Embed[]} */ this.embeds = data.embeds.map(e => new Embed(this, e)); + /** * A collection of attachments in the message - e.g. Pictures - mapped by their ID. * @type {Collection} */ this.attachments = new Collection(); for (const attachment of data.attachments) this.attachments.set(attachment.id, new Attachment(this, attachment)); + /** * An object containing a further users, roles or channels collections * @type {Object} @@ -92,11 +107,6 @@ class Message { channels: new Collection(), everyone: data.mention_everyone, }; - /** - * The ID of the message (unique in the channel it was sent) - * @type {string} - */ - this.id = data.id; for (const mention of data.mentions) { let user = this.client.users.get(mention.id); @@ -123,14 +133,9 @@ class Message { } } + this._timestamp = new Date(data.timestamp).getTime(); + this._editedTimestamp = data.edited_timestamp ? new Date(data.edited_timestamp).getTime() : null; this._edits = []; - - /** - * Whether or not this message was sent by Discord, not actually a user (e.g. pin notifications) - * @type {boolean} - */ - this.system = false; - if (data.type === 6) this.system = true; } /** diff --git a/src/structures/MessageAttachment.js b/src/structures/MessageAttachment.js index ad74479e8..a72518ec6 100644 --- a/src/structures/MessageAttachment.js +++ b/src/structures/MessageAttachment.js @@ -8,6 +8,7 @@ class MessageAttachment { * @type {Client} */ this.client = message.client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); /** * The message this attachment is part of. * @type {Message} diff --git a/src/structures/MessageEmbed.js b/src/structures/MessageEmbed.js index f844ff125..689098b22 100644 --- a/src/structures/MessageEmbed.js +++ b/src/structures/MessageEmbed.js @@ -13,6 +13,7 @@ class MessageEmbed { * @type {Client} */ this.client = message.client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); this.setup(data); } diff --git a/src/structures/PartialGuild.js b/src/structures/PartialGuild.js index ac6334abd..d605cc274 100644 --- a/src/structures/PartialGuild.js +++ b/src/structures/PartialGuild.js @@ -15,30 +15,35 @@ class PartialGuild { * @type {Client} */ this.client = client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + this.setup(data); } setup(data) { - /** - * The hash of the guild splash image, or null if no splash (VIP only) - * @type {?string} - */ - this.splash = data.splash; /** * The ID of this guild * @type {string} */ this.id = data.id; - /** - * The hash of this guild's icon, or null if there is none. - * @type {?string} - */ - this.icon = data.icon; + /** * The name of this guild * @type {string} */ this.name = data.name; + + /** + * The hash of this guild's icon, or null if there is none. + * @type {?string} + */ + this.icon = data.icon; + + /** + * The hash of the guild splash image, or null if no splash (VIP only) + * @type {?string} + */ + this.splash = data.splash; } } diff --git a/src/structures/PartialGuildChannel.js b/src/structures/PartialGuildChannel.js index 01a5ae73d..47e33884d 100644 --- a/src/structures/PartialGuildChannel.js +++ b/src/structures/PartialGuildChannel.js @@ -14,6 +14,8 @@ class PartialGuildChannel { * @type {Client} */ this.client = client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + this.setup(data); } @@ -23,11 +25,13 @@ class PartialGuildChannel { * @type {string} */ this.id = data.id; + /** * The name of this Guild Channel * @type {string} */ this.name = data.name; + /** * The type of this Guild Channel - `text` or `voice` * @type {string} diff --git a/src/structures/PermissionOverwrites.js b/src/structures/PermissionOverwrites.js index 7c31c7fc2..b1b2944fc 100644 --- a/src/structures/PermissionOverwrites.js +++ b/src/structures/PermissionOverwrites.js @@ -8,20 +8,23 @@ class PermissionOverwrites { * @type {GuildChannel} */ this.channel = guildChannel; + if (data) this.setup(data); } setup(data) { - /** - * The type of this overwrite - * @type {string} - */ - this.type = data.type; /** * The ID of this overwrite, either a User ID or a Role ID * @type {string} */ this.id = data.id; + + /** + * The type of this overwrite + * @type {string} + */ + this.type = data.type; + this.denyData = data.deny; this.allowData = data.allow; } diff --git a/src/structures/Role.js b/src/structures/Role.js index a47cbbef2..1954b843c 100644 --- a/src/structures/Role.js +++ b/src/structures/Role.js @@ -5,19 +5,66 @@ const Constants = require('../util/Constants'); */ class Role { constructor(guild, data) { - /** - * The guild that the role belongs to - * @type {Guild} - */ - this.guild = guild; /** * The client that instantiated the role * @type {Client} */ this.client = guild.client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + + /** + * The guild that the role belongs to + * @type {Guild} + */ + this.guild = guild; + if (data) this.setup(data); } + setup(data) { + /** + * The ID of the role (unique to the guild it is part of) + * @type {string} + */ + this.id = data.id; + + /** + * The name of the role + * @type {string} + */ + this.name = data.name; + + /** + * The base 10 color of the role + * @type {number} + */ + this.color = data.color; + + /** + * If true, users that are part of this role will appear in a separate category in the users list + * @type {boolean} + */ + this.hoist = data.hoist; + + /** + * The position of the role in the role manager + * @type {number} + */ + this.position = data.position; + + /** + * The evaluated permissions number + * @type {number} + */ + this.permissions = data.permissions; + + /** + * Whether or not the role is managed by an external service + * @type {boolean} + */ + this.managed = data.managed; + } + equals(role) { return role && this.id === role.id && @@ -29,44 +76,6 @@ class Role { this.managed === role.managed; } - setup(data) { - /** - * The ID of the role (unique to the guild it is part of) - * @type {string} - */ - this.id = data.id; - /** - * The name of the role - * @type {string} - */ - this.name = data.name; - /** - * The base 10 color of the role - * @type {number} - */ - this.color = data.color; - /** - * If true, users that are part of this role will appear in a separate category in the users list - * @type {boolean} - */ - this.hoist = data.hoist; - /** - * The position of the role in the role manager - * @type {number} - */ - this.position = data.position; - /** - * The evaluated permissions number - * @type {number} - */ - this.permissions = data.permissions; - /** - * Whether or not the role is managed by an external service - * @type {boolean} - */ - this.managed = data.managed; - } - /** * The time the role was created * @readonly diff --git a/src/structures/TextChannel.js b/src/structures/TextChannel.js index b992f9a20..49df7d28b 100644 --- a/src/structures/TextChannel.js +++ b/src/structures/TextChannel.js @@ -15,16 +15,19 @@ class TextChannel extends GuildChannel { setup(data) { super.setup(data); + /** * The topic of the Guild Channel, if there is one. * @type {?string} */ this.topic = data.topic; + /** * The ID of the last message in the channel, if one was sent. * @type {?string} */ this.lastMessageID = data.last_message_id; + this.type = 'text'; } diff --git a/src/structures/User.js b/src/structures/User.js index a2ae237d7..1e4e5372e 100644 --- a/src/structures/User.js +++ b/src/structures/User.js @@ -7,36 +7,47 @@ const Constants = require('../util/Constants'); */ class User { constructor(client, data) { + /** + * The Client that created the instance of the the User. + * @type {Client} + */ this.client = client; + Object.defineProperty(this, 'client', { enumerable: false, configurable: false }); + if (data) this.setup(data); } setup(data) { - /** - * The username of the User - * @type {string} - */ - this.username = data.username; /** * The ID of the User * @type {string} */ this.id = data.id; + + /** + * The username of the User + * @type {string} + */ + this.username = data.username; + /** * A discriminator based on username for the User * @type {string} */ this.discriminator = data.discriminator; + /** * The ID of the user's avatar * @type {string} */ this.avatar = data.avatar; + /** * Whether or not the User is a Bot. * @type {boolean} */ this.bot = Boolean(data.bot); + /** * The status of the user: * @@ -46,6 +57,7 @@ class User { * @type {string} */ this.status = data.status || this.status || 'offline'; + /** * The game that the user is playing, `null` if they aren't playing a game. * @type {string} @@ -99,8 +111,8 @@ class User { */ equals(user) { let equal = user && - this.username === user.username && this.id === user.id && + this.username === user.username && this.discriminator === user.discriminator && this.avatar === user.avatar && this.bot === Boolean(user.bot); diff --git a/src/structures/VoiceChannel.js b/src/structures/VoiceChannel.js index b5174c3d6..ce1463457 100644 --- a/src/structures/VoiceChannel.js +++ b/src/structures/VoiceChannel.js @@ -8,6 +8,7 @@ const Collection = require('../util/Collection'); class VoiceChannel extends GuildChannel { constructor(guild, data) { super(guild, data); + /** * The members in this Voice Channel. * @type {Collection} @@ -17,16 +18,19 @@ class VoiceChannel extends GuildChannel { setup(data) { super.setup(data); + /** * The bitrate of this voice channel * @type {number} */ this.bitrate = data.bitrate; + /** * The maximum amount of users allowed in this channel - 0 means unlimited. * @type {number} */ this.userLimit = data.user_limit; + this.type = 'voice'; } diff --git a/src/structures/interface/TextBasedChannel.js b/src/structures/interface/TextBasedChannel.js index d8629168e..68930a900 100644 --- a/src/structures/interface/TextBasedChannel.js +++ b/src/structures/interface/TextBasedChannel.js @@ -14,6 +14,12 @@ class TextBasedChannel { * @type {Collection} */ this.messages = new Collection(); + + /** + * The ID of the last message in the channel, if one was sent. + * @type {?string} + */ + this.lastMessageID = null; } /**