From 1df3b84da6f7b98d527f64b4706ba82b1982ca8d Mon Sep 17 00:00:00 2001 From: Gus Caplan Date: Fri, 31 Mar 2017 12:36:09 -0500 Subject: [PATCH] rewrite endpoints for consistancy and modularity (#1323) * rewrite endpoints for consistancy and modularity * clean up a bit * add missing endpoint --- .eslintrc.json | 2 +- src/client/rest/APIRequest.js | 20 ++-- src/client/rest/RESTMethods.js | 204 ++++++++++++++------------------ src/util/Constants.js | 208 +++++++++++++++++++-------------- src/util/Util.js | 4 +- 5 files changed, 223 insertions(+), 215 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 013f503cc..49d819f4b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -102,7 +102,7 @@ "max-len": ["error", 120, 2], "max-nested-callbacks": ["error", { "max": 4 }], "max-statements-per-line": ["error", { "max": 2 }], - "new-cap": "error", + "new-cap": "off", "newline-per-chained-call": ["error", { "ignoreChainWithDepth": 3 }], "no-array-constructor": "error", "no-inline-comments": "error", diff --git a/src/client/rest/APIRequest.js b/src/client/rest/APIRequest.js index 9fc6a40de..537cb6443 100644 --- a/src/client/rest/APIRequest.js +++ b/src/client/rest/APIRequest.js @@ -2,14 +2,15 @@ const request = require('superagent'); const Constants = require('../../util/Constants'); class APIRequest { - constructor(rest, method, url, auth, data, files) { + constructor(rest, method, path, auth, data, files) { this.rest = rest; + this.client = rest.client; this.method = method; - this.url = url; + this.path = path.toString(); this.auth = auth; this.data = data; this.files = files; - this.route = this.getRoute(this.url); + this.route = this.getRoute(this.path); } getRoute(url) { @@ -23,16 +24,17 @@ class APIRequest { } getAuth() { - if (this.rest.client.token && this.rest.client.user && this.rest.client.user.bot) { - return `Bot ${this.rest.client.token}`; - } else if (this.rest.client.token) { - return this.rest.client.token; + if (this.client.token && this.client.user && this.client.user.bot) { + return `Bot ${this.client.token}`; + } else if (this.client.token) { + return this.client.token; } throw new Error(Constants.Errors.NO_TOKEN); } gen() { - const apiRequest = request[this.method](this.url); + const API = `${this.client.options.http.host}/api/v${this.client.options.http.version}`; + const apiRequest = request[this.method](`${API}${this.path}`); if (this.auth) apiRequest.set('authorization', this.getAuth()); if (this.files) { for (const file of this.files) if (file && file.file) apiRequest.attach(file.name, file.file, file.name); @@ -41,7 +43,7 @@ class APIRequest { } else if (this.data) { apiRequest.send(this.data); } - if (!this.rest.client.browser) apiRequest.set('User-Agent', this.rest.userAgentManager.userAgent); + if (!this.client.browser) apiRequest.set('User-Agent', this.rest.userAgentManager.userAgent); return apiRequest; } } diff --git a/src/client/rest/RESTMethods.js b/src/client/rest/RESTMethods.js index c00f1bac3..d0d8e85c7 100644 --- a/src/client/rest/RESTMethods.js +++ b/src/client/rest/RESTMethods.js @@ -2,6 +2,7 @@ const querystring = require('querystring'); const long = require('long'); const Permissions = require('../../util/Permissions'); const Constants = require('../../util/Constants'); +const Endpoints = Constants.Endpoints; const Collection = require('../../util/Collection'); const Snowflake = require('../../util/Snowflake'); const Util = require('../../util/Util'); @@ -35,23 +36,25 @@ class RESTMethods { } logout() { - return this.rest.makeRequest('post', Constants.Endpoints.logout, true, {}); + return this.rest.makeRequest('post', Endpoints.logout, true, {}); } getGateway() { - return this.rest.makeRequest('get', Constants.Endpoints.gateway, true).then(res => { - this.client.ws.gateway = `${res.url}/?v=${Constants.PROTOCOL_VERSION}`; + return this.rest.makeRequest('get', Endpoints.gateway, true).then(res => { + this.client.ws.gateway = `${res.url}/?v=${this.client.options.ws.version}`; return this.client.ws.gateway; }); } getBotGateway() { - return this.rest.makeRequest('get', Constants.Endpoints.botGateway, true); + return this.rest.makeRequest('get', Endpoints.gateway.bot, true); } fetchVoiceRegions(guildID) { - const endpoint = Constants.Endpoints[guildID ? 'guildVoiceRegions' : 'voiceRegions']; - return this.rest.makeRequest('get', guildID ? endpoint(guildID) : endpoint, true).then(res => { + let endpoint; + if (guildID) endpoint = Endpoints.Guild(guildID).voiceRegions; + else endpoint = Endpoints.voiceRegions; + return this.rest.makeRequest('get', endpoint, true).then(res => { const regions = new Collection(); for (const region of res) regions.set(region.id, new VoiceRegion(region)); return regions; @@ -113,7 +116,7 @@ class RESTMethods { }); }(content, 0)); } else { - this.rest.makeRequest('post', Constants.Endpoints.channelMessages(chan.id), true, { + this.rest.makeRequest('post', Endpoints.Channel(chan).messages, true, { content, tts, nonce, embed, }, files).then(data => resolve(this.client.actions.MessageCreate.handle(data).message), reject); } @@ -143,13 +146,13 @@ class RESTMethods { content = `${mention}${content ? `, ${content}` : ''}`; } - return this.rest.makeRequest('patch', Constants.Endpoints.channelMessage(message.channel.id, message.id), true, { + return this.rest.makeRequest('patch', Endpoints.Message(message), true, { content, embed, }).then(data => this.client.actions.MessageUpdate.handle(data).updated); } deleteMessage(message) { - return this.rest.makeRequest('del', Constants.Endpoints.channelMessage(message.channel.id, message.id), true) + return this.rest.makeRequest('del', Endpoints.Message(message), true) .then(() => this.client.actions.MessageDelete.handle({ id: message.id, @@ -159,30 +162,21 @@ class RESTMethods { } ackMessage(message) { - return this.rest.makeRequest('post', - `${Constants.Endpoints.channelMessage(message.channel.id, message.id)}/ack`, - true, - { token: this._ackToken } - ).then(res => { - this._ackToken = res.token; + return this.rest.makeRequest('post', Endpoints.Message(message).ack, true, { token: this._ackToken }).then(res => { + if (res.token) this._ackToken = res.token; return message; }); } ackTextChannel(channel) { - return this.rest.makeRequest('post', - `${Constants.Endpoints.channel(channel.id)}/ack`, - true, - { token: this._ackToken } - ).then(res => { - this._ackToken = res.token; + return this.rest.makeRequest('post', Endpoints.Channel(channel).ack, true, { token: this._ackToken }).then(res => { + if (res.token) this._ackToken = res.token; return channel; }); } ackGuild(guild) { - return this.rest.makeRequest('post', `${Constants.Endpoints.guild(guild.id)}/ack`, true) - .then(() => guild); + return this.rest.makeRequest('post', Endpoints.Guild(guild).ack, true).then(() => guild); } bulkDeleteMessages(channel, messages, filterOld) { @@ -191,7 +185,7 @@ class RESTMethods { Date.now() - Snowflake.deconstruct(id).date.getTime() < 1209600000 ); } - return this.rest.makeRequest('post', `${Constants.Endpoints.channelMessages(channel.id)}/bulk-delete`, true, { + return this.rest.makeRequest('post', Endpoints.Channel(channel).messages.bulkDelete, true, { messages, }).then(() => this.client.actions.MessageDeleteBulk.handle({ @@ -244,17 +238,15 @@ class RESTMethods { for (const key in options) if (options[key] === undefined) delete options[key]; const queryString = (querystring.stringify(options).match(/[^=&?]+=[^=&?]+/g) || []).join('&'); - let type; + let endpoint; if (target instanceof Channel) { - type = 'channel'; + endpoint = Endpoints.Channel(target).search; } else if (target instanceof Guild) { - type = 'guild'; + endpoint = Endpoints.Guild(target).search; } else { throw new TypeError('Target must be a TextChannel, DMChannel, GroupDMChannel, or Guild.'); } - - const url = `${Constants.Endpoints[`${type}Search`](target.id)}?${queryString}`; - return this.rest.makeRequest('get', url, true).then(body => { + return this.rest.makeRequest('get', `${endpoint}?${queryString}`, true).then(body => { const messages = body.messages.map(x => x.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client)) ); @@ -267,7 +259,7 @@ class RESTMethods { createChannel(guild, channelName, channelType, overwrites) { if (overwrites instanceof Collection) overwrites = overwrites.array(); - return this.rest.makeRequest('post', Constants.Endpoints.guildChannels(guild.id), true, { + return this.rest.makeRequest('post', Endpoints.Guild(guild).channel, true, { name: channelName, type: channelType, permission_overwrites: overwrites, @@ -277,7 +269,7 @@ class RESTMethods { createDM(recipient) { const dmChannel = this.getExistingDM(recipient); if (dmChannel) return Promise.resolve(dmChannel); - return this.rest.makeRequest('post', Constants.Endpoints.userChannels(this.client.user.id), true, { + return this.rest.makeRequest('post', Endpoints.User(this.client.user).channels, true, { recipient_id: recipient.id, }).then(data => this.client.actions.ChannelCreate.handle(data).channel); } @@ -287,7 +279,7 @@ class RESTMethods { { access_tokens: options.accessTokens, nicks: options.nicks } : { recipients: options.recipients }; - return this.rest.makeRequest('post', Constants.Endpoints.meChannels, true, data) + return this.rest.makeRequest('post', Endpoints.User('@me').channels, true, data) .then(res => new GroupDMChannel(this.client, res)); } @@ -295,7 +287,7 @@ class RESTMethods { const data = this.client.user.bot ? { nick: options.nick, access_token: options.accessToken } : { recipient: options.id }; - return this.rest.makeRequest('put', Constants.Endpoints.dmChannelRecipient(channel.id, options.id), true, data) + return this.rest.makeRequest('put', Endpoints.Channel(channel).Recipient(options.id), true, data) .then(() => channel); } @@ -308,7 +300,7 @@ class RESTMethods { deleteChannel(channel) { if (channel instanceof User || channel instanceof GuildMember) channel = this.getExistingDM(channel); if (!channel) return Promise.reject(new Error('No channel to delete.')); - return this.rest.makeRequest('del', Constants.Endpoints.channel(channel.id), true).then(data => { + return this.rest.makeRequest('del', Endpoints.Channel(channel), true).then(data => { data.id = channel.id; return this.client.actions.ChannelDelete.handle(data).channel; }); @@ -321,14 +313,14 @@ class RESTMethods { data.position = _data.position || channel.position; data.bitrate = _data.bitrate || channel.bitrate; data.user_limit = _data.userLimit || channel.userLimit; - return this.rest.makeRequest('patch', Constants.Endpoints.channel(channel.id), true, data).then(newData => + return this.rest.makeRequest('patch', Endpoints.Channel(channel), true, data).then(newData => this.client.actions.ChannelUpdate.handle(newData).updated ); } leaveGuild(guild) { if (guild.ownerID === this.client.user.id) return Promise.reject(new Error('Guild is owned by the client.')); - return this.rest.makeRequest('del', Constants.Endpoints.meGuild(guild.id), true).then(() => + return this.rest.makeRequest('del', Endpoints.User('@me').Guild(guild.id), true).then(() => this.client.actions.GuildDelete.handle({ id: guild.id }).guild ); } @@ -337,7 +329,7 @@ class RESTMethods { options.icon = this.client.resolver.resolveBase64(options.icon) || null; options.region = options.region || 'us-central'; return new Promise((resolve, reject) => { - this.rest.makeRequest('post', Constants.Endpoints.guilds, true, options).then(data => { + this.rest.makeRequest('post', Endpoints.guilds, true, options).then(data => { if (this.client.guilds.has(data.id)) { resolve(this.client.guilds.get(data.id)); return; @@ -362,13 +354,13 @@ class RESTMethods { // Untested but probably will work deleteGuild(guild) { - return this.rest.makeRequest('del', Constants.Endpoints.guild(guild.id), true).then(() => + return this.rest.makeRequest('del', Endpoints.Guild(guild), true).then(() => this.client.actions.GuildDelete.handle({ id: guild.id }).guild ); } getUser(userID, cache) { - return this.rest.makeRequest('get', Constants.Endpoints.user(userID), true).then(data => { + return this.rest.makeRequest('get', Endpoints.User(userID), true).then(data => { if (cache) { return this.client.actions.UserGet.handle(data).user; } else { @@ -387,7 +379,7 @@ class RESTMethods { data.password = password; if (_data.new_password) data.new_password = _data.newPassword; } - return this.rest.makeRequest('patch', Constants.Endpoints.me, true, data).then(newData => + return this.rest.makeRequest('patch', Endpoints.User('@me'), true, data).then(newData => this.client.actions.UserUpdate.handle(newData).updated ); } @@ -402,13 +394,13 @@ class RESTMethods { if (_data.icon) data.icon = this.client.resolver.resolveBase64(_data.icon); if (_data.owner) data.owner_id = this.client.resolver.resolveUser(_data.owner).id; if (_data.splash) data.splash = this.client.resolver.resolveBase64(_data.splash); - return this.rest.makeRequest('patch', Constants.Endpoints.guild(guild.id), true, data).then(newData => + return this.rest.makeRequest('patch', Endpoints.Guild(guild), true, data).then(newData => this.client.actions.GuildUpdate.handle(newData).updated ); } kickGuildMember(guild, member) { - return this.rest.makeRequest('del', Constants.Endpoints.guildMember(guild.id, member.id), true).then(() => + return this.rest.makeRequest('del', Endpoints.Guild(guild).Member(member), true).then(() => this.client.actions.GuildMemberRemove.handle({ guild_id: guild.id, user: member.user, @@ -419,7 +411,7 @@ class RESTMethods { createGuildRole(guild, data) { if (data.color) data.color = this.client.resolver.resolveColor(data.color); if (data.permissions) data.permissions = Permissions.resolve(data.permissions); - return this.rest.makeRequest('post', Constants.Endpoints.guildRoles(guild.id), true, data).then(role => + return this.rest.makeRequest('post', Endpoints.Guild(guild).roles, true, data).then(role => this.client.actions.GuildRoleCreate.handle({ guild_id: guild.id, role, @@ -428,7 +420,7 @@ class RESTMethods { } deleteGuildRole(role) { - return this.rest.makeRequest('del', Constants.Endpoints.guildRole(role.guild.id, role.id), true).then(() => + return this.rest.makeRequest('del', Endpoints.Guild(role.guild).Role(role.id), true).then(() => this.client.actions.GuildRoleDelete.handle({ guild_id: role.guild.id, role_id: role.id, @@ -437,14 +429,12 @@ class RESTMethods { } setChannelOverwrite(channel, payload) { - return this.rest.makeRequest( - 'put', `${Constants.Endpoints.channelPermissions(channel.id)}/${payload.id}`, true, payload - ); + return this.rest.makeRequest('put', `${Endpoints.Channel(channel).permissions}/${payload.id}`, true, payload); } deletePermissionOverwrites(overwrite) { return this.rest.makeRequest( - 'del', `${Constants.Endpoints.channelPermissions(overwrite.channel.id)}/${overwrite.id}`, true + 'del', `${Endpoints.Channel(overwrite.channel).permissions}/${overwrite.id}`, true ).then(() => overwrite); } @@ -455,7 +445,7 @@ class RESTMethods { else if (payload.before) params.push(`before=${payload.before}`); else if (payload.after) params.push(`after=${payload.after}`); - let endpoint = Constants.Endpoints.channelMessages(channel.id); + let endpoint = Endpoints.Channel(channel).messages; if (params.length > 0) endpoint += `?${params.join('&')}`; return this.rest.makeRequest('get', endpoint, true); } @@ -463,7 +453,7 @@ class RESTMethods { getChannelMessage(channel, messageID) { const msg = channel.messages.get(messageID); if (msg) return Promise.resolve(msg); - return this.rest.makeRequest('get', Constants.Endpoints.channelMessage(channel.id, messageID), true); + return this.rest.makeRequest('get', Endpoints.Channel(channel).Message(messageID), true); } putGuildMember(guild, user, options) { @@ -474,12 +464,12 @@ class RESTMethods { options.roles = roles.map(role => role.id); } } - return this.rest.makeRequest('put', Constants.Endpoints.guildMember(guild.id, user.id), true, options) + return this.rest.makeRequest('put', Endpoints.Guild(guild).Member(user.id), true, options) .then(data => this.client.actions.GuildMemberGet.handle(guild, data).member); } getGuildMember(guild, user, cache) { - return this.rest.makeRequest('get', Constants.Endpoints.guildMember(guild.id, user.id), true).then(data => { + return this.rest.makeRequest('get', Endpoints.Guild(guild).Member(user.id), true).then(data => { if (cache) { return this.client.actions.GuildMemberGet.handle(guild, data).member; } else { @@ -492,12 +482,12 @@ class RESTMethods { if (data.channel) data.channel_id = this.client.resolver.resolveChannel(data.channel).id; if (data.roles) data.roles = data.roles.map(role => role instanceof Role ? role.id : role); - let endpoint = Constants.Endpoints.guildMember(member.guild.id, member.id); + let endpoint = Endpoints.Member(member); // Fix your endpoints, discord ;-; if (member.id === this.client.user.id) { const keys = Object.keys(data); if (keys.length === 1 && keys[0] === 'nick') { - endpoint = Constants.Endpoints.guildMemberNickname(member.guild.id); + endpoint = Endpoints.Member(member).nickname; } } @@ -523,11 +513,7 @@ class RESTMethods { this.client.on('guildMemberUpdate', listener); this.client.setTimeout(() => this.client.removeListener('guildMemberUpdate', listener), 10e3); - this.rest.makeRequest( - 'put', - Constants.Endpoints.guildMemberRole(member.guild.id, member.id, role.id), - true - ); + this.rest.makeRequest('put', Endpoints.Member(member).Role(role.id), true); }); } @@ -548,23 +534,19 @@ class RESTMethods { this.client.on('guildMemberUpdate', listener); this.client.setTimeout(() => this.client.removeListener('guildMemberUpdate', listener), 10e3); - this.rest.makeRequest( - 'delete', - Constants.Endpoints.guildMemberRole(member.guild.id, member.id, role.id), - true - ); + this.rest.makeRequest('delete', Endpoints.Member(member).Role(role.id), true); }); } sendTyping(channelID) { - return this.rest.makeRequest('post', `${Constants.Endpoints.channel(channelID)}/typing`, true); + return this.rest.makeRequest('post', Endpoints.Channel(channelID).typing, true); } banGuildMember(guild, member, deleteDays = 0) { const id = this.client.resolver.resolveUserID(member); if (!id) return Promise.reject(new Error('Couldn\'t resolve the user ID to ban.')); return this.rest.makeRequest( - 'put', `${Constants.Endpoints.guildBans(guild.id)}/${id}?delete-message-days=${deleteDays}`, true, { + 'put', `${Endpoints.Guild(guild).bans}/${id}?delete-message-days=${deleteDays}`, true, { 'delete-message-days': deleteDays, } ).then(() => { @@ -597,7 +579,7 @@ class RESTMethods { reject(new Error('Took too long to receive the ban remove event.')); }, 10000); - this.rest.makeRequest('del', `${Constants.Endpoints.guildBans(guild.id)}/${id}`, true).catch(err => { + this.rest.makeRequest('del', `${Endpoints.Guild(guild).bans}/${id}`, true).catch(err => { this.client.removeListener(Constants.Events.GUILD_BAN_REMOVE, listener); this.client.clearTimeout(timeout); reject(err); @@ -606,7 +588,7 @@ class RESTMethods { } getGuildBans(guild) { - return this.rest.makeRequest('get', Constants.Endpoints.guildBans(guild.id), true).then(banItems => { + return this.rest.makeRequest('get', Endpoints.Guild(guild).bans, true).then(banItems => { const bannedUsers = new Collection(); for (const banItem of banItems) { const user = this.client.dataManager.newUser(banItem.user); @@ -635,9 +617,7 @@ class RESTMethods { data.permissions = role.permissions; } - return this.rest.makeRequest( - 'patch', Constants.Endpoints.guildRole(role.guild.id, role.id), true, data - ).then(_role => + return this.rest.makeRequest('patch', Endpoints.Guild(role.guild).Role(role.id), true, data).then(_role => this.client.actions.GuildRoleUpdate.handle({ role: _role, guild_id: role.guild.id, @@ -646,17 +626,17 @@ class RESTMethods { } pinMessage(message) { - return this.rest.makeRequest('put', `${Constants.Endpoints.channel(message.channel.id)}/pins/${message.id}`, true) + return this.rest.makeRequest('put', Endpoints.Channel(message.channel).Pin(message.id), true) .then(() => message); } unpinMessage(message) { - return this.rest.makeRequest('del', `${Constants.Endpoints.channel(message.channel.id)}/pins/${message.id}`, true) + return this.rest.makeRequest('del', Endpoints.Channel(message.channel).Pin(message.id), true) .then(() => message); } getChannelPinnedMessages(channel) { - return this.rest.makeRequest('get', `${Constants.Endpoints.channel(channel.id)}/pins`, true); + return this.rest.makeRequest('get', Endpoints.Channel(channel).pins, true); } createChannelInvite(channel, options) { @@ -664,22 +644,22 @@ class RESTMethods { payload.temporary = options.temporary; payload.max_age = options.maxAge; payload.max_uses = options.maxUses; - return this.rest.makeRequest('post', `${Constants.Endpoints.channelInvites(channel.id)}`, true, payload) + return this.rest.makeRequest('post', Endpoints.Channel(channel).invites, true, payload) .then(invite => new Invite(this.client, invite)); } deleteInvite(invite) { - return this.rest.makeRequest('del', Constants.Endpoints.invite(invite.code), true).then(() => invite); + return this.rest.makeRequest('del', Endpoints.Invite(invite.code), true).then(() => invite); } getInvite(code) { - return this.rest.makeRequest('get', Constants.Endpoints.invite(code), true).then(invite => + return this.rest.makeRequest('get', Endpoints.Invite(code), true).then(invite => new Invite(this.client, invite) ); } getGuildInvites(guild) { - return this.rest.makeRequest('get', Constants.Endpoints.guildInvites(guild.id), true).then(inviteItems => { + return this.rest.makeRequest('get', Endpoints.Guild(guild).invites, true).then(inviteItems => { const invites = new Collection(); for (const inviteItem of inviteItems) { const invite = new Invite(this.client, inviteItem); @@ -690,14 +670,14 @@ class RESTMethods { } pruneGuildMembers(guild, days, dry) { - return this.rest.makeRequest(dry ? 'get' : 'post', `${Constants.Endpoints.guildPrune(guild.id)}?days=${days}`, true) + return this.rest.makeRequest(dry ? 'get' : 'post', `${Endpoints.Guild(guild).prune}?days=${days}`, true) .then(data => data.pruned); } createEmoji(guild, image, name, roles) { const data = { image, name }; if (roles) data.roles = roles.map(r => r.id ? r.id : r); - return this.rest.makeRequest('post', `${Constants.Endpoints.guildEmojis(guild.id)}`, true, data) + return this.rest.makeRequest('post', Endpoints.Guild(guild).emojis, true, data) .then(emoji => this.client.actions.GuildEmojiCreate.handle(guild, emoji).emoji); } @@ -705,23 +685,23 @@ class RESTMethods { const data = {}; if (_data.name) data.name = _data.name; if (_data.roles) data.roles = _data.roles.map(r => r.id ? r.id : r); - return this.rest.makeRequest('patch', Constants.Endpoints.guildEmoji(emoji.guild.id, emoji.id), true, data) + return this.rest.makeRequest('patch', Endpoints.Guild(emoji.guild).Emoji(emoji.id), true, data) .then(newEmoji => this.client.actions.GuildEmojiUpdate.handle(emoji, newEmoji).emoji); } deleteEmoji(emoji) { - return this.rest.makeRequest('delete', `${Constants.Endpoints.guildEmojis(emoji.guild.id)}/${emoji.id}`, true) + return this.rest.makeRequest('delete', Endpoints.Guild(emoji.guild).Emoji(emoji.id), true) .then(() => this.client.actions.GuildEmojiDelete.handle(emoji).data); } getWebhook(id, token) { - return this.rest.makeRequest('get', Constants.Endpoints.webhook(id, token), !token).then(data => + return this.rest.makeRequest('get', Endpoints.Webhook(id, token), !token).then(data => new Webhook(this.client, data) ); } getGuildWebhooks(guild) { - return this.rest.makeRequest('get', Constants.Endpoints.guildWebhooks(guild.id), true).then(data => { + return this.rest.makeRequest('get', Endpoints.Guild(guild).webhooks, true).then(data => { const hooks = new Collection(); for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); return hooks; @@ -729,7 +709,7 @@ class RESTMethods { } getChannelWebhooks(channel) { - return this.rest.makeRequest('get', Constants.Endpoints.channelWebhooks(channel.id), true).then(data => { + return this.rest.makeRequest('get', Endpoints.Channel(channel).webhooks, true).then(data => { const hooks = new Collection(); for (const hook of data) hooks.set(hook.id, new Webhook(this.client, hook)); return hooks; @@ -737,12 +717,12 @@ class RESTMethods { } createWebhook(channel, name, avatar) { - return this.rest.makeRequest('post', Constants.Endpoints.channelWebhooks(channel.id), true, { name, avatar }) + return this.rest.makeRequest('post', Endpoints.Channel(channel).webhooks, true, { name, avatar }) .then(data => new Webhook(this.client, data)); } editWebhook(webhook, name, avatar) { - return this.rest.makeRequest('patch', Constants.Endpoints.webhook(webhook.id, webhook.token), false, { + return this.rest.makeRequest('patch', Endpoints.Webhook(webhook.id, webhook.token), false, { name, avatar, }).then(data => { @@ -753,7 +733,7 @@ class RESTMethods { } deleteWebhook(webhook) { - return this.rest.makeRequest('delete', Constants.Endpoints.webhook(webhook.id, webhook.token), false); + return this.rest.makeRequest('delete', Endpoints.Webhook(webhook.id, webhook.token), false); } sendWebhookMessage(webhook, content, { avatarURL, tts, disableEveryone, embeds, username } = {}, file = null) { @@ -764,7 +744,7 @@ class RESTMethods { content = content.replace(/@(everyone|here)/g, '@\u200b$1'); } } - return this.rest.makeRequest('post', `${Constants.Endpoints.webhook(webhook.id, webhook.token)}?wait=true`, false, { + return this.rest.makeRequest('post', `${Endpoints.Webhook(webhook.id, webhook.token)}?wait=true`, false, { username, avatar_url: avatarURL, content, @@ -775,12 +755,12 @@ class RESTMethods { sendSlackWebhookMessage(webhook, body) { return this.rest.makeRequest( - 'post', `${Constants.Endpoints.webhook(webhook.id, webhook.token)}/slack?wait=true`, false, body + 'post', `${Endpoints.Webhook(webhook.id, webhook.token)}/slack?wait=true`, false, body ); } fetchUserProfile(user) { - return this.rest.makeRequest('get', Constants.Endpoints.userProfile(user.id), true).then(data => + return this.rest.makeRequest('get', Endpoints.User(user).profile, true).then(data => new UserProfile(user, data) ); } @@ -789,29 +769,29 @@ class RESTMethods { if (options.guild) options.guild = options.guild.id ? options.guild.id : options.guild; return this.rest.makeRequest( 'get', - Constants.Endpoints.meMentions(options.limit, options.roles, options.everyone, options.guild) + Endpoints.User('@me').mentions(options.limit, options.roles, options.everyone, options.guild) ).then(res => res.body.map(m => new Message(this.client.channels.get(m.channel_id), m, this.client))); } addFriend(user) { - return this.rest.makeRequest('post', Constants.Endpoints.relationships('@me'), true, { + return this.rest.makeRequest('post', Endpoints.User('@me'), true, { username: user.username, discriminator: user.discriminator, }).then(() => user); } removeFriend(user) { - return this.rest.makeRequest('delete', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true) + return this.rest.makeRequest('delete', Endpoints.User('@me').Relationship(user.id), true) .then(() => user); } blockUser(user) { - return this.rest.makeRequest('put', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true, { type: 2 }) + return this.rest.makeRequest('put', Endpoints.User('@me').Relationship(user.id), true, { type: 2 }) .then(() => user); } unblockUser(user) { - return this.rest.makeRequest('delete', `${Constants.Endpoints.relationships('@me')}/${user.id}`, true) + return this.rest.makeRequest('delete', Endpoints.User('@me').Relationship(user.id), true) .then(() => user); } @@ -824,7 +804,7 @@ class RESTMethods { }; } - return this.rest.makeRequest('patch', Constants.Endpoints.guildChannels(guildID), true, data).then(() => + return this.rest.makeRequest('patch', Endpoints.Guild(guildID).channels, true, data).then(() => this.client.actions.GuildChannelsPositionUpdate.handle({ guild_id: guildID, channels, @@ -833,7 +813,7 @@ class RESTMethods { } setRolePositions(guildID, roles) { - return this.rest.makeRequest('patch', Constants.Endpoints.guildRoles(guildID), true, roles).then(() => + return this.rest.makeRequest('patch', Endpoints.Guild(guildID).roles, true, roles).then(() => this.client.actions.GuildRolesPositionUpdate.handle({ guild_id: guildID, roles, @@ -843,7 +823,7 @@ class RESTMethods { addMessageReaction(message, emoji) { return this.rest.makeRequest( - 'put', Constants.Endpoints.selfMessageReaction(message.channel.id, message.id, emoji), true + 'put', Endpoints.Message(message).Reaction(emoji).User('@me'), true ).then(() => this.client.actions.MessageReactionAdd.handle({ user_id: this.client.user.id, @@ -855,10 +835,7 @@ class RESTMethods { } removeMessageReaction(message, emoji, user) { - let endpoint = Constants.Endpoints.selfMessageReaction(message.channel.id, message.id, emoji); - if (user !== this.client.user.id) { - endpoint = Constants.Endpoints.userMessageReaction(message.channel.id, message.id, emoji, null, user); - } + let endpoint = Endpoints.Message(message).Reaction(emoji).User(user === this.client.user.id ? '@me' : user.id); return this.rest.makeRequest('delete', endpoint, true).then(() => this.client.actions.MessageReactionRemove.handle({ user_id: user, @@ -870,38 +847,33 @@ class RESTMethods { } removeMessageReactions(message) { - return this.rest.makeRequest('delete', Constants.Endpoints.messageReactions(message.channel.id, message.id), true) + return this.rest.makeRequest('delete', Endpoints.Message(message).reactions, true) .then(() => message); } getMessageReactionUsers(message, emoji, limit = 100) { - return this.rest.makeRequest( - 'get', Constants.Endpoints.messageReaction(message.channel.id, message.id, emoji, limit), true - ); + return this.rest.makeRequest('get', Endpoints.Message(message).Reaction(emoji, limit), true); } getApplication(id) { - return this.rest.makeRequest('get', Constants.Endpoints.oauth2Application(id), true).then(app => + return this.rest.makeRequest('get', Endpoints.OAUTH2.Application(id), true).then(app => new OAuth2Application(this.client, app) ); } resetApplication(id) { - return this.rest.makeRequest( - 'post', - `${Constants.Endpoints.oauth2Application(id)}/reset`, - true - ).then(app => new OAuth2Application(this.client, app)); + return this.rest.makeRequest('post', Endpoints.OAUTH2.Application(id).reset, true) + .then(app => new OAuth2Application(this.client, app)); } setNote(user, note) { - return this.rest.makeRequest('put', Constants.Endpoints.note(user.id), true, { note }).then(() => user); + return this.rest.makeRequest('put', Endpoints.User(user).note, true, { note }).then(() => user); } acceptInvite(code) { if (code.id) code = code.id; return new Promise((resolve, reject) => - this.rest.makeRequest('post', Constants.Endpoints.invite(code), true).then(res => { + this.rest.makeRequest('post', Endpoints.Invite(code), true).then(res => { const handler = guild => { if (guild.id === res.id) { resolve(guild); diff --git a/src/util/Constants.js b/src/util/Constants.js index de7c66fb9..b2b6392ea 100644 --- a/src/util/Constants.js +++ b/src/util/Constants.js @@ -61,6 +61,11 @@ exports.DefaultOptions = { $referrer: '', $referring_domain: '', }, + version: 6, + }, + http: { + version: 6, + host: 'https://discordapp.com', }, }; @@ -78,96 +83,125 @@ exports.Errors = { INVALID_TOKEN: 'An invalid token was provided.', }; -const PROTOCOL_VERSION = exports.PROTOCOL_VERSION = 6; -const HOST = exports.HOST = `https://discordapp.com`; -const API = exports.API = `${HOST}/api/v${PROTOCOL_VERSION}`; -const Endpoints = exports.Endpoints = { - // General - login: `${API}/auth/login`, - logout: `${API}/auth/logout`, - gateway: `${API}/gateway`, - botGateway: `${API}/gateway/bot`, - invite: id => `${API}/invite/${id}`, - inviteLink: id => `https://discord.gg/${id}`, - assets: asset => `${HOST}/assets/${asset}`, - CDN: 'https://cdn.discordapp.com', - - // Users - user: userID => `${API}/users/${userID}`, - userChannels: userID => `${Endpoints.user(userID)}/channels`, - userProfile: userID => `${Endpoints.user(userID)}/profile`, - avatar: (userID, avatar) => { - if (userID === '1') return avatar; - return `${Endpoints.CDN}/avatars/${userID}/${avatar}.${avatar.startsWith('a_') ? 'gif' : 'jpg'}?size=1024`; +exports.Endpoints = { + User: userID => { + if (userID.id) userID = userID.id; + const base = `/users/${userID}`; + return { + toString: () => base, + channels: `${base}/channels`, + profile: `${base}/profile`, + relationships: `${base}/relationships`, + Relationship: uID => `${base}/relationships/${uID}`, + Guild: guildID => `${base}/guilds/${guildID}`, + Note: id => `${base}/notes/${id}`, + Mentions: (limit, roles, everyone, guildID) => + `${base}/mentions?limit=${limit}&roles=${roles}&everyone=${everyone}${guildID ? `&guild_id=${guildID}` : ''}`, + Avatar: hash => { + if (userID === '1') return hash; + return hash; + }, + }; }, - me: `${API}/users/@me`, - meGuild: guildID => `${Endpoints.me}/guilds/${guildID}`, - meChannels: `${API}/users/@me/channels`, - meMentions: (limit, roles, everyone, guildID) => - `users/@me/mentions?limit=${limit}&roles=${roles}&everyone=${everyone}${guildID ? `&guild_id=${guildID}` : ''}`, - relationships: userID => `${Endpoints.user(userID)}/relationships`, - note: userID => `${Endpoints.me}/notes/${userID}`, - - voiceRegions: `${API}/voice/regions`, - - // Guilds - guilds: `${API}/guilds`, - guild: guildID => `${Endpoints.guilds}/${guildID}`, - guildIcon: (guildID, hash) => `${Endpoints.CDN}/icons/${guildID}/${hash}.jpg`, - guildSplash: (guildID, hash) => `${Endpoints.CDN}/splashes/${guildID}/${hash}.jpg`, - guildPrune: guildID => `${Endpoints.guild(guildID)}/prune`, - guildEmbed: guildID => `${Endpoints.guild(guildID)}/embed`, - guildInvites: guildID => `${Endpoints.guild(guildID)}/invites`, - guildRoles: guildID => `${Endpoints.guild(guildID)}/roles`, - guildRole: (guildID, roleID) => `${Endpoints.guildRoles(guildID)}/${roleID}`, - guildBans: guildID => `${Endpoints.guild(guildID)}/bans`, - guildIntegrations: guildID => `${Endpoints.guild(guildID)}/integrations`, - guildMembers: guildID => `${Endpoints.guild(guildID)}/members`, - guildMember: (guildID, memberID) => `${Endpoints.guildMembers(guildID)}/${memberID}`, - guildMemberRole: (guildID, memberID, roleID) => `${Endpoints.guildMember(guildID, memberID)}/roles/${roleID}`, - guildMemberNickname: guildID => `${Endpoints.guildMember(guildID, '@me')}/nick`, - guildChannels: guildID => `${Endpoints.guild(guildID)}/channels`, - guildEmojis: guildID => `${Endpoints.guild(guildID)}/emojis`, - guildEmoji: (guildID, emojiID) => `${Endpoints.guildEmojis(guildID)}/${emojiID}`, - guildSearch: guildID => `${Endpoints.guild(guildID)}/messages/search`, - guildVoiceRegions: guildID => `${Endpoints.guild(guildID)}/regions`, - guildWebhooks: guildID => `${Endpoints.guild(guildID)}/webhooks`, - - // Channels - channels: `${API}/channels`, - channel: channelID => `${Endpoints.channels}/${channelID}`, - channelMessages: channelID => `${Endpoints.channel(channelID)}/messages`, - channelInvites: channelID => `${Endpoints.channel(channelID)}/invites`, - channelTyping: channelID => `${Endpoints.channel(channelID)}/typing`, - channelPermissions: channelID => `${Endpoints.channel(channelID)}/permissions`, - channelMessage: (channelID, messageID) => `${Endpoints.channelMessages(channelID)}/${messageID}`, - channelWebhooks: channelID => `${Endpoints.channel(channelID)}/webhooks`, - channelSearch: channelID => `${Endpoints.channelMessages(channelID)}/search`, - - dmChannelRecipient: (channelID, recipientID) => `${Endpoints.channel(channelID)}/recipients/${recipientID}`, - - // Message reactions - messageReactions: (channelID, messageID) => `${Endpoints.channelMessage(channelID, messageID)}/reactions`, - messageReaction: - (channel, msg, emoji, limit) => - `${Endpoints.messageReactions(channel, msg)}/${emoji}` + - `${limit ? `?limit=${limit}` : ''}`, - selfMessageReaction: (channel, msg, emoji, limit) => - `${Endpoints.messageReaction(channel, msg, emoji, limit)}/@me`, - userMessageReaction: (channel, msg, emoji, limit, id) => - `${Endpoints.messageReaction(channel, msg, emoji, limit)}/${id}`, - - // Webhooks - webhook: (webhookID, token) => `${API}/webhooks/${webhookID}${token ? `/${token}` : ''}`, - - // OAuth - oauth2Application: appID => `${API}/oauth2/applications/${appID}`, - getApp: id => `${API}/oauth2/authorize?client_id=${id}`, - - // Emoji - emoji: emojiID => `${Endpoints.CDN}/emojis/${emojiID}.png`, + guilds: '/guilds', + Guild: guildID => { + if (guildID.id) guildID = guildID.id; + const base = `/guilds/${guildID}`; + return { + toString: () => base, + prune: `${base}/prune`, + embed: `${base}/embed`, + bans: `${base}/bans`, + integrations: `${base}/integrations`, + members: `${base}/members`, + channels: `${base}/channels`, + invites: `${base}/invites`, + roles: `${base}/roles`, + emojis: `${base}/emojis`, + search: `${base}/messages/search`, + voiceRegions: `${base}/regions`, + webhooks: `${base}/webhooks`, + ack: `${base}/ack`, + settings: `${base}/settings`, + Emoji: emojiID => `${base}/emojis/${emojiID}`, + Icon: hash => `cdn/${hash}`, + Splash: hash => `cdn/${hash}`, + Role: roleID => `${base}/roles/${roleID}`, + Member: memberID => { + const mbase = `${base}/members/${memberID}`; + return { + toString: () => mbase, + role: roleID => `${mbase}/roles/${roleID}`, + nickname: `${mbase}/nick`, + }; + }, + }; + }, + channels: '/channels', + Channel: channelID => { + if (channelID.id) channelID = channelID.id; + const base = `/channels/${channelID}`; + return { + toString: () => base, + messages: { + toString: () => `${base}/messages`, + bulkDelete: `${base}/messages/bulk-delete`, + }, + invites: `${base}/invites`, + typing: `${base}/typing`, + permissions: `${base}/permissions`, + webhooks: `${base}/webhooks`, + search: `${base}/search`, + ack: `${base}/ack`, + pins: `${base}/pins`, + Pin: messageID => `${base}/pins/${messageID}`, + Recipient: recipientID => `${base}/recipients/${recipientID}`, + Message: messageID => { + if (messageID.id) messageID = messageID.id; + const mbase = `${base}/messages/${messageID}`; + return { + toString: () => mbase, + reactions: `${mbase}/reactions`, + ack: `${base}/ack`, + Reaction: (emoji, limit) => { + const rbase = `${mbase}/reactions/${emoji}${limit ? `?limit=${limit}` : ''}`; + return { + toString: () => rbase, + User: userID => `${rbase}/${userID}`, + }; + }, + }; + }, + }; + }, + Message: m => exports.Endpoints.Channel(m.channel).Message(m), + Member: m => exports.Endpoints.Guild(m.guild).Member(m), + CDN: { + Emoji: emojiID => `/emojis/$${emojiID}.png`, + Asset: name => `/assets/${name}`, + }, + OAUTH2: { + Application: appID => { + const base = `/oauth2/applications/${appID}`; + return { + toString: () => base, + reset: `${base}/reset`, + }; + }, + App: appID => `/oauth2/authorize?client_id=${appID}`, + }, + login: '/auth/login', + logout: '/auth/logout', + voiceRegions: '/voice/regions', + gateway: { + toString: () => '/gateway', + bot: '/gateway/bot', + }, + Invite: inviteID => `/invite/${inviteID}`, + Webhook: (webhookID, token) => `/webhooks/${webhookID}${token ? `/${token}` : ''}`, }; + /** * The current status of the client. Here are the available statuses: * - READY diff --git a/src/util/Util.js b/src/util/Util.js index 6b5a856dd..7eea58cde 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -1,5 +1,5 @@ const superagent = require('superagent'); -const botGateway = require('./Constants').Endpoints.botGateway; +const Constants = require('./Constants'); /** * Contains various general-purpose utility methods. These functions are also available on the base `Discord` object. @@ -54,7 +54,7 @@ class Util { static fetchRecommendedShards(token, guildsPerShard = 1000) { return new Promise((resolve, reject) => { if (!token) throw new Error('A token must be provided.'); - superagent.get(botGateway) + superagent.get(Constants.Endpoints.gateway.bot) .set('Authorization', `Bot ${token.replace(/^Bot\s*/i, '')}`) .end((err, res) => { if (err) reject(err);