Clean up a bunch of stuff

- Channel typing data is now a Map
- Client properties on structures are now non-enumerable and
non-configurable
This commit is contained in:
Schuyler Cebulskie
2016-09-07 00:24:45 -04:00
parent 3a790e74f4
commit b7f582b7f0
22 changed files with 411 additions and 316 deletions

View File

@@ -9,21 +9,13 @@ class TypingStartHandler extends AbstractHandler {
const user = client.users.get(data.user_id); const user = client.users.get(data.user_id);
const timestamp = new Date(data.timestamp * 1000); 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 && user) {
if (channel.typingMap[user.id]) { if (channel._typing.has(user.id)) {
// already typing, renew const typing = channel._typing.get(user.id);
const mapping = channel.typingMap[user.id]; typing.lastTimestamp = timestamp;
mapping.lastTimestamp = timestamp; typing.resetTimeout(tooLate(channel, user));
mapping.resetTimeout(tooLate());
} else { } 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); 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 * Emitted whenever a user starts typing in a channel
* @event Client#typingStart * @event Client#typingStart

View File

@@ -8,8 +8,8 @@ class Channel {
* @type {Client} * @type {Client}
*/ */
this.client = client; this.client = client;
this.typingMap = {}; Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
this.typingTimeouts = [];
/** /**
* The type of the channel, either: * The type of the channel, either:
* * `dm` - a DM channel * * `dm` - a DM channel
@@ -19,6 +19,8 @@ class Channel {
* @type {string} * @type {string}
*/ */
this.type = null; this.type = null;
this._typing = new Map();
if (data) this.setup(data); if (data) this.setup(data);
} }

View File

@@ -7,11 +7,13 @@ const User = require('./User');
class ClientUser extends User { class ClientUser extends User {
setup(data) { setup(data) {
super.setup(data); super.setup(data);
/** /**
* Whether or not this account has been verified * Whether or not this account has been verified
* @type {boolean} * @type {boolean}
*/ */
this.verified = data.verified; this.verified = data.verified;
/** /**
* The email of this account * The email of this account
* @type {string} * @type {string}

View File

@@ -16,18 +16,15 @@ class DMChannel extends Channel {
setup(data) { setup(data) {
super.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 * The recipient on the other end of the DM
* @type {User} * @type {User}
*/ */
this.recipient = recipient; this.recipient = this.client.users.get(data.recipients[0].id) || new User(this.client, data.recipients[0]);
/**
* The ID of the last sent message, if available
* @type {?string}
*/
this.lastMessageID = data.last_message_id;
this.type = 'dm'; this.type = 'dm';
this.lastMessageID = data.last_message_id;
} }
/** /**

View File

@@ -11,11 +11,14 @@ class Emoji {
* @type {Client} * @type {Client}
*/ */
this.client = guild.client; this.client = guild.client;
Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
/** /**
* The Guild this emoji is part of * The Guild this emoji is part of
* @type {Guild} * @type {Guild}
*/ */
this.guild = guild; this.guild = guild;
this.setup(data); this.setup(data);
} }
@@ -25,22 +28,26 @@ class Emoji {
* @type {string} * @type {string}
*/ */
this.id = data.id; this.id = data.id;
/** /**
* The name of the Emoji * The name of the Emoji
* @type {string} * @type {string}
*/ */
this.name = data.name; this.name = data.name;
this.roleIDS = data.roles;
/** /**
* Whether or not this emoji requires colons surrounding it * Whether or not this emoji requires colons surrounding it
* @type {boolean} * @type {boolean}
*/ */
this.requiresColons = data.require_colons; this.requiresColons = data.require_colons;
/** /**
* Whether this emoji is managed by an external service * Whether this emoji is managed by an external service
* @type {boolean} * @type {boolean}
*/ */
this.managed = data.managed; this.managed = data.managed;
this._roles = data.roles;
} }
/** /**
@@ -59,8 +66,8 @@ class Emoji {
*/ */
get roles() { get roles() {
const roles = new Collection(); const roles = new Collection();
for (const role of this.roleIDS) { for (const role of this._roles) {
if (this.guild.roles.get(role)) roles.set(role, this.guild.roles.get(role)); if (this.guild.roles.has(role)) roles.set(role, this.guild.roles.get(role));
} }
return roles; return roles;
} }

