buddy

node MVC discord bot
Log | Files | Refs | README

Webhook.js (8147B)


      1 'use strict';
      2 
      3 const APIMessage = require('./APIMessage');
      4 const Channel = require('./Channel');
      5 const { WebhookTypes } = require('../util/Constants');
      6 const DataResolver = require('../util/DataResolver');
      7 const Snowflake = require('../util/Snowflake');
      8 
      9 /**
     10  * Represents a webhook.
     11  */
     12 class Webhook {
     13   constructor(client, data) {
     14     /**
     15      * The client that instantiated the webhook
     16      * @name Webhook#client
     17      * @type {Client}
     18      * @readonly
     19      */
     20     Object.defineProperty(this, 'client', { value: client });
     21     if (data) this._patch(data);
     22   }
     23 
     24   _patch(data) {
     25     /**
     26      * The name of the webhook
     27      * @type {string}
     28      */
     29     this.name = data.name;
     30 
     31     /**
     32      * The token for the webhook
     33      * @name Webhook#token
     34      * @type {?string}
     35      */
     36     Object.defineProperty(this, 'token', { value: data.token || null, writable: true, configurable: true });
     37 
     38     /**
     39      * The avatar for the webhook
     40      * @type {?string}
     41      */
     42     this.avatar = data.avatar;
     43 
     44     /**
     45      * The ID of the webhook
     46      * @type {Snowflake}
     47      */
     48     this.id = data.id;
     49 
     50     /**
     51      * The type of the webhook
     52      * @type {WebhookTypes}
     53      */
     54     this.type = WebhookTypes[data.type];
     55 
     56     /**
     57      * The guild the webhook belongs to
     58      * @type {Snowflake}
     59      */
     60     this.guildID = data.guild_id;
     61 
     62     /**
     63      * The channel the webhook belongs to
     64      * @type {Snowflake}
     65      */
     66     this.channelID = data.channel_id;
     67 
     68     if (data.user) {
     69       /**
     70        * The owner of the webhook
     71        * @type {?User|Object}
     72        */
     73       this.owner = this.client.users ? this.client.users.cache.get(data.user.id) : data.user;
     74     } else {
     75       this.owner = null;
     76     }
     77   }
     78 
     79   /**
     80    * Options that can be passed into send.
     81    * @typedef {Object} WebhookMessageOptions
     82    * @property {string} [username=this.name] Username override for the message
     83    * @property {string} [avatarURL] Avatar URL override for the message
     84    * @property {boolean} [tts=false] Whether or not the message should be spoken aloud
     85    * @property {string} [nonce=''] The nonce for the message
     86    * @property {Object[]} [embeds] An array of embeds for the message
     87    * @property {MessageMentionOptions} [allowedMentions] Which mentions should be parsed from the message content
     88    * (see [here](https://discordapp.com/developers/docs/resources/channel#embed-object) for more details)
     89    * @property {DisableMentionType} [disableMentions=this.client.options.disableMentions] Whether or not all mentions or
     90    * everyone/here mentions should be sanitized to prevent unexpected mentions
     91    * @property {FileOptions[]|string[]} [files] Files to send with the message
     92    * @property {string|boolean} [code] Language for optional codeblock formatting to apply
     93    * @property {boolean|SplitOptions} [split=false] Whether or not the message should be split into multiple messages if
     94    * it exceeds the character limit. If an object is provided, these are the options for splitting the message.
     95    */
     96 
     97   /**
     98    * Sends a message with this webhook.
     99    * @param {StringResolvable|APIMessage} [content=''] The content to send
    100    * @param {WebhookMessageOptions|MessageAdditions} [options={}] The options to provide
    101    * @returns {Promise<Message|Object>}
    102    * @example
    103    * // Send a basic message
    104    * webhook.send('hello!')
    105    *   .then(message => console.log(`Sent message: ${message.content}`))
    106    *   .catch(console.error);
    107    * @example
    108    * // Send a remote file
    109    * webhook.send({
    110    *   files: ['https://cdn.discordapp.com/icons/222078108977594368/6e1019b3179d71046e463a75915e7244.png?size=2048']
    111    * })
    112    *   .then(console.log)
    113    *   .catch(console.error);
    114    * @example
    115    * // Send a local file
    116    * webhook.send({
    117    *   files: [{
    118    *     attachment: 'entire/path/to/file.jpg',
    119    *     name: 'file.jpg'
    120    *   }]
    121    * })
    122    *   .then(console.log)
    123    *   .catch(console.error);
    124    * @example
    125    * // Send an embed with a local image inside
    126    * webhook.send('This is an embed', {
    127    *   embeds: [{
    128    *     thumbnail: {
    129    *          url: 'attachment://file.jpg'
    130    *       }
    131    *    }],
    132    *    files: [{
    133    *       attachment: 'entire/path/to/file.jpg',
    134    *       name: 'file.jpg'
    135    *    }]
    136    * })
    137    *   .then(console.log)
    138    *   .catch(console.error);
    139    */
    140   async send(content, options) {
    141     let apiMessage;
    142 
    143     if (content instanceof APIMessage) {
    144       apiMessage = content.resolveData();
    145     } else {
    146       apiMessage = APIMessage.create(this, content, options).resolveData();
    147       if (Array.isArray(apiMessage.data.content)) {
    148         return Promise.all(apiMessage.split().map(this.send.bind(this)));
    149       }
    150     }
    151 
    152     const { data, files } = await apiMessage.resolveFiles();
    153     return this.client.api
    154       .webhooks(this.id, this.token)
    155       .post({
    156         data,
    157         files,
    158         query: { wait: true },
    159         auth: false,
    160       })
    161       .then(d => {
    162         const channel = this.client.channels ? this.client.channels.cache.get(d.channel_id) : undefined;
    163         if (!channel) return d;
    164         return channel.messages.add(d, false);
    165       });
    166   }
    167 
    168   /**
    169    * Sends a raw slack message with this webhook.
    170    * @param {Object} body The raw body to send
    171    * @returns {Promise<boolean>}
    172    * @example
    173    * // Send a slack message
    174    * webhook.sendSlackMessage({
    175    *   'username': 'Wumpus',
    176    *   'attachments': [{
    177    *     'pretext': 'this looks pretty cool',
    178    *     'color': '#F0F',
    179    *     'footer_icon': 'http://snek.s3.amazonaws.com/topSnek.png',
    180    *     'footer': 'Powered by sneks',
    181    *     'ts': Date.now() / 1000
    182    *   }]
    183    * }).catch(console.error);
    184    */
    185   sendSlackMessage(body) {
    186     return this.client.api
    187       .webhooks(this.id, this.token)
    188       .slack.post({
    189         query: { wait: true },
    190         auth: false,
    191         data: body,
    192       })
    193       .then(data => data.toString() === 'ok');
    194   }
    195 
    196   /**
    197    * Edits the webhook.
    198    * @param {Object} options Options
    199    * @param {string} [options.name=this.name] New name for this webhook
    200    * @param {BufferResolvable} [options.avatar] New avatar for this webhook
    201    * @param {ChannelResolvable} [options.channel] New channel for this webhook
    202    * @param {string} [reason] Reason for editing this webhook
    203    * @returns {Promise<Webhook>}
    204    */
    205   async edit({ name = this.name, avatar, channel }, reason) {
    206     if (avatar && typeof avatar === 'string' && !avatar.startsWith('data:')) {
    207       avatar = await DataResolver.resolveImage(avatar);
    208     }
    209     if (channel) channel = channel instanceof Channel ? channel.id : channel;
    210     const data = await this.client.api.webhooks(this.id, channel ? undefined : this.token).patch({
    211       data: { name, avatar, channel_id: channel },
    212       reason,
    213     });
    214 
    215     this.name = data.name;
    216     this.avatar = data.avatar;
    217     this.channelID = data.channel_id;
    218     return this;
    219   }
    220 
    221   /**
    222    * Deletes the webhook.
    223    * @param {string} [reason] Reason for deleting this webhook
    224    * @returns {Promise}
    225    */
    226   delete(reason) {
    227     return this.client.api.webhooks(this.id, this.token).delete({ reason });
    228   }
    229   /**
    230    * The timestamp the webhook was created at
    231    * @type {number}
    232    * @readonly
    233    */
    234   get createdTimestamp() {
    235     return Snowflake.deconstruct(this.id).timestamp;
    236   }
    237 
    238   /**
    239    * The time the webhook was created at
    240    * @type {Date}
    241    * @readonly
    242    */
    243   get createdAt() {
    244     return new Date(this.createdTimestamp);
    245   }
    246 
    247   /**
    248    * The url of this webhook
    249    * @type {string}
    250    * @readonly
    251    */
    252   get url() {
    253     return this.client.options.http.api + this.client.api.webhooks(this.id, this.token);
    254   }
    255 
    256   /**
    257    * A link to the webhook's avatar.
    258    * @param {ImageURLOptions} [options={}] Options for the Image URL
    259    * @returns {?string}
    260    */
    261   avatarURL({ format, size } = {}) {
    262     if (!this.avatar) return null;
    263     return this.client.rest.cdn.Avatar(this.id, this.avatar, format, size);
    264   }
    265 
    266   static applyToClass(structure) {
    267     for (const prop of ['send', 'sendSlackMessage', 'edit', 'delete', 'createdTimestamp', 'createdAt', 'url']) {
    268       Object.defineProperty(structure.prototype, prop, Object.getOwnPropertyDescriptor(Webhook.prototype, prop));
    269     }
    270   }
    271 }
    272 
    273 module.exports = Webhook;