REST API speed improvement (#1577)

This commit is contained in:
Gus Caplan
2017-07-01 04:14:17 -05:00
committed by Amish Shah
parent 6bc7b3e068
commit 5ecd5f7d69
25 changed files with 114 additions and 109 deletions

View File

@@ -50,13 +50,6 @@ class Client extends EventEmitter {
*/ */
this.rest = new RESTManager(this); this.rest = new RESTManager(this);
/**
* API shortcut
* @type {Object}
* @private
*/
this.api = this.rest.api;
/** /**
* The data manager of the client * The data manager of the client
* @type {ClientDataManager} * @type {ClientDataManager}
@@ -198,6 +191,15 @@ class Client extends EventEmitter {
return this.ws.connection ? this.ws.connection.lastPingTimestamp : 0; return this.ws.connection ? this.ws.connection.lastPingTimestamp : 0;
} }
/**
* API shortcut
* @type {Object}
* @private
*/
get api() {
return this.rest.api;
}
/** /**
* Current status of the client's connection to Discord * Current status of the client's connection to Discord
* @type {?Status} * @type {?Status}
@@ -330,7 +332,7 @@ class Client extends EventEmitter {
*/ */
fetchUser(id, cache = true) { fetchUser(id, cache = true) {
if (this.users.has(id)) return Promise.resolve(this.users.get(id)); if (this.users.has(id)) return Promise.resolve(this.users.get(id));
return this.api.users(id).get().then(data => return this.api.users[id].get().then(data =>
cache ? this.dataManager.newUser(data) : new User(this, data) cache ? this.dataManager.newUser(data) : new User(this, data)
); );
} }
@@ -342,7 +344,7 @@ class Client extends EventEmitter {
*/ */
fetchInvite(invite) { fetchInvite(invite) {
const code = this.resolver.resolveInviteCode(invite); const code = this.resolver.resolveInviteCode(invite);
return this.api.invites(code).get({ query: { with_counts: true } }) return this.api.invites[code].get({ query: { with_counts: true } })
.then(data => new Invite(this, data)); .then(data => new Invite(this, data));
} }
@@ -353,7 +355,7 @@ class Client extends EventEmitter {
* @returns {Promise<Webhook>} * @returns {Promise<Webhook>}
*/ */
fetchWebhook(id, token) { fetchWebhook(id, token) {
return this.api.webhooks(id, token).get().then(data => new Webhook(this, data)); return this.api.webhooks.opts(id, token).get().then(data => new Webhook(this, data));
} }
/** /**

View File

@@ -34,13 +34,6 @@ class WebhookClient extends Webhook {
*/ */
this.rest = new RESTManager(this); this.rest = new RESTManager(this);
/**
* API shortcut
* @type {Object}
* @private
*/
this.api = this.rest.api;
/** /**
* The data resolver of the client * The data resolver of the client
* @type {ClientDataResolver} * @type {ClientDataResolver}
@@ -63,6 +56,15 @@ class WebhookClient extends Webhook {
this._intervals = new Set(); this._intervals = new Set();
} }
/**
* API shortcut
* @type {Object}
* @private
*/
get api() {
return this.rest.api;
}
/** /**
* Sets a timeout that will be automatically cancelled if the client is destroyed. * Sets a timeout that will be automatically cancelled if the client is destroyed.
* @param {Function} fn Function to execute * @param {Function} fn Function to execute

View File

@@ -1,37 +1,30 @@
const util = require('util'); const util = require('util');
const methods = ['get', 'post', 'delete', 'patch', 'put']; const methods = ['get', 'post', 'delete', 'patch', 'put'];
// Paramable exists so we don't return a function unless we actually need one #savingmemory const reflectors = [
const paramable = [ 'toString', 'valueOf', 'inspect', 'constructor',
'channels', 'users', 'guilds', 'members', Symbol.toPrimitive, util.inspect.custom,
'bans', 'emojis', 'pins', 'permissions',
'reactions', 'webhooks', 'messages',
'notes', 'roles', 'applications',
'invites', 'bot',
]; ];
const reflectors = ['toString', 'valueOf', 'inspect', Symbol.toPrimitive, util.inspect.custom];
module.exports = restManager => { module.exports = restManager => {
const handler = { const handler = {
get(list, name) { get(list, name) {
if (reflectors.includes(name)) return () => list.join('/'); if (name === 'opts') {
if (paramable.includes(name)) {
function toReturn(...args) { // eslint-disable-line no-inner-declarations function toReturn(...args) { // eslint-disable-line no-inner-declarations
list = list.concat(name); list.push(...args.filter(x => x !== null && typeof x !== 'undefined'));
for (const arg of args) {
if (arg !== null && typeof arg !== 'undefined') list = list.concat(arg);
}
return new Proxy(list, handler); return new Proxy(list, handler);
} }
const directJoin = () => `${list.join('/')}/${name}`; const directJoin = () => `${list.join('/')}/${name}`;
for (const r of reflectors) toReturn[r] = directJoin; for (const r of reflectors) toReturn[r] = directJoin;
for (const method of methods) { for (const method of methods) {
toReturn[method] = options => restManager.request(method, `${list.join('/')}/${name}`, options); toReturn[method] = options => restManager.request(method, directJoin(), options);
} }
return toReturn; return toReturn;
} }
if (reflectors.includes(name)) return () => list.join('/');
if (methods.includes(name)) return options => restManager.request(name, list.join('/'), options); if (methods.includes(name)) return options => restManager.request(name, list.join('/'), options);
return new Proxy(list.concat(name), handler); list.push(name);
return new Proxy(list, handler);
}, },
}; };

View File

@@ -3,12 +3,18 @@
* @extends Error * @extends Error
*/ */
class DiscordAPIError extends Error { class DiscordAPIError extends Error {
constructor(error) { constructor(path, error) {
super(); super();
const flattened = error.errors ? `\n${this.constructor.flattenErrors(error.errors).join('\n')}` : ''; const flattened = error.errors ? `\n${this.constructor.flattenErrors(error.errors).join('\n')}` : '';
this.name = 'DiscordAPIError'; this.name = 'DiscordAPIError';
this.message = `${error.message}${flattened}`; this.message = `${error.message}${flattened}`;
/**
* The path of the request relative to the HTTP endpoint
* @type {string}
*/
this.path = path;
/** /**
* HTTP error code returned by Discord * HTTP error code returned by Discord
* @type {number} * @type {number}

View File

@@ -12,8 +12,10 @@ class RESTManager {
this.userAgentManager = new UserAgentManager(this); this.userAgentManager = new UserAgentManager(this);
this.rateLimitedEndpoints = {}; this.rateLimitedEndpoints = {};
this.globallyRateLimited = false; this.globallyRateLimited = false;
}
this.api = mountApi(this); get api() {
return mountApi(this);
} }
destroy() { destroy() {

View File

@@ -41,7 +41,7 @@ class BurstRequestHandler extends RequestHandler {
this.resetTimeout = null; this.resetTimeout = null;
}, Number(res.headers['retry-after']) + this.client.options.restTimeOffset); }, Number(res.headers['retry-after']) + this.client.options.restTimeOffset);
} else { } else {
item.reject(err.status === 400 ? new DiscordAPIError(res.body) : err); item.reject(err.status === 400 ? new DiscordAPIError(res.request.path, res.body) : err);
this.handle(); this.handle();
} }
} else { } else {

View File

@@ -65,7 +65,7 @@ class SequentialRequestHandler extends RequestHandler {
}, Number(res.headers['retry-after']) + this.restManager.client.options.restTimeOffset); }, Number(res.headers['retry-after']) + this.restManager.client.options.restTimeOffset);
if (res.headers['x-ratelimit-global']) this.globalLimit = true; if (res.headers['x-ratelimit-global']) this.globalLimit = true;
} else { } else {
item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.body) : err); item.reject(err.status >= 400 && err.status < 500 ? new DiscordAPIError(res.request.path, res.body) : err);
resolve(err); resolve(err);
} }
} else { } else {

View File

@@ -62,7 +62,7 @@ class Channel {
* .catch(console.error); // Log error * .catch(console.error); // Log error
*/ */
delete() { delete() {
return this.client.api.channels(this.id).delete().then(() => this); return this.client.api.channels[this.id].delete().then(() => this);
} }
} }

View File

@@ -91,7 +91,7 @@ class ClientUser extends User {
if (data.new_password) _data.new_password = data.newPassword; if (data.new_password) _data.new_password = data.newPassword;
} }
return this.client.api.users('@me').patch({ data }) return this.client.api.users['@me'].patch({ data })
.then(newData => this.client.actions.UserUpdate.handle(newData).updated); .then(newData => this.client.actions.UserUpdate.handle(newData).updated);
} }
@@ -284,7 +284,7 @@ class ClientUser extends User {
if (options.guild instanceof Guild) options.guild = options.guild.id; if (options.guild instanceof Guild) options.guild = options.guild.id;
Util.mergeDefault({ limit: 25, roles: true, everyone: true, guild: null }, options); Util.mergeDefault({ limit: 25, roles: true, everyone: true, guild: null }, options);
return this.client.api.users('@me').mentions.get({ query: options }) return this.client.api.users['@me'].mentions.get({ query: options })
.then(data => data.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))); .then(data => data.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client)));
} }
@@ -351,7 +351,7 @@ class ClientUser extends User {
return o; return o;
}, {}), }, {}),
} : { recipients: recipients.map(u => this.client.resolver.resolveUserID(u)) }; } : { recipients: recipients.map(u => this.client.resolver.resolveUserID(u)) };
return this.client.api.users('@me').channels.post({ data }) return this.client.api.users['@me'].channels.post({ data })
.then(res => new GroupDMChannel(this.client, res)); .then(res => new GroupDMChannel(this.client, res));
} }
} }

