Files
discord.js/src/client/ClientDataResolver.js
2016-12-28 18:27:02 -05:00

310 lines
9.7 KiB
JavaScript

const path = require('path');
const fs = require('fs');
const request = require('superagent');
const Constants = require('../util/Constants');
const convertArrayBuffer = require('../util/ConvertArrayBuffer');
const User = require('../structures/User');
const Message = require('../structures/Message');
const Guild = require('../structures/Guild');
const Channel = require('../structures/Channel');
const GuildMember = require('../structures/GuildMember');
const Emoji = require('../structures/Emoji');
const ReactionEmoji = require('../structures/ReactionEmoji');
/**
* The DataResolver identifies different objects and tries to resolve a specific piece of information from them, e.g.
* extracting a User from a Message object.
* @private
*/
class ClientDataResolver {
/**
* @param {Client} client The client the resolver is for
*/
constructor(client) {
this.client = client;
}
/**
* Data that resolves to give a User object. This can be:
* * A User object
* * A user ID
* * A Message object (resolves to the message author)
* * A Guild object (owner of the guild)
* * A GuildMember object
* @typedef {User|string|Message|Guild|GuildMember} UserResolvable
*/
/**
* Resolves a UserResolvable to a User object
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?User}
*/
resolveUser(user) {
if (user instanceof User) return user;
if (typeof user === 'string') return this.client.users.get(user) || null;
if (user instanceof GuildMember) return user.user;
if (user instanceof Message) return user.author;
if (user instanceof Guild) return user.owner;
return null;
}
/**
* Resolves a UserResolvable to a user ID string
* @param {UserResolvable} user The UserResolvable to identify
* @returns {?string}
*/
resolveUserID(user) {
if (user instanceof User || user instanceof GuildMember) return user.id;
if (typeof user === 'string') return user || null;
if (user instanceof Message) return user.author.id;
if (user instanceof Guild) return user.ownerID;
return null;
}
/**
* Data that resolves to give a Guild object. This can be:
* * A Guild object
* * A Guild ID
* @typedef {Guild|string} GuildResolvable
*/
/**
* Resolves a GuildResolvable to a Guild object
* @param {GuildResolvable} guild The GuildResolvable to identify
* @returns {?Guild}
*/
resolveGuild(guild) {
if (guild instanceof Guild) return guild;
if (typeof guild === 'string') return this.client.guilds.get(guild) || null;
return null;
}
/**
* Data that resolves to give a GuildMember object. This can be:
* * A GuildMember object
* * A User object
* @typedef {Guild} GuildMemberResolvable
*/
/**
* Resolves a GuildMemberResolvable to a GuildMember object
* @param {GuildResolvable} guild The guild that the member is part of
* @param {UserResolvable} user The user that is part of the guild
* @returns {?GuildMember}
*/
resolveGuildMember(guild, user) {
if (user instanceof GuildMember) return user;
guild = this.resolveGuild(guild);
user = this.resolveUser(user);
if (!guild || !user) return null;
return guild.members.get(user.id) || null;
}
/**
* Data that can be resolved to give a Channel. This can be:
* * A Channel object
* * A Message object (the channel the message was sent in)
* * A Guild object (the #general channel)
* * A channel ID
* @typedef {Channel|Guild|Message|string} ChannelResolvable
*/
/**
* Resolves a ChannelResolvable to a Channel object
* @param {ChannelResolvable} channel The channel resolvable to resolve
* @returns {?Channel}
*/
resolveChannel(channel) {
if (channel instanceof Channel) return channel;
if (channel instanceof Message) return channel.channel;
if (channel instanceof Guild) return channel.channels.get(channel.id) || null;
if (typeof channel === 'string') return this.client.channels.get(channel) || null;
return null;
}
/**
* Data that can be resolved to give an invite code. This can be:
* * An invite code
* * An invite URL
* @typedef {string} InviteResolvable
*/
/**
* Resolves InviteResolvable to an invite code
* @param {InviteResolvable} data The invite resolvable to resolve
* @returns {string}
*/
resolveInviteCode(data) {
const inviteRegex = /discord(?:app)?\.(?:gg|com\/invite)\/([a-z0-9]{5})/i;
const match = inviteRegex.exec(data);
if (match && match[1]) return match[1];
return data;
}
/**
* Data that can be resolved to give a permission number. This can be:
* * A string
* * A permission number
*
* Possible strings:
* ```js
* [
* "CREATE_INSTANT_INVITE",
* "KICK_MEMBERS",
* "BAN_MEMBERS",
* "ADMINISTRATOR",
* "MANAGE_CHANNELS",
* "MANAGE_GUILD",
* "ADD_REACTIONS", // add reactions to messages
* "READ_MESSAGES",
* "SEND_MESSAGES",
* "SEND_TTS_MESSAGES",
* "MANAGE_MESSAGES",
* "EMBED_LINKS",
* "ATTACH_FILES",
* "READ_MESSAGE_HISTORY",
* "MENTION_EVERYONE",
* "EXTERNAL_EMOJIS", // use external emojis
* "CONNECT", // connect to voice
* "SPEAK", // speak on voice
* "MUTE_MEMBERS", // globally mute members on voice
* "DEAFEN_MEMBERS", // globally deafen members on voice
* "MOVE_MEMBERS", // move member's voice channels
* "USE_VAD", // use voice activity detection
* "CHANGE_NICKNAME",
* "MANAGE_NICKNAMES", // change nicknames of others
* "MANAGE_ROLES_OR_PERMISSIONS",
* "MANAGE_WEBHOOKS",
* "MANAGE_EMOJIS"
* ]
* ```
* @typedef {string|number} PermissionResolvable
*/
/**
* Resolves a PermissionResolvable to a permission number
* @param {PermissionResolvable} permission The permission resolvable to resolve
* @returns {number}
*/
resolvePermission(permission) {
if (typeof permission === 'string') permission = Constants.PermissionFlags[permission];
if (typeof permission !== 'number' || permission < 1) throw new Error(Constants.Errors.NOT_A_PERMISSION);
return permission;
}
/**
* Turn an array of permissions into a valid Discord permission bitfield
* @param {PermissionResolvable[]} permissions Permissions to resolve together
* @returns {number}
*/
resolvePermissions(permissions) {
let bitfield = 0;
for (const permission of permissions) bitfield |= this.resolvePermission(permission);
return bitfield;
}
/**
* Data that can be resolved to give a string. This can be:
* * A string
* * An array (joined with a new line delimiter to give a string)
* * Any value
* @typedef {string|Array|*} StringResolvable
*/
/**
* Resolves a StringResolvable to a string
* @param {StringResolvable} data The string resolvable to resolve
* @returns {string}
*/
resolveString(data) {
if (typeof data === 'string') return data;
if (data instanceof Array) return data.join('\n');
return String(data);
}
/**
* 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}
*/
resolveBase64(data) {
if (data instanceof Buffer) 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
* @typedef {string|Buffer} BufferResolvable
*/
/**
* Resolves a BufferResolvable to a Buffer
* @param {BufferResolvable} resource The buffer resolvable to resolve
* @returns {Promise<Buffer>}
*/
resolveBuffer(resource) {
if (resource instanceof Buffer) return Promise.resolve(resource);
if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(convertArrayBuffer(resource));
if (typeof resource === 'string') {
return new Promise((resolve, reject) => {
if (/^https?:\/\//.test(resource)) {
const req = request.get(resource).set('Content-Type', 'blob');
if (this.client.browser) req.responseType('arraybuffer');
req.end((err, res) => {
if (err) return reject(err);
if (this.client.browser) return resolve(convertArrayBuffer(res.xhr.response));
if (!(res.body instanceof Buffer)) return reject(new TypeError('The response body isn\'t a Buffer.'));
return resolve(res.body);
});
} else {
const file = path.resolve(resource);
fs.stat(file, (err, stats) => {
if (err) reject(err);
if (!stats || !stats.isFile()) throw new Error(`The file could not be found: ${file}`);
fs.readFile(file, (err2, data) => {
if (err2) reject(err2); else resolve(data);
});
});
}
});
}
return Promise.reject(new TypeError('The resource must be a string or Buffer.'));
}
/**
* Data that can be resolved to give an emoji identifier. This can be:
* * A string
* * An Emoji
* * A ReactionEmoji
* @typedef {string|Emoji|ReactionEmoji} EmojiIdentifierResolvable
*/
/**
* Resolves an EmojiResolvable to an emoji identifier
* @param {EmojiIdentifierResolvable} emoji The emoji resolvable to resolve
* @returns {string}
*/
resolveEmojiIdentifier(emoji) {
if (emoji instanceof Emoji || emoji instanceof ReactionEmoji) return emoji.identifier;
if (typeof emoji === 'string') {
if (!emoji.includes('%')) return encodeURIComponent(emoji);
}
return null;
}
}
module.exports = ClientDataResolver;