Files
discord.js/src/structures/User.js
2017-09-05 12:02:21 +01:00

271 lines
7.5 KiB
JavaScript

const TextBasedChannel = require('./interfaces/TextBasedChannel');
const { Presence } = require('./Presence');
const UserProfile = require('./UserProfile');
const Snowflake = require('../util/Snowflake');
const Base = require('./Base');
const { Error } = require('../errors');
/**
* Represents a user on Discord.
* @implements {TextBasedChannel}
* @extends {Base}
*/
class User extends Base {
constructor(client, data) {
super(client);
/**
* The ID of the user
* @type {Snowflake}
*/
this.id = data.id;
this._patch(data);
}
_patch(data) {
/**
* The username of the user
* @type {string}
* @name User#username
*/
if (data.username) this.username = data.username;
/**
* A discriminator based on username for the user
* @type {string}
* @name User#discriminator
*/
if (data.discriminator) this.discriminator = data.discriminator;
/**
* The ID of the user's avatar
* @type {string}
* @name User#avatar
*/
if (data.avatar) this.avatar = data.avatar;
/**
* Whether or not the user is a bot
* @type {boolean}
* @name User#bot
*/
if (typeof this.bot === 'undefined' && typeof data.bot !== 'undefined') this.bot = Boolean(data.bot);
/**
* The ID of the last message sent by the user, if one was sent
* @type {?Snowflake}
*/
this.lastMessageID = null;
/**
* The Message object of the last message sent by the user, if one was sent
* @type {?Message}
*/
this.lastMessage = null;
if (data.token) this.client.token = data.token;
}
/**
* The timestamp the user was created at
* @type {number}
* @readonly
*/
get createdTimestamp() {
return Snowflake.deconstruct(this.id).timestamp;
}
/**
* 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.
* @param {Object} [options={}] Options for the avatar url
* @param {string} [options.format='webp'] One of `webp`, `png`, `jpg`, `gif`. If no format is provided,
* it will be `gif` for animated avatars or otherwise `webp`
* @param {number} [options.size=128] One of `128`, `256`, `512`, `1024`, `2048`
* @returns {?string}
*/
avatarURL({ format, size } = {}) {
if (!this.avatar) return null;
return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size);
}
/**
* A link to the user's default avatar
* @type {string}
* @readonly
*/
get defaultAvatarURL() {
return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5);
}
/**
* A link to the user's avatar if they have one.
* Otherwise a link to their default avatar will be returned.
* @param {Object} [options={}] Options for the avatar url
* @param {string} [options.format='webp'] One of `webp`, `png`, `jpg`, `gif`. If no format is provided,
* it will be `gif` for animated avatars or otherwise `webp`
* @param {number} [options.size=128] One of `128`, `256`, `512`, `1024`, `2048`
* @returns {string}
*/
displayAvatarURL(options) {
return this.avatarURL(options) || this.defaultAvatarURL;
}
/**
* The Discord "tag" for this user
* @type {string}
* @readonly
*/
get tag() {
return `${this.username}#${this.discriminator}`;
}
/**
* The note that is set for the user
* <warn>This is only available when using a user account.</warn>
* @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;
}
/**
* The DM between the client's user and this user
* @type {?DMChannel}
* @readonly
*/
get dmChannel() {
return this.client.channels.filter(c => c.type === 'dm').find(c => c.recipient.id === this.id);
}
/**
* Creates a DM channel between the client and the user.
* @returns {Promise<DMChannel>}
*/
createDM() {
if (this.dmChannel) return Promise.resolve(this.dmChannel);
return this.client.api.users(this.client.user.id).channels.post({ data: {
recipient_id: this.id,
} })
.then(data => this.client.actions.ChannelCreate.handle(data).channel);
}
/**
* Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
* @returns {Promise<DMChannel>}
*/
deleteDM() {
if (!this.dmChannel) return Promise.reject(new Error('USER_NO_DMCHANNEL'));
return this.client.api.channels(this.dmChannel.id).delete()
.then(data => this.client.actions.ChannelDelete.handle(data).channel);
}
/**
* Get the profile of the user.
* <warn>This is only available when using a user account.</warn>
* @returns {Promise<UserProfile>}
*/
fetchProfile() {
return this.client.api.users(this.id).profile.get().then(data => new UserProfile(this, data));
}
/**
* Sets a note for the user.
* <warn>This is only available when using a user account.</warn>
* @param {string} note The note to set for the user
* @returns {Promise<User>}
*/
setNote(note) {
return this.client.api.users('@me').notes(this.id).put({ data: { note } })
.then(() => this);
}
/**
* Checks if the user is equal to another. It compares ID, username, discriminator, avatar, and bot flags.
* It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties.
* @param {User} user User to compare with
* @returns {boolean}
*/
equals(user) {
let equal = user &&
this.id === user.id &&
this.username === user.username &&
this.discriminator === user.discriminator &&
this.avatar === user.avatar;
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
/* eslint-disable no-empty-function */
send() {}
}
TextBasedChannel.applyToClass(User);
module.exports = User;