buddy

node MVC discord bot
Log | Files | Refs | README

User.js (8359B)


      1 'use strict';
      2 
      3 const Base = require('./Base');
      4 const { Presence } = require('./Presence');
      5 const TextBasedChannel = require('./interfaces/TextBasedChannel');
      6 const { Error } = require('../errors');
      7 const Snowflake = require('../util/Snowflake');
      8 const UserFlags = require('../util/UserFlags');
      9 
     10 /**
     11  * Represents a user on Discord.
     12  * @implements {TextBasedChannel}
     13  * @extends {Base}
     14  */
     15 class User extends Base {
     16   /**
     17    * @param {Client} client The instantiating client
     18    * @param {Object} data The data for the user
     19    */
     20   constructor(client, data) {
     21     super(client);
     22 
     23     /**
     24      * The ID of the user
     25      * @type {Snowflake}
     26      */
     27     this.id = data.id;
     28 
     29     /**
     30      * Whether or not the user is a bot
     31      * @type {boolean}
     32      * @name User#bot
     33      */
     34     this.bot = Boolean(data.bot);
     35 
     36     this._patch(data);
     37   }
     38 
     39   _patch(data) {
     40     /**
     41      * The username of the user
     42      * @type {?string}
     43      * @name User#username
     44      */
     45     if (data.username) this.username = data.username;
     46 
     47     /**
     48      * A discriminator based on username for the user
     49      * @type {?string}
     50      * @name User#discriminator
     51      */
     52     if (data.discriminator) this.discriminator = data.discriminator;
     53 
     54     /**
     55      * The ID of the user's avatar
     56      * @type {?string}
     57      * @name User#avatar
     58      */
     59     if (typeof data.avatar !== 'undefined') this.avatar = data.avatar;
     60 
     61     if (typeof data.bot !== 'undefined') this.bot = Boolean(data.bot);
     62 
     63     /**
     64      * Whether the user is an Official Discord System user (part of the urgent message system)
     65      * @type {?boolean}
     66      * @name User#system
     67      */
     68     if (typeof data.system !== 'undefined') this.system = Boolean(data.system);
     69 
     70     /**
     71      * The locale of the user's client (ISO 639-1)
     72      * @type {?string}
     73      * @name User#locale
     74      */
     75     if (data.locale) this.locale = data.locale;
     76 
     77     /**
     78      * The flags for this user
     79      * @type {?UserFlags}
     80      */
     81     if (typeof data.public_flags !== 'undefined') this.flags = new UserFlags(data.public_flags);
     82 
     83     /**
     84      * The ID of the last message sent by the user, if one was sent
     85      * @type {?Snowflake}
     86      */
     87     this.lastMessageID = null;
     88 
     89     /**
     90      * The ID of the channel for the last message sent by the user, if one was sent
     91      * @type {?Snowflake}
     92      */
     93     this.lastMessageChannelID = null;
     94   }
     95 
     96   /**
     97    * Whether this User is a partial
     98    * @type {boolean}
     99    * @readonly
    100    */
    101   get partial() {
    102     return typeof this.username !== 'string';
    103   }
    104 
    105   /**
    106    * The timestamp the user was created at
    107    * @type {number}
    108    * @readonly
    109    */
    110   get createdTimestamp() {
    111     return Snowflake.deconstruct(this.id).timestamp;
    112   }
    113 
    114   /**
    115    * The time the user was created at
    116    * @type {Date}
    117    * @readonly
    118    */
    119   get createdAt() {
    120     return new Date(this.createdTimestamp);
    121   }
    122 
    123   /**
    124    * The Message object of the last message sent by the user, if one was sent
    125    * @type {?Message}
    126    * @readonly
    127    */
    128   get lastMessage() {
    129     const channel = this.client.channels.cache.get(this.lastMessageChannelID);
    130     return (channel && channel.messages.cache.get(this.lastMessageID)) || null;
    131   }
    132 
    133   /**
    134    * The presence of this user
    135    * @type {Presence}
    136    * @readonly
    137    */
    138   get presence() {
    139     for (const guild of this.client.guilds.cache.values()) {
    140       if (guild.presences.cache.has(this.id)) return guild.presences.cache.get(this.id);
    141     }
    142     return new Presence(this.client, { user: { id: this.id } });
    143   }
    144 
    145   /**
    146    * A link to the user's avatar.
    147    * @param {ImageURLOptions} [options={}] Options for the Image URL
    148    * @returns {?string}
    149    */
    150   avatarURL({ format, size, dynamic } = {}) {
    151     if (!this.avatar) return null;
    152     return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size, dynamic);
    153   }
    154 
    155   /**
    156    * A link to the user's default avatar
    157    * @type {string}
    158    * @readonly
    159    */
    160   get defaultAvatarURL() {
    161     return this.client.rest.cdn.DefaultAvatar(this.discriminator % 5);
    162   }
    163 
    164   /**
    165    * A link to the user's avatar if they have one.
    166    * Otherwise a link to their default avatar will be returned.
    167    * @param {ImageURLOptions} [options={}] Options for the Image URL
    168    * @returns {string}
    169    */
    170   displayAvatarURL(options) {
    171     return this.avatarURL(options) || this.defaultAvatarURL;
    172   }
    173 
    174   /**
    175    * The Discord "tag" (e.g. `hydrabolt#0001`) for this user
    176    * @type {?string}
    177    * @readonly
    178    */
    179   get tag() {
    180     return typeof this.username === 'string' ? `${this.username}#${this.discriminator}` : null;
    181   }
    182 
    183   /**
    184    * Checks whether the user is typing in a channel.
    185    * @param {ChannelResolvable} channel The channel to check in
    186    * @returns {boolean}
    187    */
    188   typingIn(channel) {
    189     channel = this.client.channels.resolve(channel);
    190     return channel._typing.has(this.id);
    191   }
    192 
    193   /**
    194    * Gets the time that the user started typing.
    195    * @param {ChannelResolvable} channel The channel to get the time in
    196    * @returns {?Date}
    197    */
    198   typingSinceIn(channel) {
    199     channel = this.client.channels.resolve(channel);
    200     return channel._typing.has(this.id) ? new Date(channel._typing.get(this.id).since) : null;
    201   }
    202 
    203   /**
    204    * Gets the amount of time the user has been typing in a channel for (in milliseconds), or -1 if they're not typing.
    205    * @param {ChannelResolvable} channel The channel to get the time in
    206    * @returns {number}
    207    */
    208   typingDurationIn(channel) {
    209     channel = this.client.channels.resolve(channel);
    210     return channel._typing.has(this.id) ? channel._typing.get(this.id).elapsedTime : -1;
    211   }
    212 
    213   /**
    214    * The DM between the client's user and this user
    215    * @type {?DMChannel}
    216    * @readonly
    217    */
    218   get dmChannel() {
    219     return this.client.channels.cache.find(c => c.type === 'dm' && c.recipient.id === this.id) || null;
    220   }
    221 
    222   /**
    223    * Creates a DM channel between the client and the user.
    224    * @returns {Promise<DMChannel>}
    225    */
    226   async createDM() {
    227     const { dmChannel } = this;
    228     if (dmChannel && !dmChannel.partial) return dmChannel;
    229     const data = await this.client.api.users(this.client.user.id).channels.post({
    230       data: {
    231         recipient_id: this.id,
    232       },
    233     });
    234     return this.client.actions.ChannelCreate.handle(data).channel;
    235   }
    236 
    237   /**
    238    * Deletes a DM channel (if one exists) between the client and the user. Resolves with the channel if successful.
    239    * @returns {Promise<DMChannel>}
    240    */
    241   async deleteDM() {
    242     const { dmChannel } = this;
    243     if (!dmChannel) throw new Error('USER_NO_DMCHANNEL');
    244     const data = await this.client.api.channels(dmChannel.id).delete();
    245     return this.client.actions.ChannelDelete.handle(data).channel;
    246   }
    247 
    248   /**
    249    * Checks if the user is equal to another. It compares ID, username, discriminator, avatar, and bot flags.
    250    * It is recommended to compare equality by using `user.id === user2.id` unless you want to compare all properties.
    251    * @param {User} user User to compare with
    252    * @returns {boolean}
    253    */
    254   equals(user) {
    255     let equal =
    256       user &&
    257       this.id === user.id &&
    258       this.username === user.username &&
    259       this.discriminator === user.discriminator &&
    260       this.avatar === user.avatar;
    261 
    262     return equal;
    263   }
    264 
    265   /**
    266    * Fetches this user's flags.
    267    * @returns {Promise<UserFlags>}
    268    */
    269   async fetchFlags() {
    270     if (this.flags) return this.flags;
    271     const data = await this.client.api.users(this.id).get();
    272     this._patch(data);
    273     return this.flags;
    274   }
    275 
    276   /**
    277    * Fetches this user.
    278    * @returns {Promise<User>}
    279    */
    280   fetch() {
    281     return this.client.users.fetch(this.id, true);
    282   }
    283 
    284   /**
    285    * When concatenated with a string, this automatically returns the user's mention instead of the User object.
    286    * @returns {string}
    287    * @example
    288    * // Logs: Hello from <@123456789012345678>!
    289    * console.log(`Hello from ${user}!`);
    290    */
    291   toString() {
    292     return `<@${this.id}>`;
    293   }
    294 
    295   toJSON(...props) {
    296     const json = super.toJSON(
    297       {
    298         createdTimestamp: true,
    299         defaultAvatarURL: true,
    300         tag: true,
    301         lastMessage: false,
    302         lastMessageID: false,
    303       },
    304       ...props,
    305     );
    306     json.avatarURL = this.avatarURL();
    307     json.displayAvatarURL = this.displayAvatarURL();
    308     return json;
    309   }
    310 
    311   // These are here only for documentation purposes - they are implemented by TextBasedChannel
    312   /* eslint-disable no-empty-function */
    313   send() {}
    314 }
    315 
    316 TextBasedChannel.applyToClass(User);
    317 
    318 module.exports = User;