View File

@@ -32,7 +32,7 @@ class ClientUserSettings {
* @returns {Promise<Object>} * @returns {Promise<Object>}
*/ */
update(name, value) { update(name, value) {
return this.user.client.api.users('@me').settings.patch({ data: { [name]: value } }); return this.user.client.api.users['@me'].settings.patch({ data: { [name]: value } });
} }
/** /**

View File

@@ -121,7 +121,7 @@ class Emoji {
* .catch(console.error); * .catch(console.error);
*/ */
edit(data, reason) { edit(data, reason) {
return this.client.api.guilds(this.guild.id).emojis(this.id) return this.client.api.guilds[this.guild.id].emojis(this.id)
.patch({ data: { .patch({ data: {
name: data.name, name: data.name,
roles: data.roles ? data.roles.map(r => r.id ? r.id : r) : [], roles: data.roles ? data.roles.map(r => r.id ? r.id : r) : [],

View File

@@ -135,7 +135,7 @@ class GroupDMChannel extends Channel {
const data = this.client.user.bot ? const data = this.client.user.bot ?
{ nick, access_token: accessTokenOrUser } : { nick, access_token: accessTokenOrUser } :
{ recipient: id }; { recipient: id };
return this.client.api.channels(this.id).recipients(id).put({ data }) return this.client.api.channels[this.id].recipients[id].put({ data })
.then(() => this); .then(() => this);
} }

View File

@@ -378,7 +378,7 @@ class Guild {
* @returns {Promise<Collection<Snowflake, Object>>} * @returns {Promise<Collection<Snowflake, Object>>}
*/ */
fetchBans() { fetchBans() {
return this.client.api.guilds(this.id).bans.get().then(bans => return this.client.api.guilds[this.id].bans.get().then(bans =>
bans.reduce((collection, ban) => { bans.reduce((collection, ban) => {
collection.set(ban.user.id, { collection.set(ban.user.id, {
reason: ban.reason, reason: ban.reason,
@@ -394,7 +394,7 @@ class Guild {
* @returns {Promise<Collection<string, Invite>>} * @returns {Promise<Collection<string, Invite>>}
*/ */
fetchInvites() { fetchInvites() {
return this.client.api.guilds(this.id).invites.get() return this.client.api.guilds[this.id].invites.get()
.then(inviteItems => { .then(inviteItems => {
const invites = new Collection(); const invites = new Collection();
for (const inviteItem of inviteItems) { for (const inviteItem of inviteItems) {
@@ -410,7 +410,7 @@ class Guild {
* @returns {Collection<Snowflake, Webhook>} * @returns {Collection<Snowflake, Webhook>}
*/ */
fetchWebhooks() { fetchWebhooks() {
return this.client.api.guilds(this.id).webhooks.get().then(data => { return this.client.api.guilds[this.id].webhooks.get().then(data => {
const hooks = new Collection(); const hooks = new Collection();
for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
return hooks; return hooks;
@@ -422,7 +422,7 @@ class Guild {
* @returns {Collection<string, VoiceRegion>} * @returns {Collection<string, VoiceRegion>}
*/ */
fetchVoiceRegions() { fetchVoiceRegions() {
return this.client.api.guilds(this.id).regions.get().then(res => { return this.client.api.guilds[this.id].regions.get().then(res => {
const regions = new Collection(); const regions = new Collection();
for (const region of res) regions.set(region.id, new VoiceRegion(region)); for (const region of res) regions.set(region.id, new VoiceRegion(region));
return regions; return regions;
@@ -444,7 +444,7 @@ class Guild {
if (options.after && options.after instanceof GuildAuditLogs.Entry) options.after = options.after.id; if (options.after && options.after instanceof GuildAuditLogs.Entry) options.after = options.after.id;
if (typeof options.type === 'string') options.type = GuildAuditLogs.Actions[options.type]; if (typeof options.type === 'string') options.type = GuildAuditLogs.Actions[options.type];
return this.client.api.guilds(this.id)['audit-logs'].get({ query: { return this.client.api.guilds[this.id]['audit-logs'].get({ query: {
before: options.before, before: options.before,
after: options.after, after: options.after,
limit: options.limit, limit: options.limit,
@@ -476,7 +476,7 @@ class Guild {
options.roles = roles.map(role => role.id); options.roles = roles.map(role => role.id);
} }
} }
return this.client.api.guilds(this.id).members(user.id).put({ data: options }) return this.client.api.guilds[this.id].members[user.id].put({ data: options })
.then(data => this.client.actions.GuildMemberGet.handle(this, data).member); .then(data => this.client.actions.GuildMemberGet.handle(this, data).member);
} }
@@ -490,7 +490,7 @@ class Guild {
user = this.client.resolver.resolveUser(user); user = this.client.resolver.resolveUser(user);
if (!user) return Promise.reject(new Error('User is not cached. Use Client.fetchUser first.')); if (!user) return Promise.reject(new Error('User is not cached. Use Client.fetchUser first.'));
if (this.members.has(user.id)) return Promise.resolve(this.members.get(user.id)); if (this.members.has(user.id)) return Promise.resolve(this.members.get(user.id));
return this.client.api.guilds(this.id).members(user.id).get() return this.client.api.guilds[this.id].members[user.id].get()
.then(data => { .then(data => {
if (cache) return this.client.actions.GuildMemberGet.handle(this, data).member; if (cache) return this.client.actions.GuildMemberGet.handle(this, data).member;
else return new GuildMember(this, data); else return new GuildMember(this, data);
@@ -596,7 +596,7 @@ class Guild {
if (typeof data.explicitContentFilter !== 'undefined') { if (typeof data.explicitContentFilter !== 'undefined') {
_data.explicit_content_filter = Number(data.explicitContentFilter); _data.explicit_content_filter = Number(data.explicitContentFilter);
} }
return this.client.api.guilds(this.id).patch({ data: _data, reason }) return this.client.api.guilds[this.id].patch({ data: _data, reason })
.then(newData => this.client.actions.GuildUpdate.handle(newData).updated); .then(newData => this.client.actions.GuildUpdate.handle(newData).updated);
} }
@@ -741,7 +741,7 @@ class Guild {
* @returns {Promise<Guild>} * @returns {Promise<Guild>}
*/ */
acknowledge() { acknowledge() {
return this.client.api.guilds(this.id).ack return this.client.api.guilds[this.id].ack
.post({ data: { token: this.client.rest._ackToken } }) .post({ data: { token: this.client.rest._ackToken } })
.then(res => { .then(res => {
if (res.token) this.client.rest._ackToken = res.token; if (res.token) this.client.rest._ackToken = res.token;
@@ -780,7 +780,7 @@ class Guild {
if (options.days) options['delete-message-days'] = options.days; if (options.days) options['delete-message-days'] = options.days;
const id = this.client.resolver.resolveUserID(user); const id = this.client.resolver.resolveUserID(user);
if (!id) return Promise.reject(new Error('Couldn\'t resolve the user ID to ban.')); if (!id) return Promise.reject(new Error('Couldn\'t resolve the user ID to ban.'));
return this.client.api.guilds(this.id).bans(id).put({ query: options }) return this.client.api.guilds[this.id].bans[id].put({ query: options })
.then(() => { .then(() => {
if (user instanceof GuildMember) return user; if (user instanceof GuildMember) return user;
const _user = this.client.resolver.resolveUser(id); const _user = this.client.resolver.resolveUser(id);
@@ -806,7 +806,7 @@ class Guild {
unban(user, reason) { unban(user, reason) {
const id = this.client.resolver.resolveUserID(user); const id = this.client.resolver.resolveUserID(user);
if (!id) throw new Error('BAN_RESOLVE_ID'); if (!id) throw new Error('BAN_RESOLVE_ID');
return this.client.api.guilds(this.id).bans(id).delete({ reason }) return this.client.api.guilds(this.id).bans[id].delete({ reason })
.then(() => user); .then(() => user);
} }
@@ -829,7 +829,7 @@ class Guild {
*/ */
pruneMembers({ days = 7, dry = false, reason } = {}) { pruneMembers({ days = 7, dry = false, reason } = {}) {
if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE'); if (typeof days !== 'number') throw new TypeError('PRUNE_DAYS_TYPE');
return this.client.api.guilds(this.id).prune[dry ? 'get' : 'post']({ query: { days }, reason }) return this.client.api.guilds[this.id].prune[dry ? 'get' : 'post']({ query: { days }, reason })
.then(data => data.pruned); .then(data => data.pruned);
} }
@@ -864,7 +864,7 @@ class Guild {
id: overwrite.id, id: overwrite.id,
})); }));
} }
return this.client.api.guilds(this.id).channels.post({ return this.client.api.guilds[this.id].channels.post({
data: { data: {
name, type, permission_overwrites: overwrites, name, type, permission_overwrites: overwrites,
}, },
@@ -897,7 +897,7 @@ class Guild {
}; };
} }
return this.client.api.guilds(this.id).channels.patch({ data: { return this.client.api.guilds[this.id].channels.patch({ data: {
guild_id: this.id, guild_id: this.id,
channels: channelPositions, channels: channelPositions,
} }).then(() => } }).then(() =>
@@ -935,7 +935,7 @@ class Guild {
if (data.color) data.color = Util.resolveColor(data.color); if (data.color) data.color = Util.resolveColor(data.color);
if (data.permissions) data.permissions = Permissions.resolve(data.permissions); if (data.permissions) data.permissions = Permissions.resolve(data.permissions);
return this.client.api.guilds(this.id).roles.post({ data, reason }).then(role => return this.client.api.guilds[this.id].roles.post({ data, reason }).then(role =>
this.client.actions.GuildRoleCreate.handle({ this.client.actions.GuildRoleCreate.handle({
guild_id: this.id, guild_id: this.id,
role, role,
@@ -964,7 +964,7 @@ class Guild {
if (typeof attachment === 'string' && attachment.startsWith('data:')) { if (typeof attachment === 'string' && attachment.startsWith('data:')) {
const data = { image: attachment, name }; const data = { image: attachment, name };
if (roles) data.roles = roles.map(r => r.id ? r.id : r); if (roles) data.roles = roles.map(r => r.id ? r.id : r);
return this.client.api.guilds(this.id).emojis.post({ data }) return this.client.api.guilds[this.id].emojis.post({ data })
.then(emoji => this.client.actions.GuildEmojiCreate.handle(this, emoji).emoji); .then(emoji => this.client.actions.GuildEmojiCreate.handle(this, emoji).emoji);
} else { } else {
return this.client.resolver.resolveBuffer(attachment) return this.client.resolver.resolveBuffer(attachment)
@@ -982,7 +982,7 @@ class Guild {
*/ */
deleteEmoji(emoji) { deleteEmoji(emoji) {
if (!(emoji instanceof Emoji)) emoji = this.emojis.get(emoji); if (!(emoji instanceof Emoji)) emoji = this.emojis.get(emoji);
return this.client.api.guilds(this.id).emojis(emoji.id).delete() return this.client.api.guilds(this.id).emojis[emoji.id].delete()
.then(() => this.client.actions.GuildEmojiDelete.handle(emoji).data); .then(() => this.client.actions.GuildEmojiDelete.handle(emoji).data);
} }
@@ -997,7 +997,7 @@ class Guild {
*/ */
leave() { leave() {
if (this.ownerID === this.client.user.id) return Promise.reject(new Error('Guild is owned by the client.')); if (this.ownerID === this.client.user.id) return Promise.reject(new Error('Guild is owned by the client.'));
return this.client.api.users('@me').guilds(this.id).delete() return this.client.api.users['@me'].guilds[this.id].delete()
.then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild); .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild);
} }
@@ -1011,7 +1011,7 @@ class Guild {
* .catch(console.error); * .catch(console.error);
*/ */
delete() { delete() {
return this.client.api.guilds(this.id).delete() return this.client.api.guilds[this.id].delete()
.then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild); .then(() => this.client.actions.GuildDelete.handle({ id: this.id }).guild);
} }
@@ -1169,7 +1169,7 @@ class Guild {
Util.moveElementInArray(updatedRoles, role, position, relative); Util.moveElementInArray(updatedRoles, role, position, relative);
updatedRoles = updatedRoles.map((r, i) => ({ id: r.id, position: i })); updatedRoles = updatedRoles.map((r, i) => ({ id: r.id, position: i }));
return this.client.api.guilds(this.id).roles.patch({ data: updatedRoles }) return this.client.api.guilds[this.id].roles.patch({ data: updatedRoles })
.then(() => .then(() =>
this.client.actions.GuildRolesPositionUpdate.handle({ this.client.actions.GuildRolesPositionUpdate.handle({
guild_id: this.id, guild_id: this.id,
@@ -1199,7 +1199,7 @@ class Guild {
Util.moveElementInArray(updatedChannels, channel, position, relative); Util.moveElementInArray(updatedChannels, channel, position, relative);
updatedChannels = updatedChannels.map((r, i) => ({ id: r.id, position: i })); updatedChannels = updatedChannels.map((r, i) => ({ id: r.id, position: i }));
return this.client.api.guilds(this.id).channels.patch({ data: updatedChannels }) return this.client.api.guilds[this.id].channels.patch({ data: updatedChannels })
.then(() => .then(() =>
this.client.actions.GuildChannelsPositionUpdate.handle({ this.client.actions.GuildChannelsPositionUpdate.handle({
guild_id: this.id, guild_id: this.id,

View File

@@ -188,7 +188,7 @@ class GuildChannel extends Channel {
} }
} }
return this.client.api.channels(this.id).permissions(payload.id) return this.client.api.channels[this.id].permissions(payload.id)
.put({ data: payload, reason }) .put({ data: payload, reason })
.then(() => this); .then(() => this);
} }
@@ -215,7 +215,7 @@ class GuildChannel extends Channel {
* .catch(console.error); * .catch(console.error);
*/ */
edit(data, reason) { edit(data, reason) {
return this.client.api.channels(this.id).patch({ return this.client.api.channels[this.id].patch({
data: { data: {
name: (data.name || this.name).trim(), name: (data.name || this.name).trim(),
topic: data.topic || this.topic, topic: data.topic || this.topic,
@@ -287,7 +287,7 @@ class GuildChannel extends Channel {
* @returns {Promise<Invite>} * @returns {Promise<Invite>}
*/ */
createInvite({ temporary = false, maxAge = 86400, maxUses = 0, reason } = {}) { createInvite({ temporary = false, maxAge = 86400, maxUses = 0, reason } = {}) {
return this.client.api.channels(this.id).invites.post({ data: { return this.client.api.channels[this.id].invites.post({ data: {
temporary, max_age: maxAge, max_uses: maxUses, temporary, max_age: maxAge, max_uses: maxUses,
}, reason }) }, reason })
.then(invite => new Invite(this.client, invite)); .then(invite => new Invite(this.client, invite));
@@ -351,7 +351,7 @@ class GuildChannel extends Channel {
* .catch(console.error); // Log error * .catch(console.error); // Log error
*/ */
delete(reason) { delete(reason) {
return this.client.api.channels(this.id).delete({ reason }).then(() => this); return this.client.api.channels[this.id].delete({ reason }).then(() => this);
} }
/** /**

View File

@@ -342,13 +342,13 @@ class GuildMember {
data.channel = null; data.channel = null;
} }
if (data.roles) data.roles = data.roles.map(role => role instanceof Role ? role.id : role); if (data.roles) data.roles = data.roles.map(role => role instanceof Role ? role.id : role);
let endpoint = this.client.api.guilds(this.guild.id); let endpoint = this.client.api.guilds[this.guild.id];
if (this.user.id === this.client.user.id) { if (this.user.id === this.client.user.id) {
const keys = Object.keys(data); const keys = Object.keys(data);
if (keys.length === 1 && keys[0] === 'nick') endpoint = endpoint.members('@me').nick; if (keys.length === 1 && keys[0] === 'nick') endpoint = endpoint.members['@me'].nick;
else endpoint = endpoint.members(this.id); else endpoint = endpoint.members[this.id];
} else { } else {
endpoint = endpoint.members(this.id); endpoint = endpoint.members[this.id];
} }
return endpoint.patch({ data, reason }).then(newData => this.guild._updateMember(this, newData).mem); return endpoint.patch({ data, reason }).then(newData => this.guild._updateMember(this, newData).mem);
} }
@@ -398,7 +398,7 @@ class GuildMember {
if (!(role instanceof Role)) role = this.guild.roles.get(role); if (!(role instanceof Role)) role = this.guild.roles.get(role);
if (!role) return Promise.reject(new TypeError('Supplied parameter was neither a Role nor a Snowflake.')); if (!role) return Promise.reject(new TypeError('Supplied parameter was neither a Role nor a Snowflake.'));
if (this._roles.includes(role.id)) return Promise.resolve(this); if (this._roles.includes(role.id)) return Promise.resolve(this);
return this.client.api.guilds(this.guild.id).members(this.user.id).roles(role.id) return this.client.api.guilds[this.guild.id].members[this.user.id].roles[role.id]
.put() .put()
.then(() => this); .then(() => this);
} }
@@ -427,7 +427,7 @@ class GuildMember {
removeRole(role) { removeRole(role) {
if (!(role instanceof Role)) role = this.guild.roles.get(role); if (!(role instanceof Role)) role = this.guild.roles.get(role);
if (!role) return Promise.reject(new TypeError('Supplied parameter was neither a Role nor a Snowflake.')); if (!role) return Promise.reject(new TypeError('Supplied parameter was neither a Role nor a Snowflake.'));
return this.client.api.guilds(this.guild.id).members(this.user.id).roles(role.id) return this.client.api.guilds[this.guild.id].members[this.user.id].roles[role.id]
.delete() .delete()
.then(() => this); .then(() => this);
} }
@@ -484,7 +484,7 @@ class GuildMember {
* @returns {Promise<GuildMember>} * @returns {Promise<GuildMember>}
*/ */
kick(reason) { kick(reason) {
return this.client.api.guilds(this.guild.id).members(this.user.id).delete({ reason }) return this.client.api.guilds[this.guild.id].members[this.user.id].delete({ reason })
.then(() => .then(() =>
this.client.actions.GuildMemberRemove.handle({ this.client.actions.GuildMemberRemove.handle({
guild_id: this.guild.id, guild_id: this.guild.id,

View File

@@ -145,7 +145,7 @@ class Invite {
* @returns {Promise<Invite>} * @returns {Promise<Invite>}
*/ */
delete(reason) { delete(reason) {
return this.client.api.invites(this.code).delete({ reason }).then(() => this); return this.client.api.invites[this.code].delete({ reason }).then(() => this);
} }
/** /**

View File

@@ -399,7 +399,7 @@ class Message {
content = `${mention}${content ? `, ${content}` : ''}`; content = `${mention}${content ? `, ${content}` : ''}`;
} }
return this.client.api.channels(this.channel.id).messages(this.id) return this.client.api.channels[this.channel.id].messages[this.id]
.patch({ data: { content, embed } }) .patch({ data: { content, embed } })
.then(data => this.client.actions.MessageUpdate.handle(data).updated); .then(data => this.client.actions.MessageUpdate.handle(data).updated);
} }
@@ -409,7 +409,7 @@ class Message {
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
pin() { pin() {
return this.client.api.channels(this.channel.id).pins(this.id).put() return this.client.api.channels[this.channel.id].pins[this.id].put()
.then(() => this); .then(() => this);
} }
@@ -418,7 +418,7 @@ class Message {
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
unpin() { unpin() {
return this.client.api.channels(this.channel.id).pins(this.id).delete() return this.client.api.channels[this.channel.id].pins[this.id].delete()
.then(() => this); .then(() => this);
} }
@@ -431,7 +431,7 @@ class Message {
emoji = this.client.resolver.resolveEmojiIdentifier(emoji); emoji = this.client.resolver.resolveEmojiIdentifier(emoji);
if (!emoji) throw new TypeError('EMOJI_TYPE'); if (!emoji) throw new TypeError('EMOJI_TYPE');
return this.client.api.channels(this.channel.id).messages(this.id).reactions(emoji)['@me'] return this.client.api.channels[this.channel.id].messages[this.id].reactions[emoji]['@me']
.put() .put()
.then(() => this._addReaction(Util.parseEmoji(emoji), this.client.user)); .then(() => this._addReaction(Util.parseEmoji(emoji), this.client.user));
} }
@@ -441,7 +441,7 @@ class Message {
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
clearReactions() { clearReactions() {
return this.client.api.channels(this.channel.id).messages(this.id).reactions.delete() return this.client.api.channels[this.channel.id].messages[this.id].reactions.delete()
.then(() => this); .then(() => this);
} }
@@ -459,7 +459,7 @@ class Message {
*/ */
delete({ timeout = 0, reason } = {}) { delete({ timeout = 0, reason } = {}) {
if (timeout <= 0) { if (timeout <= 0) {
return this.client.api.channels(this.channel.id).messages(this.id) return this.client.api.channels[this.channel.id].messages[this.id]
.delete({ reason }) .delete({ reason })
.then(() => .then(() =>
this.client.actions.MessageDelete.handle({ this.client.actions.MessageDelete.handle({
@@ -502,7 +502,7 @@ class Message {
* @returns {Promise<Message>} * @returns {Promise<Message>}
*/ */
acknowledge() { acknowledge() {
return this.client.api.channels(this.channel.id).messages(this.id).ack return this.client.api.channels[this.channel.id].messages[this.id].ack
.post({ data: { token: this.client.rest._ackToken } }) .post({ data: { token: this.client.rest._ackToken } })
.then(res => { .then(res => {
if (res.token) this.client.rest._ackToken = res.token; if (res.token) this.client.rest._ackToken = res.token;

View File

@@ -63,8 +63,8 @@ class MessageReaction {
remove(user = this.message.client.user) { remove(user = this.message.client.user) {
const userID = this.message.client.resolver.resolveUserID(user); const userID = this.message.client.resolver.resolveUserID(user);
if (!userID) return Promise.reject(new Error('Couldn\'t resolve the user ID to remove from the reaction.')); if (!userID) return Promise.reject(new Error('Couldn\'t resolve the user ID to remove from the reaction.'));
return this.message.client.api.channels(this.message.channel.id).messages(this.message.id) return this.message.client.api.channels[this.message.channel.id].messages[this.message.id]
.reactions(this.emoji.identifier)[userID === this.message.client.user.id ? '@me' : userID] .reactions[this.emoji.identifier][userID === this.message.client.user.id ? '@me' : userID]
.delete() .delete()
.then(() => .then(() =>
this.message.client.actions.MessageReactionRemove.handle({ this.message.client.actions.MessageReactionRemove.handle({
@@ -83,8 +83,8 @@ class MessageReaction {
*/ */
fetchUsers(limit = 100) { fetchUsers(limit = 100) {
const message = this.message; const message = this.message;
return message.client.api.channels(message.channel.id).messages(message.id) return message.client.api.channels[message.channel.id].messages[message.id]
.reactions(this.emoji.identifier) .reactions[this.emoji.identifier]
.get({ query: { limit } }) .get({ query: { limit } })
.then(users => { .then(users => {
this.users = new Collection(); this.users = new Collection();

View File

@@ -51,7 +51,7 @@ class PermissionOverwrites {
* @returns {Promise<PermissionOverwrites>} * @returns {Promise<PermissionOverwrites>}
*/ */
delete(reason) { delete(reason) {
return this.channel.client.api.channels(this.channel.id).permissions(this.id) return this.channel.client.api.channels[this.channel.id].permissions[this.id]
.delete({ reason }) .delete({ reason })
.then(() => this); .then(() => this);
} }

View File

@@ -202,7 +202,7 @@ class Role {
edit(data, reason) { edit(data, reason) {
if (data.permissions) data.permissions = Permissions.resolve(data.permissions); if (data.permissions) data.permissions = Permissions.resolve(data.permissions);
else data.permissions = this.permissions; else data.permissions = this.permissions;
return this.client.api.guilds(this.guild.id).roles(this.id).patch({ return this.client.api.guilds[this.guild.id].roles[this.id].patch({
data: { data: {
name: data.name || this.name, name: data.name || this.name,
position: typeof data.position !== 'undefined' ? data.position : this.position, position: typeof data.position !== 'undefined' ? data.position : this.position,
@@ -311,7 +311,7 @@ class Role {
* .catch(console.error); * .catch(console.error);
*/ */
delete(reason) { delete(reason) {
return this.client.api.guilds(this.guild.id).roles(this.id).delete({ reason }) return this.client.api.guilds[this.guild.id].roles[this.id].delete({ reason })
.then(() => .then(() =>
this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: this.id }).role this.client.actions.GuildRoleDelete.handle({ guild_id: this.guild.id, role_id: this.id }).role
); );

View File

@@ -57,7 +57,7 @@ class TextChannel extends GuildChannel {
* @returns {Promise<Collection<Snowflake, Webhook>>} * @returns {Promise<Collection<Snowflake, Webhook>>}
*/ */
fetchWebhooks() { fetchWebhooks() {
return this.client.api.channels(this.id).webhooks.get().then(data => { return this.client.api.channels[this.id].webhooks.get().then(data => {
const hooks = new Collection(); const hooks = new Collection();
for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook));
return hooks; return hooks;
@@ -76,7 +76,7 @@ class TextChannel extends GuildChannel {
*/ */
createWebhook(name, avatar) { createWebhook(name, avatar) {
if (typeof avatar === 'string' && avatar.startsWith('data:')) { if (typeof avatar === 'string' && avatar.startsWith('data:')) {
return this.client.api.channels(this.id).webhooks.post({ data: { return this.client.api.channels[this.id].webhooks.post({ data: {
name, avatar, name, avatar,
} }).then(data => new Webhook(this.client, data)); } }).then(data => new Webhook(this.client, data));
} else { } else {

View File

@@ -205,7 +205,7 @@ class User {
*/ */
createDM() { createDM() {
if (this.dmChannel) return Promise.resolve(this.dmChannel); if (this.dmChannel) return Promise.resolve(this.dmChannel);
return this.client.api.users(this.client.user.id).channels.post({ data: { return this.client.api.users[this.client.user.id].channels.post({ data: {
recipient_id: this.id, recipient_id: this.id,
} }) } })
.then(data => this.client.actions.ChannelCreate.handle(data).channel); .then(data => this.client.actions.ChannelCreate.handle(data).channel);
@@ -217,7 +217,7 @@ class User {
*/ */
deleteDM() { deleteDM() {
if (!this.dmChannel) return Promise.reject(new Error('No DM Channel exists!')); if (!this.dmChannel) return Promise.reject(new Error('No DM Channel exists!'));
return this.client.api.channels(this.dmChannel.id).delete().then(data => return this.client.api.channels[this.dmChannel.id].delete().then(data =>
this.client.actions.ChannelDelete.handle(data).channel this.client.actions.ChannelDelete.handle(data).channel
); );
} }
@@ -228,7 +228,7 @@ class User {
* @returns {Promise<UserProfile>} * @returns {Promise<UserProfile>}
*/ */
fetchProfile() { fetchProfile() {
return this.client.api.users(this.id).profile.get().then(data => new UserProfile(data)); return this.client.api.users[this.id].profile.get().then(data => new UserProfile(data));
} }
/** /**
@@ -238,7 +238,7 @@ class User {
* @returns {Promise<User>} * @returns {Promise<User>}
*/ */
setNote(note) { setNote(note) {
return this.client.api.users('@me').notes(this.id).put({ data: { note } }) return this.client.api.users['@me'].notes[this.id].put({ data: { note } })
.then(() => this); .then(() => this);
} }

View File

@@ -150,7 +150,7 @@ class Webhook {
file.file = buffer; file.file = buffer;
return file; return file;
}) })
)).then(files => this.client.api.webhooks(this.id, this.token).post({ )).then(files => this.client.api.webhooks.opts(this.id, this.token).post({
data: options, data: options,
query: { wait: true }, query: { wait: true },
files, files,
@@ -158,7 +158,7 @@ class Webhook {
})); }));
} }
return this.client.api.webhooks(this.id, this.token).post({ return this.client.api.webhooks.opts(this.id, this.token).post({
data: options, data: options,
query: { wait: true }, query: { wait: true },
auth: false, auth: false,
@@ -187,7 +187,7 @@ class Webhook {
* }).catch(console.error); * }).catch(console.error);
*/ */
sendSlackMessage(body) { sendSlackMessage(body) {
return this.client.api.webhooks(this.id, this.token).slack.post({ return this.client.api.webhooks.opts(this.id, this.token).slack.post({
query: { wait: true }, query: { wait: true },
auth: false, auth: false,
data: body, data: body,
@@ -213,7 +213,7 @@ class Webhook {
return this.edit({ name, avatar: dataURI }, reason); return this.edit({ name, avatar: dataURI }, reason);
}); });
} }
return this.client.api.webhooks(this.id, this.token).patch({ return this.client.api.webhooks.opts(this.id, this.token).patch({
data: { name, avatar }, data: { name, avatar },
reason, reason,
}).then(data => { }).then(data => {
@@ -229,7 +229,7 @@ class Webhook {
* @returns {Promise} * @returns {Promise}
*/ */
delete(reason) { delete(reason) {
return this.client.api.webhooks(this.id, this.token).delete({ reason }); return this.client.api.webhooks.opts(this.id, this.token).delete({ reason });
} }
} }

View File

@@ -141,7 +141,7 @@ class TextBasedChannel {
return msg; return msg;
}); });
} }
return this.client.api.channels(this.id).messages(messageID).get() return this.client.api.channels[this.id].messages[messageID].get()
.then(data => { .then(data => {
const msg = data instanceof Message ? data : new Message(this, data, this.client); const msg = data instanceof Message ? data : new Message(this, data, this.client);
this._cacheMessage(msg); this._cacheMessage(msg);
@@ -171,7 +171,7 @@ class TextBasedChannel {
*/ */
fetchMessages(options = {}) { fetchMessages(options = {}) {
const Message = require('../Message'); const Message = require('../Message');
return this.client.api.channels(this.id).messages.get({ query: options }) return this.client.api.channels[this.id].messages.get({ query: options })
.then(data => { .then(data => {
const messages = new Collection(); const messages = new Collection();
for (const message of data) { for (const message of data) {
@@ -189,7 +189,7 @@ class TextBasedChannel {
*/ */
fetchPinnedMessages() { fetchPinnedMessages() {
const Message = require('../Message'); const Message = require('../Message');
return this.client.api.channels(this.id).pins.get().then(data => { return this.client.api.channels[this.id].pins.get().then(data => {
const messages = new Collection(); const messages = new Collection();
for (const message of data) { for (const message of data) {
const msg = new Message(this, message, this.client); const msg = new Message(this, message, this.client);
@@ -230,7 +230,7 @@ class TextBasedChannel {
startTyping(count) { startTyping(count) {
if (typeof count !== 'undefined' && count < 1) throw new RangeError('TYPING_COUNT'); if (typeof count !== 'undefined' && count < 1) throw new RangeError('TYPING_COUNT');
if (!this.client.user._typing.has(this.id)) { if (!this.client.user._typing.has(this.id)) {
const endpoint = this.client.api.channels(this.id).typing; const endpoint = this.client.api.channels[this.id].typing;
this.client.user._typing.set(this.id, { this.client.user._typing.set(this.id, {
count: count || 1, count: count || 1,
interval: this.client.setInterval(() => { interval: this.client.setInterval(() => {
@@ -353,7 +353,7 @@ class TextBasedChannel {
Date.now() - Snowflake.deconstruct(id).date.getTime() < 1209600000 Date.now() - Snowflake.deconstruct(id).date.getTime() < 1209600000
); );
} }
return this.client.api.channels(this.id).messages()['bulk-delete'] return this.client.api.channels[this.id].messages['bulk-delete']
.post({ data: { messages: messageIDs } }) .post({ data: { messages: messageIDs } })
.then(() => .then(() =>
this.client.actions.MessageDeleteBulk.handle({ this.client.actions.MessageDeleteBulk.handle({
@@ -372,7 +372,7 @@ class TextBasedChannel {
*/ */
acknowledge() { acknowledge() {
if (!this.lastMessageID) return Promise.resolve(this); if (!this.lastMessageID) return Promise.resolve(this);
return this.client.api.channels(this.id).messages(this.lastMessageID).ack return this.client.api.channels[this.id].messages[this.lastMessageID].ack
.post({ data: { token: this.client.rest._ackToken } }) .post({ data: { token: this.client.rest._ackToken } })
.then(res => { .then(res => {
if (res.token) this.client.rest._ackToken = res.token; if (res.token) this.client.rest._ackToken = res.token;

View File

@@ -55,7 +55,7 @@ module.exports = function sendMessage(channel, options) {
}); });
} }
return channel.client.api.channels(channel.id).messages.post({ return channel.client.api.channels[channel.id].messages.post({
data: { content, tts, nonce, embed }, data: { content, tts, nonce, embed },
files, files,
}).then(data => channel.client.actions.MessageCreate.handle(data).message); }).then(data => channel.client.actions.MessageCreate.handle(data).message);