const TextBasedChannel = require('./interface/TextBasedChannel'); const Constants = require('../util/Constants'); const Presence = require('./Presence').Presence; /** * Represents a user on Discord. * @implements {TextBasedChannel} */ class User { constructor(client, data) { /** * The Client that created the instance of the the User. * @name User#client * @type {Client} * @readonly */ Object.defineProperty(this, 'client', { value: client }); if (data) this.setup(data); } setup(data) { /** * 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 ID of the last message sent by the user, if one was sent. * @type {?string} */ this.lastMessageID = null; } patch(data) { for (const prop of ['id', 'username', 'discriminator', 'avatar', 'bot']) { if (typeof data[prop] !== 'undefined') this[prop] = data[prop]; } } /** * The timestamp the user was created at * @type {number} * @readonly */ get createdTimestamp() { return (this.id / 4194304) + 1420070400000; } /** * The time the user was created * @type {Date} * @readonly */ get createdAt() { return new Date(this.createdTimestamp); } /** * The presence of this user * @type {Presence} * @readonly */ get presence() { if (this.client.presences.has(this.id)) return this.client.presences.get(this.id); for (const guild of this.client.guilds.values()) { if (guild.presences.has(this.id)) return guild.presences.get(this.id); } return new Presence(); } /** * A link to the user's avatar (if they have one, otherwise null) * @type {?string} * @readonly */ get avatarURL() { if (!this.avatar) return null; return Constants.Endpoints.avatar(this.id, this.avatar); } /** * A link to the user's default avatar * @type {string} * @readonly */ get defaultAvatarURL() { let defaultAvatars = Object.values(Constants.DefaultAvatars); let defaultAvatar = this.discriminator % defaultAvatars.length; return Constants.endpoints.assets(`${defaultAvatars[defaultAvatar]}.png`); } /** * A link to the user's avatar if they have one. Otherwise a link to their default avatar will be returned * @type {string} * @readonly */ get displayAvatarURL() { return this.avatarURL || this.defaultAvatarURL; } /** * The note that is set for the user * This is only available when using a user account. * @type {?string} * @readonly */ get note() { return this.client.user.notes.get(this.id) || null; } /** * Check whether the user is typing in a channel. * @param {ChannelResolvable} channel The channel to check in * @returns {boolean} */ typingIn(channel) { channel = this.client.resolver.resolveChannel(channel); return channel._typing.has(this.id); } /** * Get the time that the user started typing. * @param {ChannelResolvable} channel The channel to get the time in * @returns {?Date} */ typingSinceIn(channel) { channel = this.client.resolver.resolveChannel(channel); return channel._typing.has(this.id) ? new Date(channel._typing.get(this.id).since) : null; } /** * Get the amount of time the user has been typing in a channel for (in milliseconds), or -1 if they're not typing. * @param {ChannelResolvable} channel The channel to get the time in * @returns {number} */ typingDurationIn(channel) { channel = this.client.resolver.resolveChannel(channel); return channel._typing.has(this.id) ? channel._typing.get(this.id).elapsedTime : -1; } /** * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful. * @returns {Promise} */ deleteDM() { return this.client.rest.methods.deleteChannel(this); } /** * Sends a friend request to the user * This is only available when using a user account. * @returns {Promise} */ addFriend() { return this.client.rest.methods.addFriend(this); } /** * Removes the user from your friends * This is only available when using a user account. * @returns {Promise} */ removeFriend() { return this.client.rest.methods.removeFriend(this); } /** * Blocks the user * This is only available when using a user account. * @returns {Promise} */ block() { return this.client.rest.methods.blockUser(this); } /** * Unblocks the user * This is only available when using a user account. * @returns {Promise} */ unblock() { return this.client.rest.methods.unblockUser(this); } /** * Get the profile of the user * This is only available when using a user account. * @returns {Promise} */ fetchProfile() { return this.client.rest.methods.fetchUserProfile(this); } /** * Sets a note for the user * This is only available when using a user account. * @param {string} note The note to set for the user * @returns {Promise} */ setNote(note) { return this.client.rest.methods.setNote(this, note); } /** * Checks if the user is equal to another. It compares username, ID, discriminator, status and the game being played. * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties. * @param {User} user The user to compare * @returns {boolean} */ equals(user) { let equal = user && this.id === user.id && this.username === user.username && this.discriminator === user.discriminator && this.avatar === user.avatar && this.bot === Boolean(user.bot); return equal; } /** * When concatenated with a string, this automatically concatenates the user's mention instead of the User object. * @returns {string} * @example * // logs: Hello from <@123456789>! * console.log(`Hello from ${user}!`); */ toString() { return `<@${this.id}>`; } // These are here only for documentation purposes - they are implemented by TextBasedChannel sendMessage() { return; } sendTTSMessage() { return; } sendEmbed() { return; } sendFile() { return; } sendCode() { return; } } TextBasedChannel.applyToClass(User); module.exports = User;