buddy

node MVC discord bot
Log | Files | Refs | README

MessageEmbed.js (12303B)


      1 'use strict';
      2 
      3 const { RangeError } = require('../errors');
      4 const Util = require('../util/Util');
      5 
      6 /**
      7  * Represents an embed in a message (image/video preview, rich embed, etc.)
      8  */
      9 class MessageEmbed {
     10   /**
     11    * @name MessageEmbed
     12    * @kind constructor
     13    * @memberof MessageEmbed
     14    * @param {MessageEmbed|Object} [data={}] MessageEmbed to clone or raw embed data
     15    */
     16 
     17   constructor(data = {}, skipValidation = false) {
     18     this.setup(data, skipValidation);
     19   }
     20 
     21   setup(data, skipValidation) {
     22     /**
     23      * The type of this embed, either:
     24      * * `rich` - a rich embed
     25      * * `image` - an image embed
     26      * * `video` - a video embed
     27      * * `gifv` - a gifv embed
     28      * * `article` - an article embed
     29      * * `link` - a link embed
     30      * @type {string}
     31      */
     32     this.type = data.type;
     33 
     34     /**
     35      * The title of this embed
     36      * @type {?string}
     37      */
     38     this.title = data.title;
     39 
     40     /**
     41      * The description of this embed
     42      * @type {?string}
     43      */
     44     this.description = data.description;
     45 
     46     /**
     47      * The URL of this embed
     48      * @type {?string}
     49      */
     50     this.url = data.url;
     51 
     52     /**
     53      * The color of this embed
     54      * @type {?number}
     55      */
     56     this.color = Util.resolveColor(data.color);
     57 
     58     /**
     59      * The timestamp of this embed
     60      * @type {?number}
     61      */
     62     this.timestamp = data.timestamp ? new Date(data.timestamp).getTime() : null;
     63 
     64     /**
     65      * @typedef {Object} EmbedField
     66      * @property {string} name The name of this field
     67      * @property {string} value The value of this field
     68      * @property {boolean} inline If this field will be displayed inline
     69      */
     70 
     71     /**
     72      * The fields of this embed
     73      * @type {EmbedField[]}
     74      */
     75     this.fields = [];
     76     if (data.fields) {
     77       this.fields = skipValidation ? data.fields.map(Util.cloneObject) : this.constructor.normalizeFields(data.fields);
     78     }
     79 
     80     /**
     81      * @typedef {Object} MessageEmbedThumbnail
     82      * @property {string} url URL for this thumbnail
     83      * @property {string} proxyURL ProxyURL for this thumbnail
     84      * @property {number} height Height of this thumbnail
     85      * @property {number} width Width of this thumbnail
     86      */
     87 
     88     /**
     89      * The thumbnail of this embed (if there is one)
     90      * @type {?MessageEmbedThumbnail}
     91      */
     92     this.thumbnail = data.thumbnail
     93       ? {
     94           url: data.thumbnail.url,
     95           proxyURL: data.thumbnail.proxyURL || data.thumbnail.proxy_url,
     96           height: data.thumbnail.height,
     97           width: data.thumbnail.width,
     98         }
     99       : null;
    100 
    101     /**
    102      * @typedef {Object} MessageEmbedImage
    103      * @property {string} url URL for this image
    104      * @property {string} proxyURL ProxyURL for this image
    105      * @property {number} height Height of this image
    106      * @property {number} width Width of this image
    107      */
    108 
    109     /**
    110      * The image of this embed, if there is one
    111      * @type {?MessageEmbedImage}
    112      */
    113     this.image = data.image
    114       ? {
    115           url: data.image.url,
    116           proxyURL: data.image.proxyURL || data.image.proxy_url,
    117           height: data.image.height,
    118           width: data.image.width,
    119         }
    120       : null;
    121 
    122     /**
    123      * @typedef {Object} MessageEmbedVideo
    124      * @property {string} url URL of this video
    125      * @property {string} proxyURL ProxyURL for this video
    126      * @property {number} height Height of this video
    127      * @property {number} width Width of this video
    128      */
    129 
    130     /**
    131      * The video of this embed (if there is one)
    132      * @type {?MessageEmbedVideo}
    133      * @readonly
    134      */
    135     this.video = data.video
    136       ? {
    137           url: data.video.url,
    138           proxyURL: data.video.proxyURL || data.video.proxy_url,
    139           height: data.video.height,
    140           width: data.video.width,
    141         }
    142       : null;
    143 
    144     /**
    145      * @typedef {Object} MessageEmbedAuthor
    146      * @property {string} name The name of this author
    147      * @property {string} url URL of this author
    148      * @property {string} iconURL URL of the icon for this author
    149      * @property {string} proxyIconURL Proxied URL of the icon for this author
    150      */
    151 
    152     /**
    153      * The author of this embed (if there is one)
    154      * @type {?MessageEmbedAuthor}
    155      */
    156     this.author = data.author
    157       ? {
    158           name: data.author.name,
    159           url: data.author.url,
    160           iconURL: data.author.iconURL || data.author.icon_url,
    161           proxyIconURL: data.author.proxyIconURL || data.author.proxy_icon_url,
    162         }
    163       : null;
    164 
    165     /**
    166      * @typedef {Object} MessageEmbedProvider
    167      * @property {string} name The name of this provider
    168      * @property {string} url URL of this provider
    169      */
    170 
    171     /**
    172      * The provider of this embed (if there is one)
    173      * @type {?MessageEmbedProvider}
    174      */
    175     this.provider = data.provider
    176       ? {
    177           name: data.provider.name,
    178           url: data.provider.name,
    179         }
    180       : null;
    181 
    182     /**
    183      * @typedef {Object} MessageEmbedFooter
    184      * @property {string} text The text of this footer
    185      * @property {string} iconURL URL of the icon for this footer
    186      * @property {string} proxyIconURL Proxied URL of the icon for this footer
    187      */
    188 
    189     /**
    190      * The footer of this embed
    191      * @type {?MessageEmbedFooter}
    192      */
    193     this.footer = data.footer
    194       ? {
    195           text: data.footer.text,
    196           iconURL: data.footer.iconURL || data.footer.icon_url,
    197           proxyIconURL: data.footer.proxyIconURL || data.footer.proxy_icon_url,
    198         }
    199       : null;
    200 
    201     /**
    202      * The files of this embed
    203      * @type {Array<FileOptions|string|MessageAttachment>}
    204      */
    205     this.files = data.files || [];
    206   }
    207 
    208   /**
    209    * The date displayed on this embed
    210    * @type {?Date}
    211    * @readonly
    212    */
    213   get createdAt() {
    214     return this.timestamp ? new Date(this.timestamp) : null;
    215   }
    216 
    217   /**
    218    * The hexadecimal version of the embed color, with a leading hash
    219    * @type {?string}
    220    * @readonly
    221    */
    222   get hexColor() {
    223     return this.color ? `#${this.color.toString(16).padStart(6, '0')}` : null;
    224   }
    225 
    226   /**
    227    * The accumulated length for the embed title, description, fields and footer text
    228    * @type {number}
    229    * @readonly
    230    */
    231   get length() {
    232     return (
    233       (this.title ? this.title.length : 0) +
    234       (this.description ? this.description.length : 0) +
    235       (this.fields.length >= 1
    236         ? this.fields.reduce((prev, curr) => prev + curr.name.length + curr.value.length, 0)
    237         : 0) +
    238       (this.footer ? this.footer.text.length : 0)
    239     );
    240   }
    241 
    242   /**
    243    * Adds a field to the embed (max 25).
    244    * @param {StringResolvable} name The name of this field
    245    * @param {StringResolvable} value The value of this field
    246    * @param {boolean} [inline=false] If this field will be displayed inline
    247    * @returns {MessageEmbed}
    248    */
    249   addField(name, value, inline) {
    250     return this.addFields({ name, value, inline });
    251   }
    252 
    253   /**
    254    * Adds fields to the embed (max 25).
    255    * @param {...EmbedFieldData|EmbedFieldData[]} fields The fields to add
    256    * @returns {MessageEmbed}
    257    */
    258   addFields(...fields) {
    259     this.fields.push(...this.constructor.normalizeFields(fields));
    260     return this;
    261   }
    262 
    263   /**
    264    * Removes, replaces, and inserts fields in the embed (max 25).
    265    * @param {number} index The index to start at
    266    * @param {number} deleteCount The number of fields to remove
    267    * @param {...EmbedFieldData|EmbedFieldData[]} [fields] The replacing field objects
    268    * @returns {MessageEmbed}
    269    */
    270   spliceFields(index, deleteCount, ...fields) {
    271     this.fields.splice(index, deleteCount, ...this.constructor.normalizeFields(...fields));
    272     return this;
    273   }
    274 
    275   /**
    276    * Sets the file to upload alongside the embed. This file can be accessed via `attachment://fileName.extension` when
    277    * setting an embed image or author/footer icons. Multiple files can be attached.
    278    * @param {Array<FileOptions|string|MessageAttachment>} files Files to attach
    279    * @returns {MessageEmbed}
    280    */
    281   attachFiles(files) {
    282     this.files = this.files.concat(files);
    283     return this;
    284   }
    285 
    286   /**
    287    * Sets the author of this embed.
    288    * @param {StringResolvable} name The name of the author
    289    * @param {string} [iconURL] The icon URL of the author
    290    * @param {string} [url] The URL of the author
    291    * @returns {MessageEmbed}
    292    */
    293   setAuthor(name, iconURL, url) {
    294     this.author = { name: Util.resolveString(name), iconURL, url };
    295     return this;
    296   }
    297 
    298   /**
    299    * Sets the color of this embed.
    300    * @param {ColorResolvable} color The color of the embed
    301    * @returns {MessageEmbed}
    302    */
    303   setColor(color) {
    304     this.color = Util.resolveColor(color);
    305     return this;
    306   }
    307 
    308   /**
    309    * Sets the description of this embed.
    310    * @param {StringResolvable} description The description
    311    * @returns {MessageEmbed}
    312    */
    313   setDescription(description) {
    314     description = Util.resolveString(description);
    315     this.description = description;
    316     return this;
    317   }
    318 
    319   /**
    320    * Sets the footer of this embed.
    321    * @param {StringResolvable} text The text of the footer
    322    * @param {string} [iconURL] The icon URL of the footer
    323    * @returns {MessageEmbed}
    324    */
    325   setFooter(text, iconURL) {
    326     text = Util.resolveString(text);
    327     this.footer = { text, iconURL };
    328     return this;
    329   }
    330 
    331   /**
    332    * Sets the image of this embed.
    333    * @param {string} url The URL of the image
    334    * @returns {MessageEmbed}
    335    */
    336   setImage(url) {
    337     this.image = { url };
    338     return this;
    339   }
    340 
    341   /**
    342    * Sets the thumbnail of this embed.
    343    * @param {string} url The URL of the thumbnail
    344    * @returns {MessageEmbed}
    345    */
    346   setThumbnail(url) {
    347     this.thumbnail = { url };
    348     return this;
    349   }
    350 
    351   /**
    352    * Sets the timestamp of this embed.
    353    * @param {Date|number} [timestamp=Date.now()] The timestamp or date
    354    * @returns {MessageEmbed}
    355    */
    356   setTimestamp(timestamp = Date.now()) {
    357     if (timestamp instanceof Date) timestamp = timestamp.getTime();
    358     this.timestamp = timestamp;
    359     return this;
    360   }
    361 
    362   /**
    363    * Sets the title of this embed.
    364    * @param {StringResolvable} title The title
    365    * @returns {MessageEmbed}
    366    */
    367   setTitle(title) {
    368     title = Util.resolveString(title);
    369     this.title = title;
    370     return this;
    371   }
    372 
    373   /**
    374    * Sets the URL of this embed.
    375    * @param {string} url The URL
    376    * @returns {MessageEmbed}
    377    */
    378   setURL(url) {
    379     this.url = url;
    380     return this;
    381   }
    382 
    383   /**
    384    * Transforms the embed to a plain object.
    385    * @returns {Object} The raw data of this embed
    386    */
    387   toJSON() {
    388     return {
    389       title: this.title,
    390       type: 'rich',
    391       description: this.description,
    392       url: this.url,
    393       timestamp: this.timestamp ? new Date(this.timestamp) : null,
    394       color: this.color,
    395       fields: this.fields,
    396       thumbnail: this.thumbnail,
    397       image: this.image,
    398       author: this.author
    399         ? {
    400             name: this.author.name,
    401             url: this.author.url,
    402             icon_url: this.author.iconURL,
    403           }
    404         : null,
    405       footer: this.footer
    406         ? {
    407             text: this.footer.text,
    408             icon_url: this.footer.iconURL,
    409           }
    410         : null,
    411     };
    412   }
    413 
    414   /**
    415    * Normalizes field input and resolves strings.
    416    * @param {StringResolvable} name The name of the field
    417    * @param {StringResolvable} value The value of the field
    418    * @param {boolean} [inline=false] Set the field to display inline
    419    * @returns {EmbedField}
    420    */
    421   static normalizeField(name, value, inline = false) {
    422     name = Util.resolveString(name);
    423     if (!name) throw new RangeError('EMBED_FIELD_NAME');
    424     value = Util.resolveString(value);
    425     if (!value) throw new RangeError('EMBED_FIELD_VALUE');
    426     return { name, value, inline };
    427   }
    428 
    429   /**
    430    * @typedef {Object} EmbedFieldData
    431    * @property {StringResolvable} name The name of this field
    432    * @property {StringResolvable} value The value of this field
    433    * @property {boolean} [inline] If this field will be displayed inline
    434    */
    435 
    436   /**
    437    * Normalizes field input and resolves strings.
    438    * @param  {...EmbedFieldData|EmbedFieldData[]} fields Fields to normalize
    439    * @returns {EmbedField[]}
    440    */
    441   static normalizeFields(...fields) {
    442     return fields
    443       .flat(2)
    444       .map(field =>
    445         this.normalizeField(
    446           field && field.name,
    447           field && field.value,
    448           field && typeof field.inline === 'boolean' ? field.inline : false,
    449         ),
    450       );
    451   }
    452 }
    453 
    454 module.exports = MessageEmbed;