resolveImage backport (#1822)

* add resolveImage

* add groupDMChannel#setIcon + icon getter

* doc fix

* crawl no kill pls

* *whistles*

* channe
This commit is contained in:
Isabella
2017-08-22 22:29:22 -05:00
committed by Crawl
parent a85fc91630
commit 17d7f5c723
8 changed files with 98 additions and 61 deletions

View File

@@ -174,6 +174,20 @@ class ClientDataResolver {
return String(data); return String(data);
} }
/**
* Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
* @param {string|BufferResolvable|Base64Resolvable} image The image to be resolved
* @returns {Promise<string>}
*/
resolveImage(image) {
if (!image) return Promise.resolve(null);
if (typeof image === 'string' && image.startsWith('data:')) {
return Promise.resolve(image);
}
return this.resolveFile(image).then(this.resolveBase64);
}
/** /**
* Data that resolves to give a Base64 string, typically for image uploading. This can be: * Data that resolves to give a Base64 string, typically for image uploading. This can be:
* * A Buffer * * A Buffer
@@ -192,19 +206,25 @@ class ClientDataResolver {
} }
/** /**
* Data that can be resolved to give a Buffer. This can be: * Data that can be resolved to give a Buffer. This can be:
* * A Buffer * * A Buffer
* * The path to a local file * * The path to a local file
* * A URL * * A URL
* @typedef {string|Buffer} BufferResolvable * * A Stream
*/ * @typedef {string|Buffer} BufferResolvable
*/
/** /**
* Resolves a BufferResolvable to a Buffer. * @external Stream
* @param {BufferResolvable} resource The buffer resolvable to resolve * @see {@link https://nodejs.org/api/stream.html}
* @returns {Promise<Buffer>} */
*/
resolveBuffer(resource) { /**
* Resolves a BufferResolvable to a Buffer.
* @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
* @returns {Promise<Buffer>}
*/
resolveFile(resource) {
if (resource instanceof Buffer) return Promise.resolve(resource); if (resource instanceof Buffer) return Promise.resolve(resource);
if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(convertToBuffer(resource)); if (this.client.browser && resource instanceof ArrayBuffer) return Promise.resolve(convertToBuffer(resource));
@@ -229,38 +249,18 @@ class ClientDataResolver {
}); });
} }
}); });
} else if (resource.pipe && typeof resource.pipe === 'function') {
return new Promise((resolve, reject) => {
const buffers = [];
resource.once('error', reject);
resource.on('data', data => buffers.push(data));
resource.once('end', () => resolve(Buffer.concat(buffers)));
});
} }
return Promise.reject(new TypeError('The resource must be a string or Buffer.')); return Promise.reject(new TypeError('The resource must be a string or Buffer.'));
} }
/**
* @external Stream
* @see {@link https://nodejs.org/api/stream.html}
*/
/**
* Converts a Stream to a Buffer.
* @param {Stream} resource The stream to convert
* @returns {Promise<Buffer>}
*/
resolveFile(resource) {
return resource ? this.resolveBuffer(resource)
.catch(() => {
if (resource.pipe && typeof resource.pipe === 'function') {
return new Promise((resolve, reject) => {
const buffers = [];
resource.once('error', reject);
resource.on('data', data => buffers.push(data));
resource.once('end', () => resolve(Buffer.concat(buffers)));
});
} else {
throw new TypeError('The resource must be a string, Buffer or a valid file stream.');
}
}) :
Promise.reject(new TypeError('The resource must be a string, Buffer or a valid file stream.'));
}
/** /**
* Data that can be resolved to give an emoji identifier. This can be: * Data that can be resolved to give an emoji identifier. This can be:
* * The unicode representation of an emoji * * The unicode representation of an emoji

View File

@@ -285,6 +285,13 @@ class RESTMethods {
.then(() => channel); .then(() => channel);
} }
updateGroupDMChannel(channel, _data) {
const data = {};
data.name = _data.name;
data.icon = _data.icon;
return this.rest.makeRequest('patch', Endpoints.Channel(channel), true, data).then(() => channel);
}
getExistingDM(recipient) { getExistingDM(recipient) {
return this.client.channels.find(channel => return this.client.channels.find(channel =>
channel.recipient && channel.recipient.id === recipient.id channel.recipient && channel.recipient.id === recipient.id

View File

@@ -152,13 +152,9 @@ class ClientUser extends User {
* .catch(console.error); * .catch(console.error);
*/ */
setAvatar(avatar) { setAvatar(avatar) {
if (typeof avatar === 'string' && avatar.startsWith('data:')) { return this.client.resolver.resolveImage(avatar).then(data =>
return this.client.rest.methods.updateCurrentUser({ avatar }); this.client.rest.methods.updateCurrentUser({ avatar: data })
} else { );
return this.client.resolver.resolveBuffer(avatar).then(data =>
this.client.rest.methods.updateCurrentUser({ avatar: data })
);
}
} }
/** /**
@@ -337,7 +333,7 @@ class ClientUser extends User {
}, reject) }, reject)
); );
} else { } else {
return this.client.resolver.resolveBuffer(icon) return this.client.resolver.resolveFile(icon)
.then(data => this.createGuild(name, { region, icon: this.client.resolver.resolveBase64(data) || null })); .then(data => this.createGuild(name, { region, icon: this.client.resolver.resolveBase64(data) || null }));
} }
} }

View File

@@ -1,6 +1,7 @@
const Channel = require('./Channel'); const Channel = require('./Channel');
const TextBasedChannel = require('./interfaces/TextBasedChannel'); const TextBasedChannel = require('./interfaces/TextBasedChannel');
const Collection = require('../util/Collection'); const Collection = require('../util/Collection');
const Constants = require('../util/Constants');
/* /*
{ type: 3, { type: 3,
@@ -105,6 +106,23 @@ class GroupDMChannel extends Channel {
return this.client.users.get(this.ownerID); return this.client.users.get(this.ownerID);
} }
/**
* The URL to this guild's icon
* @type {?string}
* @readonly
*/
get iconURL() {
if (!this.icon) return null;
return Constants.Endpoints.Channel(this).Icon(this.client.options.http.cdn, this.icon);
}
edit(data) {
const _data = {};
if (data.name) _data.name = data.name;
if (data.icon) _data.icon = data.icon;
return this.client.rest.methods.updateGroupDMChannel(this, _data);
}
/** /**
* Whether this channel equals another channel. It compares all properties, so for most operations * Whether this channel equals another channel. It compares all properties, so for most operations
* it is advisable to just compare `channel.id === channel2.id` as it is much faster and is often * it is advisable to just compare `channel.id === channel2.id` as it is much faster and is often
@@ -140,6 +158,20 @@ class GroupDMChannel extends Channel {
}); });
} }
/**
* Set a new GroupDMChannel icon.
* @param {Base64Resolvable|BufferResolvable} icon The new icon of the group dm
* @returns {Promise<Guild>}
* @example
* // Edit the group dm icon
* channel.setIcon('./icon.png')
* .then(updated => console.log('Updated the channel icon'))
* .catch(console.error);
*/
setIcon(icon) {
return this.client.resolver.resolveImage(icon).then(data => this.edit({ icon: data }));
}
/** /**
* When concatenated with a string, this automatically concatenates the channel's name instead of the Channel object. * When concatenated with a string, this automatically concatenates the channel's name instead of the Channel object.
* @returns {string} * @returns {string}

View File

@@ -615,9 +615,9 @@ class Guild {
if (data.afkChannel) _data.afk_channel_id = this.client.resolver.resolveChannel(data.afkChannel).id; if (data.afkChannel) _data.afk_channel_id = this.client.resolver.resolveChannel(data.afkChannel).id;
if (data.systemChannel) _data.system_channel_id = this.client.resolver.resolveChannel(data.systemChannel).id; if (data.systemChannel) _data.system_channel_id = this.client.resolver.resolveChannel(data.systemChannel).id;
if (data.afkTimeout) _data.afk_timeout = Number(data.afkTimeout); if (data.afkTimeout) _data.afk_timeout = Number(data.afkTimeout);
if (data.icon) _data.icon = this.client.resolver.resolveBase64(data.icon); if (data.icon) _data.icon = data.icon;
if (data.owner) _data.owner_id = this.client.resolver.resolveUser(data.owner).id; if (data.owner) _data.owner_id = this.client.resolver.resolveUser(data.owner).id;
if (data.splash) _data.splash = this.client.resolver.resolveBase64(data.splash); if (data.splash) _data.splash = data.splash;
if (typeof data.explicitContentFilter !== 'undefined') { if (typeof data.explicitContentFilter !== 'undefined') {
_data.explicit_content_filter = Number(data.explicitContentFilter); _data.explicit_content_filter = Number(data.explicitContentFilter);
} }
@@ -721,17 +721,17 @@ class Guild {
/** /**
* Set a new guild icon. * Set a new guild icon.
* @param {Base64Resolvable} icon The new icon of the guild * @param {Base64Resolvable|BufferResolvable} icon The new icon of the guild
* @param {string} [reason] Reason for changing the guild's icon * @param {string} [reason] Reason for changing the guild's icon
* @returns {Promise<Guild>} * @returns {Promise<Guild>}
* @example * @example
* // Edit the guild icon * // Edit the guild icon
* guild.setIcon(fs.readFileSync('./icon.png')) * guild.setIcon('./icon.png')
* .then(updated => console.log('Updated the guild icon')) * .then(updated => console.log('Updated the guild icon'))
* .catch(console.error); * .catch(console.error);
*/ */
setIcon(icon, reason) { setIcon(icon, reason) {
return this.edit({ icon }, reason); return this.client.resolver.resolveImage(icon).then(data => this.edit({ icon: data, reason }));
} }
/** /**
@@ -751,17 +751,17 @@ class Guild {
/** /**
* Set a new guild splash screen. * Set a new guild splash screen.
* @param {Base64Resolvable} splash The new splash screen of the guild * @param {BufferResolvable|Base64Resolvable} splash The new splash screen of the guild
* @param {string} [reason] Reason for changing the guild's splash screen * @param {string} [reason] Reason for changing the guild's splash screen
* @returns {Promise<Guild>} * @returns {Promise<Guild>}
* @example * @example
* // Edit the guild splash * // Edit the guild splash
* guild.setIcon(fs.readFileSync('./splash.png')) * guild.setSplash('./splash.png')
* .then(updated => console.log('Updated the guild splash')) * .then(updated => console.log('Updated the guild splash'))
* .catch(console.error); * .catch(console.error);
*/ */
setSplash(splash, reason) { setSplash(splash) {
return this.edit({ splash }, reason); return this.client.resolver.resolveImage(splash).then(data => this.edit({ splash: data }));
} }
/** /**
@@ -952,7 +952,7 @@ class Guild {
if (typeof attachment === 'string' && attachment.startsWith('data:')) { if (typeof attachment === 'string' && attachment.startsWith('data:')) {
resolve(this.client.rest.methods.createEmoji(this, attachment, name, roles, reason)); resolve(this.client.rest.methods.createEmoji(this, attachment, name, roles, reason));
} else { } else {
this.client.resolver.resolveBuffer(attachment).then(data => { this.client.resolver.resolveFile(attachment).then(data => {
const dataURI = this.client.resolver.resolveBase64(data); const dataURI = this.client.resolver.resolveBase64(data);
resolve(this.client.rest.methods.createEmoji(this, dataURI, name, roles, reason)); resolve(this.client.rest.methods.createEmoji(this, dataURI, name, roles, reason));
}); });

View File

@@ -73,7 +73,7 @@ class TextChannel extends GuildChannel {
if (typeof avatar === 'string' && avatar.startsWith('data:')) { if (typeof avatar === 'string' && avatar.startsWith('data:')) {
resolve(this.client.rest.methods.createWebhook(this, name, avatar, reason)); resolve(this.client.rest.methods.createWebhook(this, name, avatar, reason));
} else { } else {
this.client.resolver.resolveBuffer(avatar).then(data => this.client.resolver.resolveFile(avatar).then(data =>
resolve(this.client.rest.methods.createWebhook(this, name, data, reason)) resolve(this.client.rest.methods.createWebhook(this, name, data, reason))
); );
} }

View File

@@ -247,7 +247,7 @@ class Webhook {
*/ */
edit(name = this.name, avatar) { edit(name = this.name, avatar) {
if (avatar) { if (avatar) {
return this.client.resolver.resolveBuffer(avatar).then(file => { return this.client.resolver.resolveFile(avatar).then(file => {
const dataURI = this.client.resolver.resolveBase64(file); const dataURI = this.client.resolver.resolveBase64(file);
return this.client.rest.methods.editWebhook(this, name, dataURI); return this.client.rest.methods.editWebhook(this, name, dataURI);
}); });

View File

@@ -167,6 +167,7 @@ const Endpoints = exports.Endpoints = {
webhooks: `${base}/webhooks`, webhooks: `${base}/webhooks`,
search: `${base}/messages/search`, search: `${base}/messages/search`,
pins: `${base}/pins`, pins: `${base}/pins`,
Icon: (root, hash) => Endpoints.CDN(root).GDMIcon(channelID, hash),
Pin: messageID => `${base}/pins/${messageID}`, Pin: messageID => `${base}/pins/${messageID}`,
Recipient: recipientID => `${base}/recipients/${recipientID}`, Recipient: recipientID => `${base}/recipients/${recipientID}`,
Message: messageID => { Message: messageID => {
@@ -195,6 +196,7 @@ const Endpoints = exports.Endpoints = {
Asset: name => `${root}/assets/${name}`, Asset: name => `${root}/assets/${name}`,
Avatar: (userID, hash) => `${root}/avatars/${userID}/${hash}.${hash.startsWith('a_') ? 'gif' : 'png'}?size=2048`, Avatar: (userID, hash) => `${root}/avatars/${userID}/${hash}.${hash.startsWith('a_') ? 'gif' : 'png'}?size=2048`,
Icon: (guildID, hash) => `${root}/icons/${guildID}/${hash}.jpg`, Icon: (guildID, hash) => `${root}/icons/${guildID}/${hash}.jpg`,
GDMIcon: (channelID, hash) => `${root}/channel-icons/${channelID}/${hash}.jpg?size=2048`,
Splash: (guildID, hash) => `${root}/splashes/${guildID}/${hash}.jpg`, Splash: (guildID, hash) => `${root}/splashes/${guildID}/${hash}.jpg`,
}; };
}, },