DataResolver.js (3907B)
1 'use strict'; 2 3 const fs = require('fs'); 4 const path = require('path'); 5 const stream = require('stream'); 6 const fetch = require('node-fetch'); 7 const { Error: DiscordError, TypeError } = require('../errors'); 8 const { browser } = require('../util/Constants'); 9 const Util = require('../util/Util'); 10 11 /** 12 * The DataResolver identifies different objects and tries to resolve a specific piece of information from them. 13 * @private 14 */ 15 class DataResolver { 16 constructor() { 17 throw new Error(`The ${this.constructor.name} class may not be instantiated.`); 18 } 19 20 /** 21 * Data that can be resolved to give an invite code. This can be: 22 * * An invite code 23 * * An invite URL 24 * @typedef {string} InviteResolvable 25 */ 26 27 /** 28 * Resolves InviteResolvable to an invite code. 29 * @param {InviteResolvable} data The invite resolvable to resolve 30 * @returns {string} 31 */ 32 static resolveInviteCode(data) { 33 const inviteRegex = /discord(?:app\.com\/invite|\.gg(?:\/invite)?)\/([\w-]{2,255})/i; 34 const match = inviteRegex.exec(data); 35 if (match && match[1]) return match[1]; 36 return data; 37 } 38 39 /** 40 * Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image. 41 * @param {BufferResolvable|Base64Resolvable} image The image to be resolved 42 * @returns {Promise<?string>} 43 */ 44 static async resolveImage(image) { 45 if (!image) return null; 46 if (typeof image === 'string' && image.startsWith('data:')) { 47 return image; 48 } 49 const file = await this.resolveFileAsBuffer(image); 50 return DataResolver.resolveBase64(file); 51 } 52 53 /** 54 * Data that resolves to give a Base64 string, typically for image uploading. This can be: 55 * * A Buffer 56 * * A base64 string 57 * @typedef {Buffer|string} Base64Resolvable 58 */ 59 60 /** 61 * Resolves a Base64Resolvable to a Base 64 image. 62 * @param {Base64Resolvable} data The base 64 resolvable you want to resolve 63 * @returns {?string} 64 */ 65 static resolveBase64(data) { 66 if (Buffer.isBuffer(data)) return `data:image/jpg;base64,${data.toString('base64')}`; 67 return data; 68 } 69 70 /** 71 * Data that can be resolved to give a Buffer. This can be: 72 * * A Buffer 73 * * The path to a local file 74 * * A URL 75 * @typedef {string|Buffer} BufferResolvable 76 */ 77 78 /** 79 * @external Stream 80 * @see {@link https://nodejs.org/api/stream.html} 81 */ 82 83 /** 84 * Resolves a BufferResolvable to a Buffer or a Stream. 85 * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve 86 * @returns {Promise<Buffer|Stream>} 87 */ 88 static async resolveFile(resource) { 89 if (!browser && Buffer.isBuffer(resource)) return resource; 90 if (browser && resource instanceof ArrayBuffer) return Util.convertToBuffer(resource); 91 if (resource instanceof stream.Readable) return resource; 92 93 if (typeof resource === 'string') { 94 if (/^https?:\/\//.test(resource)) { 95 const res = await fetch(resource); 96 return browser ? res.blob() : res.body; 97 } else if (!browser) { 98 return new Promise((resolve, reject) => { 99 const file = path.resolve(resource); 100 fs.stat(file, (err, stats) => { 101 if (err) return reject(err); 102 if (!stats.isFile()) return reject(new DiscordError('FILE_NOT_FOUND', file)); 103 return resolve(fs.createReadStream(file)); 104 }); 105 }); 106 } 107 } 108 109 throw new TypeError('REQ_RESOURCE_TYPE'); 110 } 111 112 /** 113 * Resolves a BufferResolvable to a Buffer. 114 * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve 115 * @returns {Promise<Buffer>} 116 */ 117 static async resolveFileAsBuffer(resource) { 118 const file = await this.resolveFile(resource); 119 if (Buffer.isBuffer(file)) return file; 120 121 const buffers = []; 122 for await (const data of file) buffers.push(data); 123 return Buffer.concat(buffers); 124 } 125 } 126 127 module.exports = DataResolver;