View File

@@ -10,6 +10,7 @@ class EvaluatedPermissions {
* @type {GuildMember} * @type {GuildMember}
*/ */
this.member = member; this.member = member;
/** /**
* A number representing the packed permissions. * A number representing the packed permissions.
* @private * @private

View File

@@ -36,25 +36,27 @@ class GroupDMChannel extends Channel {
this.messages = new Collection(); 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) { setup(data) {
super.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) { if (!this.recipients) {
/** /**
* A collection of the recipients of this DM, mapped by their ID. * 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; 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.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() { sendMessage() {

View File

@@ -19,6 +19,7 @@ class Guild {
* @type {Client} * @type {Client}
*/ */
this.client = 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. * 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(); this.roles = new Collection();
if (!data) return; if (!data) return;
if (data.unavailable) { if (data.unavailable) {
/** /**
* Whether the Guild is available to access. If it is not available, it indicates a server outage. * Whether the Guild is available to access. If it is not available, it indicates a server outage.
* @type {boolean} * @type {boolean}
*/ */
this.available = false; this.available = false;
/** /**
* The Unique ID of the Guild, useful for comparisons. * The Unique ID of the Guild, useful for comparisons.
* @type {string} * @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() { _checkChunks() {
if (this._fetchWaiter) { if (this._fetchWaiter) {
if (this.members.size === this.memberCount) { if (this.members.size === this.memberCount) {
@@ -83,6 +216,7 @@ class Guild {
member.voiceChannelID = voiceState.channel_id; member.voiceChannelID = voiceState.channel_id;
this.channels.get(voiceState.channel_id).members.set(member.user.id, member); this.channels.get(voiceState.channel_id).members.set(member.user.id, member);
} }
/** /**
* Emitted whenever a user joins a guild. * Emitted whenever a user joins a guild.
* @event Client#guildMemberAdd * @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 * The time the guild was created
* @readonly * @readonly

View File

@@ -13,6 +13,7 @@ const arraysEqual = require('../util/ArraysEqual');
class GuildChannel extends Channel { class GuildChannel extends Channel {
constructor(guild, data) { constructor(guild, data) {
super(guild.client, data); super(guild.client, data);
/** /**
* The guild the channel is in * The guild the channel is in
* @type {Guild} * @type {Guild}
@@ -22,17 +23,19 @@ class GuildChannel extends Channel {
setup(data) { setup(data) {
super.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 * The name of the Guild Channel
* @type {string} * @type {string}
*/ */
this.name = data.name; 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. * A map of permission overwrites in this channel for roles and users.
* @type {Collection<string, PermissionOverwrites>} * @type {Collection<string, PermissionOverwrites>}
@@ -53,11 +56,11 @@ class GuildChannel extends Channel {
*/ */
equals(channel) { equals(channel) {
let equal = channel && let equal = channel &&
this.id === channel.id &&
this.type === channel.type && this.type === channel.type &&
this.topic === channel.topic && this.topic === channel.topic &&
this.position === channel.position && this.position === channel.position &&
this.name === channel.name && this.name === channel.name;
this.id === channel.id;
if (equal) { if (equal) {
if (channel.permission_overwrites) { if (channel.permission_overwrites) {

View File

@@ -13,64 +13,76 @@ class GuildMember {
* @type {Client} * @type {Client}
*/ */
this.client = guild.client; this.client = guild.client;
Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
/** /**
* The guild that this member is part of * The guild that this member is part of
* @type {Guild} * @type {Guild}
*/ */
this.guild = guild; this.guild = guild;
/** /**
* The user that this guild member instance Represents * The user that this guild member instance Represents
* @type {User} * @type {User}
*/ */
this.user = {}; this.user = {};
this._roles = []; this._roles = [];
if (data) this.setup(data); if (data) this.setup(data);
} }
setup(data) { setup(data) {
this.user = data.user;
/** /**
* Whether this member is deafened server-wide * Whether this member is deafened server-wide
* @type {boolean} * @type {boolean}
*/ */
this.serverDeaf = data.deaf; this.serverDeaf = data.deaf;
/** /**
* Whether this member is muted server-wide * Whether this member is muted server-wide
* @type {boolean} * @type {boolean}
*/ */
this.serverMute = data.mute; this.serverMute = data.mute;
/** /**
* Whether this member is self-muted * Whether this member is self-muted
* @type {boolean} * @type {boolean}
*/ */
this.selfMute = data.self_mute; this.selfMute = data.self_mute;
/** /**
* Whether this member is self-deafened * Whether this member is self-deafened
* @type {boolean} * @type {boolean}
*/ */
this.selfDeaf = data.self_deaf; this.selfDeaf = data.self_deaf;
/** /**
* The voice session ID of this member, if any * The voice session ID of this member, if any
* @type {?string} * @type {?string}
*/ */
this.voiceSessionID = data.session_id; this.voiceSessionID = data.session_id;
/** /**
* The voice channel ID of this member, if any * The voice channel ID of this member, if any
* @type {?string} * @type {?string}
*/ */
this.voiceChannelID = data.channel_id; this.voiceChannelID = data.channel_id;
this._joinDate = new Date(data.joined_at).getTime();
/** /**
* Whether this meember is speaking * Whether this meember is speaking
* @type {?boolean} * @type {?boolean}
*/ */
this.speaking = this.speaking; this.speaking = this.speaking;
/** /**
* The nickname of this Guild Member, if they have one * The nickname of this Guild Member, if they have one
* @type {?string} * @type {?string}
*/ */
this.nickname = data.nick; this.nickname = data.nick;
this.user = data.user;
this._roles = data.roles; this._roles = data.roles;
this._joinDate = new Date(data.joined_at).getTime();
} }
/** /**

View File

@@ -32,22 +32,24 @@ class Invite {
* @type {Client} * @type {Client}
*/ */
this.client = client; this.client = client;
Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
this.setup(data); this.setup(data);
} }
setup(data) { setup(data) {
/** /**
* The maximum age of the invite, in seconds * The Guild the invite is for. If this Guild is already known, this will be a Guild object. If the Guild is
* @type {?number} * 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 * The code for this invite
* @type {string} * @type {string}
*/ */
this.code = data.code; this.code = data.code;
this._creationDate = new Date(data.created_at).getTime();
/** /**
* Whether or not this invite is temporary * Whether or not this invite is temporary
@@ -55,6 +57,12 @@ class Invite {
*/ */
this.temporary = data.temporary; 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 * How many times this invite has been used
* @type {number} * @type {number}
@@ -73,19 +81,22 @@ class Invite {
*/ */
this.inviter = this.client.dataManager.newUser(data.inviter); 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. * 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. * If the Channel is unknown, this will be a Partial Guild Channel.
* @type {GuildChannel|PartialGuildChannel} * @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} * @type {Date}
*/ */
get creationDate() { get creationDate() {
return new Date(this._creationDate); return new Date(this._createdAt);
} }
/** /**

View File

@@ -7,76 +7,91 @@ const Collection = require('../util/Collection');
*/ */
class Message { class Message {
constructor(channel, data, client) { 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 * The channel that the message was sent in
* @type {TextChannel|DMChannel|GroupDMChannel} * @type {TextChannel|DMChannel|GroupDMChannel}
*/ */
this.channel = channel; 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 * If the message was sent in a guild, this will be the guild the message was sent in
* @type {Client} * @type {?Guild}
*/ */
this.client = client; this.guild = channel.guild || null;
if (data) this.setup(data); if (data) this.setup(data);
} }
setup(data) { setup(data) {
/** /**
* Whether or not this message is pinned * The ID of the message (unique in the channel it was sent)
* @type {boolean} * @type {string}
*/ */
this.pinned = data.pinned; this.id = data.id;
/**
* 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);
}
/** /**
* The content of the message * The content of the message
* @type {string} * @type {string}
*/ */
this.content = data.content; 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 * Whether or not the message was Text-To-Speech
* @type {boolean} * @type {boolean}
*/ */
this.tts = data.tts; this.tts = data.tts;
/** /**
* A random number used for checking message delivery * A random number used for checking message delivery
* @type {string} * @type {string}
*/ */
this.nonce = data.nonce; 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 * A list of embeds in the message - e.g. YouTube Player
* @type {Embed[]} * @type {Embed[]}
*/ */
this.embeds = data.embeds.map(e => new Embed(this, e)); this.embeds = data.embeds.map(e => new Embed(this, e));
/** /**
* A collection of attachments in the message - e.g. Pictures - mapped by their ID. * A collection of attachments in the message - e.g. Pictures - mapped by their ID.
* @type {Collection<string, MessageAttachment>} * @type {Collection<string, MessageAttachment>}
*/ */
this.attachments = new Collection(); this.attachments = new Collection();
for (const attachment of data.attachments) this.attachments.set(attachment.id, new Attachment(this, attachment)); 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 * An object containing a further users, roles or channels collections
* @type {Object} * @type {Object}
@@ -92,11 +107,6 @@ class Message {
channels: new Collection(), channels: new Collection(),
everyone: data.mention_everyone, 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) { for (const mention of data.mentions) {
let user = this.client.users.get(mention.id); 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 = []; 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;
} }
/** /**

View File

@@ -8,6 +8,7 @@ class MessageAttachment {
* @type {Client} * @type {Client}
*/ */
this.client = message.client; this.client = message.client;
Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
/** /**
* The message this attachment is part of. * The message this attachment is part of.
* @type {Message} * @type {Message}

View File

@@ -13,6 +13,7 @@ class MessageEmbed {
* @type {Client} * @type {Client}
*/ */
this.client = message.client; this.client = message.client;
Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
this.setup(data); this.setup(data);
} }

View File

@@ -15,30 +15,35 @@ class PartialGuild {
* @type {Client} * @type {Client}
*/ */
this.client = client; this.client = client;
Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
this.setup(data); this.setup(data);
} }
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 * The ID of this guild
* @type {string} * @type {string}
*/ */
this.id = data.id; 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 * The name of this guild
* @type {string} * @type {string}
*/ */
this.name = data.name; 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;
} }
} }

View File

@@ -14,6 +14,8 @@ class PartialGuildChannel {
* @type {Client} * @type {Client}
*/ */
this.client = client; this.client = client;
Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
this.setup(data); this.setup(data);
} }
@@ -23,11 +25,13 @@ class PartialGuildChannel {
* @type {string} * @type {string}
*/ */
this.id = data.id; this.id = data.id;
/** /**
* The name of this Guild Channel * The name of this Guild Channel
* @type {string} * @type {string}
*/ */
this.name = data.name; this.name = data.name;
/** /**
* The type of this Guild Channel - `text` or `voice` * The type of this Guild Channel - `text` or `voice`
* @type {string} * @type {string}

View File

@@ -8,20 +8,23 @@ class PermissionOverwrites {
* @type {GuildChannel} * @type {GuildChannel}
*/ */
this.channel = guildChannel; this.channel = guildChannel;
if (data) this.setup(data); if (data) this.setup(data);
} }
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 * The ID of this overwrite, either a User ID or a Role ID
* @type {string} * @type {string}
*/ */
this.id = data.id; this.id = data.id;
/**
* The type of this overwrite
* @type {string}
*/
this.type = data.type;
this.denyData = data.deny; this.denyData = data.deny;
this.allowData = data.allow; this.allowData = data.allow;
} }

View File

@@ -5,19 +5,66 @@ const Constants = require('../util/Constants');
*/ */
class Role { class Role {
constructor(guild, data) { constructor(guild, data) {
/**
* The guild that the role belongs to
* @type {Guild}
*/
this.guild = guild;
/** /**
* The client that instantiated the role * The client that instantiated the role
* @type {Client} * @type {Client}
*/ */
this.client = guild.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); 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) { equals(role) {
return role && return role &&
this.id === role.id && this.id === role.id &&
@@ -29,44 +76,6 @@ class Role {
this.managed === role.managed; 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 * The time the role was created
* @readonly * @readonly

View File

@@ -15,16 +15,19 @@ class TextChannel extends GuildChannel {
setup(data) { setup(data) {
super.setup(data); super.setup(data);
/** /**
* The topic of the Guild Channel, if there is one. * The topic of the Guild Channel, if there is one.
* @type {?string} * @type {?string}
*/ */
this.topic = data.topic; this.topic = data.topic;
/** /**
* The ID of the last message in the channel, if one was sent. * The ID of the last message in the channel, if one was sent.
* @type {?string} * @type {?string}
*/ */
this.lastMessageID = data.last_message_id; this.lastMessageID = data.last_message_id;
this.type = 'text'; this.type = 'text';
} }

View File

@@ -7,36 +7,47 @@ const Constants = require('../util/Constants');
*/ */
class User { class User {
constructor(client, data) { constructor(client, data) {
/**
* The Client that created the instance of the the User.
* @type {Client}
*/
this.client = client; this.client = client;
Object.defineProperty(this, 'client', { enumerable: false, configurable: false });
if (data) this.setup(data); if (data) this.setup(data);
} }
setup(data) { setup(data) {
/**
* The username of the User
* @type {string}
*/
this.username = data.username;
/** /**
* The ID of the User * The ID of the User
* @type {string} * @type {string}
*/ */
this.id = data.id; this.id = data.id;
/**
* The username of the User
* @type {string}
*/
this.username = data.username;
/** /**
* A discriminator based on username for the User * A discriminator based on username for the User
* @type {string} * @type {string}
*/ */
this.discriminator = data.discriminator; this.discriminator = data.discriminator;
/** /**
* The ID of the user's avatar * The ID of the user's avatar
* @type {string} * @type {string}
*/ */
this.avatar = data.avatar; this.avatar = data.avatar;
/** /**
* Whether or not the User is a Bot. * Whether or not the User is a Bot.
* @type {boolean} * @type {boolean}
*/ */
this.bot = Boolean(data.bot); this.bot = Boolean(data.bot);
/** /**
* The status of the user: * The status of the user:
* *
@@ -46,6 +57,7 @@ class User {
* @type {string} * @type {string}
*/ */
this.status = data.status || this.status || 'offline'; this.status = data.status || this.status || 'offline';
/** /**
* The game that the user is playing, `null` if they aren't playing a game. * The game that the user is playing, `null` if they aren't playing a game.
* @type {string} * @type {string}
@@ -99,8 +111,8 @@ class User {
*/ */
equals(user) { equals(user) {
let equal = user && let equal = user &&
this.username === user.username &&
this.id === user.id && this.id === user.id &&
this.username === user.username &&
this.discriminator === user.discriminator && this.discriminator === user.discriminator &&
this.avatar === user.avatar && this.avatar === user.avatar &&
this.bot === Boolean(user.bot); this.bot === Boolean(user.bot);

View File

@@ -8,6 +8,7 @@ const Collection = require('../util/Collection');
class VoiceChannel extends GuildChannel { class VoiceChannel extends GuildChannel {
constructor(guild, data) { constructor(guild, data) {
super(guild, data); super(guild, data);
/** /**
* The members in this Voice Channel. * The members in this Voice Channel.
* @type {Collection<string, GuildMember>} * @type {Collection<string, GuildMember>}
@@ -17,16 +18,19 @@ class VoiceChannel extends GuildChannel {
setup(data) { setup(data) {
super.setup(data); super.setup(data);
/** /**
* The bitrate of this voice channel * The bitrate of this voice channel
* @type {number} * @type {number}
*/ */
this.bitrate = data.bitrate; this.bitrate = data.bitrate;
/** /**
* The maximum amount of users allowed in this channel - 0 means unlimited. * The maximum amount of users allowed in this channel - 0 means unlimited.
* @type {number} * @type {number}
*/ */
this.userLimit = data.user_limit; this.userLimit = data.user_limit;
this.type = 'voice'; this.type = 'voice';
} }

View File

@@ -14,6 +14,12 @@ class TextBasedChannel {
* @type {Collection<string, Message>} * @type {Collection<string, Message>}
*/ */
this.messages = new Collection(); this.messages = new Collection();
/**
* The ID of the last message in the channel, if one was sent.
* @type {?string}
*/
this.lastMessageID = null;
} }
/** /**