DataResolver.js
4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
'use strict';
const { Buffer } = require('node:buffer');
const fs = require('node:fs');
const path = require('node:path');
const stream = require('node:stream');
const fetch = require('node-fetch');
const { Error: DiscordError, TypeError } = require('../errors');
const Invite = require('../structures/Invite');
/**
* The DataResolver identifies different objects and tries to resolve a specific piece of information from them.
* @private
*/
class DataResolver extends null {
/**
* Data that can be resolved to give an invite code. This can be:
* * An invite code
* * An invite URL
* @typedef {string} InviteResolvable
*/
/**
* Data that can be resolved to give a template code. This can be:
* * A template code
* * A template URL
* @typedef {string} GuildTemplateResolvable
*/
/**
* Resolves the string to a code based on the passed regex.
* @param {string} data The string to resolve
* @param {RegExp} regex The RegExp used to extract the code
* @returns {string}
*/
static resolveCode(data, regex) {
return data.matchAll(regex).next().value?.[1] ?? data;
}
/**
* Resolves InviteResolvable to an invite code.
* @param {InviteResolvable} data The invite resolvable to resolve
* @returns {string}
*/
static resolveInviteCode(data) {
return this.resolveCode(data, Invite.INVITES_PATTERN);
}
/**
* Resolves GuildTemplateResolvable to a template code.
* @param {GuildTemplateResolvable} data The template resolvable to resolve
* @returns {string}
*/
static resolveGuildTemplateCode(data) {
const GuildTemplate = require('../structures/GuildTemplate');
return this.resolveCode(data, GuildTemplate.GUILD_TEMPLATES_PATTERN);
}
/**
* Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
* @param {BufferResolvable|Base64Resolvable} image The image to be resolved
* @returns {Promise<?string>}
*/
static async resolveImage(image) {
if (!image) return null;
if (typeof image === 'string' && image.startsWith('data:')) {
return image;
}
const file = await this.resolveFileAsBuffer(image);
return DataResolver.resolveBase64(file);
}
/**
* Data that resolves to give a Base64 string, typically for image uploading. This can be:
* * A Buffer
* * A base64 string
* @typedef {Buffer|string} Base64Resolvable
*/
/**
* Resolves a Base64Resolvable to a Base 64 image.
* @param {Base64Resolvable} data The base 64 resolvable you want to resolve
* @returns {?string}
*/
static resolveBase64(data) {
if (Buffer.isBuffer(data)) return `data:image/jpg;base64,${data.toString('base64')}`;
return data;
}
/**
* Data that can be resolved to give a Buffer. This can be:
* * A Buffer
* * The path to a local file
* * A URL <warn>When provided a URL, discord.js will fetch the URL internally in order to create a Buffer.
* This can pose a security risk when the URL has not been sanitized</warn>
* @typedef {string|Buffer} BufferResolvable
*/
/**
* @external Stream
* @see {@link https://nodejs.org/api/stream.html}
*/
/**
* Resolves a BufferResolvable to a Buffer or a Stream.
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
* @returns {Promise<Buffer|Stream>}
*/
static async resolveFile(resource) {
if (Buffer.isBuffer(resource) || resource instanceof stream.Readable) return resource;
if (typeof resource === 'string') {
if (/^https?:\/\//.test(resource)) {
const res = await fetch(resource);
return res.body;
}
return new Promise((resolve, reject) => {
const file = path.resolve(resource);
fs.stat(file, (err, stats) => {
if (err) return reject(err);
if (!stats.isFile()) return reject(new DiscordError('FILE_NOT_FOUND', file));
return resolve(fs.createReadStream(file));
});
});
}
throw new TypeError('REQ_RESOURCE_TYPE');
}
/**
* Resolves a BufferResolvable to a Buffer.
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
* @returns {Promise<Buffer>}
*/
static async resolveFileAsBuffer(resource) {
const file = await this.resolveFile(resource);
if (Buffer.isBuffer(file)) return file;
const buffers = [];
for await (const data of file) buffers.push(data);
return Buffer.concat(buffers);
}
}
module.exports = DataResolver;