Add files via upload
This commit is contained in:
		
							
								
								
									
										137
									
								
								node_modules/eris/lib/rest/Endpoints.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								node_modules/eris/lib/rest/Endpoints.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const {REST_VERSION} = require("../Constants"); | ||||
|  | ||||
| module.exports.BASE_URL = "/api/v" + REST_VERSION; | ||||
| module.exports.CDN_URL = "https://cdn.discordapp.com"; | ||||
| module.exports.CLIENT_URL = "https://discord.com"; | ||||
|  | ||||
| module.exports.ORIGINAL_INTERACTION_RESPONSE =            (appID, interactToken) => `/webhooks/${appID}/${interactToken}`; | ||||
| module.exports.COMMAND =                              (applicationID, commandID) => `/applications/${applicationID}/commands/${commandID}`; | ||||
| module.exports.COMMANDS =                                        (applicationID) => `/applications/${applicationID}/commands`; | ||||
| module.exports.COMMAND_PERMISSIONS =         (applicationID, guildID, commandID) => `/applications/${applicationID}/guilds/${guildID}/commands/${commandID}/permissions`; | ||||
| module.exports.CHANNEL =                                                (chanID) => `/channels/${chanID}`; | ||||
| module.exports.CHANNEL_BULK_DELETE =                                    (chanID) => `/channels/${chanID}/messages/bulk-delete`; | ||||
| module.exports.CHANNEL_CALL_RING =                                      (chanID) => `/channels/${chanID}/call/ring`; | ||||
| module.exports.CHANNEL_CROSSPOST =                               (chanID, msgID) => `/channels/${chanID}/messages/${msgID}/crosspost`; | ||||
| module.exports.CHANNEL_FOLLOW =                                         (chanID) => `/channels/${chanID}/followers`; | ||||
| module.exports.CHANNEL_INVITES =                                        (chanID) => `/channels/${chanID}/invites`; | ||||
| module.exports.CHANNEL_MESSAGE_REACTION =              (chanID, msgID, reaction) => `/channels/${chanID}/messages/${msgID}/reactions/${reaction}`; | ||||
| module.exports.CHANNEL_MESSAGE_REACTION_USER = (chanID, msgID, reaction, userID) => `/channels/${chanID}/messages/${msgID}/reactions/${reaction}/${userID}`; | ||||
| module.exports.CHANNEL_MESSAGE_REACTIONS =                       (chanID, msgID) => `/channels/${chanID}/messages/${msgID}/reactions`; | ||||
| module.exports.CHANNEL_MESSAGE =                                 (chanID, msgID) => `/channels/${chanID}/messages/${msgID}`; | ||||
| module.exports.CHANNEL_MESSAGES =                                       (chanID) => `/channels/${chanID}/messages`; | ||||
| module.exports.CHANNEL_MESSAGES_SEARCH =                                (chanID) => `/channels/${chanID}/messages/search`; | ||||
| module.exports.CHANNEL_PERMISSION =                             (chanID, overID) => `/channels/${chanID}/permissions/${overID}`; | ||||
| module.exports.CHANNEL_PERMISSIONS =                                    (chanID) => `/channels/${chanID}/permissions`; | ||||
| module.exports.CHANNEL_PIN =                                     (chanID, msgID) => `/channels/${chanID}/pins/${msgID}`; | ||||
| module.exports.CHANNEL_PINS =                                           (chanID) => `/channels/${chanID}/pins`; | ||||
| module.exports.CHANNEL_RECIPIENT =                             (groupID, userID) => `/channels/${groupID}/recipients/${userID}`; | ||||
| module.exports.CHANNEL_TYPING =                                         (chanID) => `/channels/${chanID}/typing`; | ||||
| module.exports.CHANNEL_WEBHOOKS =                                       (chanID) => `/channels/${chanID}/webhooks`; | ||||
| module.exports.CHANNELS =                                                           "/channels"; | ||||
| module.exports.CUSTOM_EMOJI_GUILD =                                    (emojiID) => `/emojis/${emojiID}/guild`; | ||||
| module.exports.DISCOVERY_CATEGORIES =                                               "/discovery/categories"; | ||||
| module.exports.DISCOVERY_VALIDATION =                                               "/discovery/valid-term"; | ||||
| module.exports.GATEWAY =                                                            "/gateway"; | ||||
| module.exports.GATEWAY_BOT =                                                        "/gateway/bot"; | ||||
| module.exports.GUILD =                                                 (guildID) => `/guilds/${guildID}`; | ||||
| module.exports.GUILD_AUDIT_LOGS =                                      (guildID) => `/guilds/${guildID}/audit-logs`; | ||||
| module.exports.GUILD_BAN =                                   (guildID, memberID) => `/guilds/${guildID}/bans/${memberID}`; | ||||
| module.exports.GUILD_BANS =                                            (guildID) => `/guilds/${guildID}/bans`; | ||||
| module.exports.GUILD_CHANNELS =                                        (guildID) => `/guilds/${guildID}/channels`; | ||||
| module.exports.GUILD_COMMAND =               (applicationID, guildID, commandID) => `/applications/${applicationID}/guilds/${guildID}/commands/${commandID}`; | ||||
| module.exports.GUILD_COMMAND_PERMISSIONS =                    (applicationID, guildID) => `/applications/${applicationID}/guilds/${guildID}/commands/permissions`; | ||||
| module.exports.GUILD_COMMANDS =                         (applicationID, guildID) => `/applications/${applicationID}/guilds/${guildID}/commands`; | ||||
| module.exports.GUILD_DISCOVERY =                                       (guildID) => `/guilds/${guildID}/discovery-metadata`; | ||||
| module.exports.GUILD_DISCOVERY_CATEGORY =                  (guildID, categoryID) => `/guilds/${guildID}/discovery-categories/${categoryID}`; | ||||
| module.exports.GUILD_EMOJI =                                  (guildID, emojiID) => `/guilds/${guildID}/emojis/${emojiID}`; | ||||
| module.exports.GUILD_EMOJIS =                                          (guildID) => `/guilds/${guildID}/emojis`; | ||||
| module.exports.GUILD_INTEGRATION =                             (guildID, inteID) => `/guilds/${guildID}/integrations/${inteID}`; | ||||
| module.exports.GUILD_INTEGRATION_SYNC =                        (guildID, inteID) => `/guilds/${guildID}/integrations/${inteID}/sync`; | ||||
| module.exports.GUILD_INTEGRATIONS =                                    (guildID) => `/guilds/${guildID}/integrations`; | ||||
| module.exports.GUILD_INVITES =                                         (guildID) => `/guilds/${guildID}/invites`; | ||||
| module.exports.GUILD_VANITY_URL =                                      (guildID) => `/guilds/${guildID}/vanity-url`; | ||||
| module.exports.GUILD_MEMBER =                                (guildID, memberID) => `/guilds/${guildID}/members/${memberID}`; | ||||
| module.exports.GUILD_MEMBER_NICK =                           (guildID, memberID) => `/guilds/${guildID}/members/${memberID}/nick`; | ||||
| module.exports.GUILD_MEMBER_ROLE =                   (guildID, memberID, roleID) => `/guilds/${guildID}/members/${memberID}/roles/${roleID}`; | ||||
| module.exports.GUILD_MEMBERS =                                         (guildID) => `/guilds/${guildID}/members`; | ||||
| module.exports.GUILD_MEMBERS_SEARCH =                                  (guildID) => `/guilds/${guildID}/members/search`; | ||||
| module.exports.GUILD_MESSAGES_SEARCH =                                 (guildID) => `/guilds/${guildID}/messages/search`; | ||||
| module.exports.GUILD_PREVIEW =                                         (guildID) => `/guilds/${guildID}/preview`; | ||||
| module.exports.GUILD_PRUNE =                                           (guildID) => `/guilds/${guildID}/prune`; | ||||
| module.exports.GUILD_ROLE =                                    (guildID, roleID) => `/guilds/${guildID}/roles/${roleID}`; | ||||
| module.exports.GUILD_ROLES =                                           (guildID) => `/guilds/${guildID}/roles`; | ||||
| module.exports.GUILD_SCHEDULED_EVENT =               (guildID, scheduledEventID) => `/guilds/${guildID}/scheduled-events/${scheduledEventID}`; | ||||
| module.exports.GUILD_SCHEDULED_EVENT_USERS =         (guildID, scheduledEventID) => `/guilds/${guildID}/scheduled-events/${scheduledEventID}/users`; | ||||
| module.exports.GUILD_SCHEDULED_EVENTS =                                (guildID) => `/guilds/${guildID}/scheduled-events`; | ||||
| module.exports.GUILD_STICKER =                              (guildID, stickerID) => `/guilds/${guildID}/stickers/${stickerID}`; | ||||
| module.exports.GUILD_STICKERS =                                        (guildID) => `/guilds/${guildID}/stickers`; | ||||
| module.exports.GUILD_TEMPLATE =                                           (code) => `/guilds/templates/${code}`; | ||||
| module.exports.GUILD_TEMPLATES =                                       (guildID) => `/guilds/${guildID}/templates`; | ||||
| module.exports.GUILD_TEMPLATE_GUILD =                            (guildID, code) => `/guilds/${guildID}/templates/${code}`; | ||||
| module.exports.GUILD_VOICE_REGIONS =                                   (guildID) => `/guilds/${guildID}/regions`; | ||||
| module.exports.GUILD_WEBHOOKS =                                        (guildID) => `/guilds/${guildID}/webhooks`; | ||||
| module.exports.GUILD_WELCOME_SCREEN =                                  (guildID) => `/guilds/${guildID}/welcome-screen`; | ||||
| module.exports.GUILD_WIDGET =                                          (guildID) => `/guilds/${guildID}/widget.json`; | ||||
| module.exports.GUILD_WIDGET_SETTINGS =                                 (guildID) => `/guilds/${guildID}/widget`; | ||||
| module.exports.GUILD_VOICE_STATE =                               (guildID, user) => `/guilds/${guildID}/voice-states/${user}`; | ||||
| module.exports.GUILDS =                                                             "/guilds"; | ||||
| module.exports.INTERACTION_RESPOND =                 (interactID, interactToken) => `/interactions/${interactID}/${interactToken}/callback`; | ||||
| module.exports.INVITE =                                               (inviteID) => `/invites/${inviteID}`; | ||||
| module.exports.OAUTH2_APPLICATION =                                      (appID) => `/oauth2/applications/${appID}`; | ||||
| module.exports.STAGE_INSTANCE =                                      (channelID) => `/stage-instances/${channelID}`; | ||||
| module.exports.STAGE_INSTANCES =                                                    "/stage-instances"; | ||||
| module.exports.STICKER =                                             (stickerID) => `/stickers/${stickerID}`; | ||||
| module.exports.STICKER_PACKS =                                                      "/sticker-packs"; | ||||
| module.exports.THREAD_MEMBER =                               (channelID, userID) => `/channels/${channelID}/thread-members/${userID}`; | ||||
| module.exports.THREAD_MEMBERS =                                      (channelID) => `/channels/${channelID}/thread-members`; | ||||
| module.exports.THREAD_WITH_MESSAGE =                          (channelID, msgID) => `/channels/${channelID}/messages/${msgID}/threads`; | ||||
| module.exports.THREAD_WITHOUT_MESSAGE =                              (channelID) => `/channels/${channelID}/threads`; | ||||
| module.exports.THREADS_ACTIVE =                                      (channelID) => `/channels/${channelID}/threads/active`; | ||||
| module.exports.THREADS_ARCHIVED =                              (channelID, type) => `/channels/${channelID}/threads/archived/${type}`; | ||||
| module.exports.THREADS_ARCHIVED_JOINED =                             (channelID) => `/channels/${channelID}/users/@me/threads/archived/private`; | ||||
| module.exports.THREADS_GUILD_ACTIVE =                                  (guildID) => `/guilds/${guildID}/threads/active`; | ||||
| module.exports.USER =                                                   (userID) => `/users/${userID}`; | ||||
| module.exports.USER_BILLING =                                           (userID) => `/users/${userID}/billing`; | ||||
| module.exports.USER_BILLING_PAYMENTS =                                  (userID) => `/users/${userID}/billing/payments`; | ||||
| module.exports.USER_BILLING_PREMIUM_SUBSCRIPTION =                      (userID) => `/users/${userID}/billing/premium-subscription`; | ||||
| module.exports.USER_CHANNELS =                                          (userID) => `/users/${userID}/channels`; | ||||
| module.exports.USER_CONNECTIONS =                                       (userID) => `/users/${userID}/connections`; | ||||
| module.exports.USER_CONNECTION_PLATFORM =                 (userID, platform, id) => `/users/${userID}/connections/${platform}/${id}`; | ||||
| module.exports.USER_GUILD =                                    (userID, guildID) => `/users/${userID}/guilds/${guildID}`; | ||||
| module.exports.USER_GUILDS =                                            (userID) => `/users/${userID}/guilds`; | ||||
| module.exports.USER_MFA_CODES =                                         (userID) => `/users/${userID}/mfa/codes`; | ||||
| module.exports.USER_MFA_TOTP_DISABLE =                                  (userID) => `/users/${userID}/mfa/totp/disable`; | ||||
| module.exports.USER_MFA_TOTP_ENABLE =                                   (userID) => `/users/${userID}/mfa/totp/enable`; | ||||
| module.exports.USER_NOTE =                                    (userID, targetID) => `/users/${userID}/note/${targetID}`; | ||||
| module.exports.USER_PROFILE =                                           (userID) => `/users/${userID}/profile`; | ||||
| module.exports.USER_RELATIONSHIP =                               (userID, relID) => `/users/${userID}/relationships/${relID}`; | ||||
| module.exports.USER_SETTINGS =                                          (userID) => `/users/${userID}/settings`; | ||||
| module.exports.USERS =                                                              "/users"; | ||||
| module.exports.VOICE_REGIONS =                                                      "/voice/regions"; | ||||
| module.exports.WEBHOOK =                                                (hookID) => `/webhooks/${hookID}`; | ||||
| module.exports.WEBHOOK_MESSAGE =                          (hookID, token, msgID) => `/webhooks/${hookID}/${token}/messages/${msgID}`; | ||||
| module.exports.WEBHOOK_SLACK =                                          (hookID) => `/webhooks/${hookID}/slack`; | ||||
| module.exports.WEBHOOK_TOKEN =                                   (hookID, token) => `/webhooks/${hookID}/${token}`; | ||||
| module.exports.WEBHOOK_TOKEN_SLACK =                             (hookID, token) => `/webhooks/${hookID}/${token}/slack`; | ||||
|  | ||||
| // CDN Endpoints | ||||
| module.exports.ACHIEVEMENT_ICON =           (applicationID, achievementID, icon) => `/app-assets/${applicationID}/achievements/${achievementID}/icons/${icon}`; | ||||
| module.exports.APPLICATION_ASSET =                        (applicationID, asset) => `/app-assets/${applicationID}/${asset}`; | ||||
| module.exports.APPLICATION_ICON =                          (applicationID, icon) => `/app-icons/${applicationID}/${icon}`; | ||||
| module.exports.BANNER =                                    (guildOrUserID, hash) => `/banners/${guildOrUserID}/${hash}`; | ||||
| module.exports.CHANNEL_ICON =                                 (chanID, chanIcon) => `/channel-icons/${chanID}/${chanIcon}`; | ||||
| module.exports.CUSTOM_EMOJI =                                          (emojiID) => `/emojis/${emojiID}`; | ||||
| module.exports.DEFAULT_USER_AVATAR =                         (userDiscriminator) => `/embed/avatars/${userDiscriminator}`; | ||||
| module.exports.GUILD_AVATAR =                     (guildID, userID, guildAvatar) => `/guilds/${guildID}/users/${userID}/avatars/${guildAvatar}`; | ||||
| module.exports.GUILD_DISCOVERY_SPLASH =          (guildID, guildDiscoverySplash) => `/discovery-splashes/${guildID}/${guildDiscoverySplash}`; | ||||
| module.exports.GUILD_ICON =                                 (guildID, guildIcon) => `/icons/${guildID}/${guildIcon}`; | ||||
| module.exports.GUILD_SCHEDULED_EVENT_COVER =                (eventID, eventIcon) => `/guild-events/${eventID}/${eventIcon}`; | ||||
| module.exports.GUILD_SPLASH =                             (guildID, guildSplash) => `/splashes/${guildID}/${guildSplash}`; | ||||
| module.exports.ROLE_ICON =                                    (roleID, roleIcon) => `/role-icons/${roleID}/${roleIcon}`; | ||||
| module.exports.TEAM_ICON =                                    (teamID, teamIcon) => `/team-icons/${teamID}/${teamIcon}`; | ||||
| module.exports.USER_AVATAR =                                (userID, userAvatar) => `/avatars/${userID}/${userAvatar}`; | ||||
|  | ||||
| // Client Endpoints | ||||
| module.exports.MESSAGE_LINK =                    (guildID, channelID, messageID) => `/channels/${guildID}/${channelID}/${messageID}`; | ||||
							
								
								
									
										451
									
								
								node_modules/eris/lib/rest/RequestHandler.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										451
									
								
								node_modules/eris/lib/rest/RequestHandler.js
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,451 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const util = require("util"); | ||||
| const Base = require("../structures/Base"); | ||||
| const DiscordHTTPError = require("../errors/DiscordHTTPError"); | ||||
| const DiscordRESTError = require("../errors/DiscordRESTError"); | ||||
| const Endpoints = require("./Endpoints"); | ||||
| const HTTPS = require("https"); | ||||
| const MultipartData = require("../util/MultipartData"); | ||||
| const SequentialBucket = require("../util/SequentialBucket"); | ||||
| const Zlib = require("zlib"); | ||||
|  | ||||
| /** | ||||
| * Handles API requests | ||||
| */ | ||||
| class RequestHandler { | ||||
|     constructor(client, options) { | ||||
|         // [DEPRECATED] Previously forceQueueing | ||||
|         if(typeof options === "boolean") { | ||||
|             options = { | ||||
|                 forceQueueing: options | ||||
|             }; | ||||
|         } | ||||
|         this.options = options = Object.assign({ | ||||
|             agent: client.options.agent || null, | ||||
|             baseURL: Endpoints.BASE_URL, | ||||
|             decodeReasons: true, | ||||
|             disableLatencyCompensation: false, | ||||
|             domain: "discord.com", | ||||
|             latencyThreshold: client.options.latencyThreshold || 30000, | ||||
|             ratelimiterOffset: client.options.ratelimiterOffset || 0, | ||||
|             requestTimeout: client.options.requestTimeout || 15000 | ||||
|         }, options); | ||||
|  | ||||
|         this._client = client; | ||||
|         this.userAgent = `DiscordBot (https://github.com/abalabahaha/eris, ${require("../../package.json").version})`; | ||||
|         this.ratelimits = {}; | ||||
|         this.latencyRef = { | ||||
|             latency: this.options.ratelimiterOffset, | ||||
|             raw: new Array(10).fill(this.options.ratelimiterOffset), | ||||
|             timeOffset: 0, | ||||
|             timeOffsets: new Array(10).fill(0), | ||||
|             lastTimeOffsetCheck: 0 | ||||
|         }; | ||||
|         this.globalBlock = false; | ||||
|         this.readyQueue = []; | ||||
|         if(this.options.forceQueueing) { | ||||
|             this.globalBlock = true; | ||||
|             this._client.once("shardPreReady", () => this.globalUnblock()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     globalUnblock() { | ||||
|         this.globalBlock = false; | ||||
|         while(this.readyQueue.length > 0) { | ||||
|             this.readyQueue.shift()(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|     * Make an API request | ||||
|     * @arg {String} method Uppercase HTTP method | ||||
|     * @arg {String} url URL of the endpoint | ||||
|     * @arg {Boolean} [auth] Whether to add the Authorization header and token or not | ||||
|     * @arg {Object} [body] Request payload | ||||
|     * @arg {Object} [file] File object | ||||
|     * @arg {Buffer} file.file A buffer containing file data | ||||
|     * @arg {String} file.name What to name the file | ||||
|     * @returns {Promise<Object>} Resolves with the returned JSON data | ||||
|     */ | ||||
|     request(method, url, auth, body, file, _route, short) { | ||||
|         const route = _route || this.routefy(url, method); | ||||
|  | ||||
|         const _stackHolder = {}; // Preserve async stack | ||||
|         Error.captureStackTrace(_stackHolder); | ||||
|  | ||||
|         return new Promise((resolve, reject) => { | ||||
|             let attempts = 0; | ||||
|  | ||||
|             const actualCall = (cb) => { | ||||
|                 const headers = { | ||||
|                     "User-Agent": this.userAgent, | ||||
|                     "Accept-Encoding": "gzip,deflate" | ||||
|                 }; | ||||
|                 let data; | ||||
|                 let finalURL = url; | ||||
|  | ||||
|                 try { | ||||
|                     if(auth) { | ||||
|                         headers.Authorization = this._client._token; | ||||
|                     } | ||||
|                     if(body && body.reason) { // Audit log reason sniping | ||||
|                         let unencodedReason = body.reason; | ||||
|                         if(this.options.decodeReasons) { | ||||
|                             try { | ||||
|                                 if(unencodedReason.includes("%") && !unencodedReason.includes(" ")) { | ||||
|                                     unencodedReason = decodeURIComponent(unencodedReason); | ||||
|                                 } | ||||
|                             } catch(err) { | ||||
|                                 this._client.emit("error", err); | ||||
|                             } | ||||
|                         } | ||||
|                         headers["X-Audit-Log-Reason"] = encodeURIComponent(unencodedReason); | ||||
|                         if((method !== "PUT" || !url.includes("/bans")) && (method !== "POST" || !url.includes("/prune"))) { | ||||
|                             delete body.reason; | ||||
|                         } else { | ||||
|                             body.reason = unencodedReason; | ||||
|                         } | ||||
|                     } | ||||
|                     if(file) { | ||||
|                         if(Array.isArray(file)) { | ||||
|                             data = new MultipartData(); | ||||
|                             headers["Content-Type"] = "multipart/form-data; boundary=" + data.boundary; | ||||
|                             file.forEach(function(f) { | ||||
|                                 if(!f.file) { | ||||
|                                     return; | ||||
|                                 } | ||||
|                                 data.attach(f.name, f.file, f.name); | ||||
|                             }); | ||||
|                             if(body) { | ||||
|                                 data.attach("payload_json", body); | ||||
|                             } | ||||
|                             data = data.finish(); | ||||
|                         } else if(file.file) { | ||||
|                             data = new MultipartData(); | ||||
|                             headers["Content-Type"] = "multipart/form-data; boundary=" + data.boundary; | ||||
|                             data.attach("file", file.file, file.name); | ||||
|                             if(body) { | ||||
|                                 if(method === "POST" && url.endsWith("/stickers")) { | ||||
|                                     for(const key in body) { | ||||
|                                         data.attach(key, body[key]); | ||||
|                                     } | ||||
|                                 } else { | ||||
|                                     data.attach("payload_json", body); | ||||
|                                 } | ||||
|                             } | ||||
|                             data = data.finish(); | ||||
|                         } else { | ||||
|                             throw new Error("Invalid file object"); | ||||
|                         } | ||||
|                     } else if(body) { | ||||
|                         if(method === "GET" || method === "DELETE") { | ||||
|                             let qs = ""; | ||||
|                             Object.keys(body).forEach(function(key) { | ||||
|                                 if(body[key] != undefined) { | ||||
|                                     if(Array.isArray(body[key])) { | ||||
|                                         body[key].forEach(function(val) { | ||||
|                                             qs += `&${encodeURIComponent(key)}=${encodeURIComponent(val)}`; | ||||
|                                         }); | ||||
|                                     } else { | ||||
|                                         qs += `&${encodeURIComponent(key)}=${encodeURIComponent(body[key])}`; | ||||
|                                     } | ||||
|                                 } | ||||
|                             }); | ||||
|                             finalURL += "?" + qs.substring(1); | ||||
|                         } else { | ||||
|                             // Replacer function serializes bigints to strings, the format Discord uses | ||||
|                             data = JSON.stringify(body, (k, v) => typeof v === "bigint" ? v.toString() : v); | ||||
|                             headers["Content-Type"] = "application/json"; | ||||
|                         } | ||||
|                     } | ||||
|                 } catch(err) { | ||||
|                     cb(); | ||||
|                     reject(err); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 let req; | ||||
|                 try { | ||||
|                     req = HTTPS.request({ | ||||
|                         method: method, | ||||
|                         host: this.options.domain, | ||||
|                         path: this.options.baseURL + finalURL, | ||||
|                         headers: headers, | ||||
|                         agent: this.options.agent | ||||
|                     }); | ||||
|                 } catch(err) { | ||||
|                     cb(); | ||||
|                     reject(err); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 let reqError; | ||||
|  | ||||
|                 req.once("abort", () => { | ||||
|                     cb(); | ||||
|                     reqError = reqError || new Error(`Request aborted by client on ${method} ${url}`); | ||||
|                     reqError.req = req; | ||||
|                     reject(reqError); | ||||
|                 }).once("error", (err) => { | ||||
|                     reqError = err; | ||||
|                     req.abort(); | ||||
|                 }); | ||||
|  | ||||
|                 let latency = Date.now(); | ||||
|  | ||||
|                 req.once("response", (resp) => { | ||||
|                     latency = Date.now() - latency; | ||||
|                     if(!this.options.disableLatencyCompensation) { | ||||
|                         this.latencyRef.raw.push(latency); | ||||
|                         this.latencyRef.latency = this.latencyRef.latency - ~~(this.latencyRef.raw.shift() / 10) + ~~(latency / 10); | ||||
|                     } | ||||
|  | ||||
|                     if(this._client.listeners("rawREST").length) { | ||||
|                         /** | ||||
|                          * Fired when the Client's RequestHandler receives a response | ||||
|                          * @event Client#rawREST | ||||
|                          * @prop {Object} [request] The data for the request. | ||||
|                          * @prop {Boolean} request.auth True if the request required an authorization token | ||||
|                          * @prop {Object} [request.body] The request payload | ||||
|                          * @prop {Object} [request.file] The file object sent in the request | ||||
|                          * @prop {Buffer} request.file.file A buffer containing file data | ||||
|                          * @prop {String} request.file.name The name of the file | ||||
|                          * @prop {Number} request.latency The HTTP response latency | ||||
|                          * @prop {String} request.method Uppercase HTTP method | ||||
|                          * @prop {IncomingMessage} request.resp The HTTP response to the request | ||||
|                          * @prop {String} request.route The calculated ratelimiting route for the request | ||||
|                          * @prop {Boolean} request.short Whether or not the request was prioritized in its ratelimiting queue | ||||
|                          * @prop {String} request.url URL of the endpoint | ||||
|                          */ | ||||
|                         this._client.emit("rawREST", {method, url, auth, body, file, route, short, resp, latency}); | ||||
|                     } | ||||
|  | ||||
|                     const headerNow = Date.parse(resp.headers["date"]); | ||||
|                     if(this.latencyRef.lastTimeOffsetCheck < Date.now() - 5000) { | ||||
|                         const timeOffset = headerNow + 500 - (this.latencyRef.lastTimeOffsetCheck = Date.now()); | ||||
|                         if(this.latencyRef.timeOffset - this.latencyRef.latency >= this.options.latencyThreshold && timeOffset - this.latencyRef.latency >= this.options.latencyThreshold) { | ||||
|                             this._client.emit("warn", new Error(`Your clock is ${this.latencyRef.timeOffset}ms behind Discord's server clock. Please check your connection and system time.`)); | ||||
|                         } | ||||
|                         this.latencyRef.timeOffset = this.latencyRef.timeOffset - ~~(this.latencyRef.timeOffsets.shift() / 10) + ~~(timeOffset / 10); | ||||
|                         this.latencyRef.timeOffsets.push(timeOffset); | ||||
|                     } | ||||
|  | ||||
|                     resp.once("aborted", () => { | ||||
|                         cb(); | ||||
|                         reqError = reqError || new Error(`Request aborted by server on ${method} ${url}`); | ||||
|                         reqError.req = req; | ||||
|                         reject(reqError); | ||||
|                     }); | ||||
|  | ||||
|                     let response = ""; | ||||
|  | ||||
|                     let _respStream = resp; | ||||
|                     if(resp.headers["content-encoding"]) { | ||||
|                         if(resp.headers["content-encoding"].includes("gzip")) { | ||||
|                             _respStream = resp.pipe(Zlib.createGunzip()); | ||||
|                         } else if(resp.headers["content-encoding"].includes("deflate")) { | ||||
|                             _respStream = resp.pipe(Zlib.createInflate()); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     _respStream.on("data", (str) => { | ||||
|                         response += str; | ||||
|                     }).on("error", (err) => { | ||||
|                         reqError = err; | ||||
|                         req.abort(); | ||||
|                     }).once("end", () => { | ||||
|                         const now = Date.now(); | ||||
|  | ||||
|                         if(resp.headers["x-ratelimit-limit"]) { | ||||
|                             this.ratelimits[route].limit = +resp.headers["x-ratelimit-limit"]; | ||||
|                         } | ||||
|  | ||||
|                         if(method !== "GET" && (resp.headers["x-ratelimit-remaining"] == undefined || resp.headers["x-ratelimit-limit"] == undefined) && this.ratelimits[route].limit !== 1) { | ||||
|                             this._client.emit("debug", `Missing ratelimit headers for SequentialBucket(${this.ratelimits[route].remaining}/${this.ratelimits[route].limit}) with non-default limit\n` | ||||
|                                 + `${resp.statusCode} ${resp.headers["content-type"]}: ${method} ${route} | ${resp.headers["cf-ray"]}\n` | ||||
|                                 + "content-type = " +  + "\n" | ||||
|                                 + "x-ratelimit-remaining = " + resp.headers["x-ratelimit-remaining"] + "\n" | ||||
|                                 + "x-ratelimit-limit = " + resp.headers["x-ratelimit-limit"] + "\n" | ||||
|                                 + "x-ratelimit-reset = " + resp.headers["x-ratelimit-reset"] + "\n" | ||||
|                                 + "x-ratelimit-global = " + resp.headers["x-ratelimit-global"]); | ||||
|                         } | ||||
|  | ||||
|                         this.ratelimits[route].remaining = resp.headers["x-ratelimit-remaining"] === undefined ? 1 : +resp.headers["x-ratelimit-remaining"] || 0; | ||||
|  | ||||
|                         const retryAfter = Number(resp.headers["x-ratelimit-reset-after"] || resp.headers["retry-after"]) * 1000; | ||||
|                         if(retryAfter >= 0) { | ||||
|                             if(resp.headers["x-ratelimit-global"]) { | ||||
|                                 this.globalBlock = true; | ||||
|                                 setTimeout(() => this.globalUnblock(), retryAfter || 1); | ||||
|                             } else { | ||||
|                                 this.ratelimits[route].reset = (retryAfter || 1) + now; | ||||
|                             } | ||||
|                         } else if(resp.headers["x-ratelimit-reset"]) { | ||||
|                             let resetTime = +resp.headers["x-ratelimit-reset"] * 1000; | ||||
|                             if(route.endsWith("/reactions/:id") && (+resp.headers["x-ratelimit-reset"] * 1000 - headerNow) === 1000) { | ||||
|                                 resetTime = now + 250; | ||||
|                             } | ||||
|                             this.ratelimits[route].reset = Math.max(resetTime - this.latencyRef.latency, now); | ||||
|                         } else { | ||||
|                             this.ratelimits[route].reset = now; | ||||
|                         } | ||||
|  | ||||
|                         if(resp.statusCode !== 429) { | ||||
|                             const content = typeof body === "object" ? `${body.content} ` : ""; | ||||
|                             this._client.emit("debug", `${content}${now} ${route} ${resp.statusCode}: ${latency}ms (${this.latencyRef.latency}ms avg) | ${this.ratelimits[route].remaining}/${this.ratelimits[route].limit} left | Reset ${this.ratelimits[route].reset} (${this.ratelimits[route].reset - now}ms left)`); | ||||
|                         } | ||||
|  | ||||
|                         if(resp.statusCode >= 300) { | ||||
|                             if(resp.statusCode === 429) { | ||||
|                                 const content = typeof body === "object" ? `${body.content} ` : ""; | ||||
|                                 let delay = retryAfter; | ||||
|                                 if(resp.headers["x-ratelimit-scope"] === "shared") { | ||||
|                                     try { | ||||
|                                         delay = JSON.parse(response).retry_after * 1000; | ||||
|                                     } catch(err) { | ||||
|                                         reject(err); | ||||
|                                         return; | ||||
|                                     } | ||||
|                                 } | ||||
|                                 this._client.emit("debug", `${resp.headers["x-ratelimit-global"] ? "Global" : "Unexpected"} 429 (╯°□°)╯︵ ┻━┻: ${response}\n${content} ${now} ${route} ${resp.statusCode}: ${latency}ms (${this.latencyRef.latency}ms avg) | ${this.ratelimits[route].remaining}/${this.ratelimits[route].limit} left | Reset ${delay} (${this.ratelimits[route].reset - now}ms left) | Scope ${resp.headers["x-ratelimit-scope"]}`); | ||||
|                                 if(delay) { | ||||
|                                     setTimeout(() => { | ||||
|                                         cb(); | ||||
|                                         this.request(method, url, auth, body, file, route, true).then(resolve).catch(reject); | ||||
|                                     }, delay); | ||||
|                                     return; | ||||
|                                 } else { | ||||
|                                     cb(); | ||||
|                                     this.request(method, url, auth, body, file, route, true).then(resolve).catch(reject); | ||||
|                                     return; | ||||
|                                 } | ||||
|                             } else if(resp.statusCode === 502 && ++attempts < 4) { | ||||
|                                 this._client.emit("debug", "A wild 502 appeared! Thanks CloudFlare!"); | ||||
|                                 setTimeout(() => { | ||||
|                                     this.request(method, url, auth, body, file, route, true).then(resolve).catch(reject); | ||||
|                                 }, Math.floor(Math.random() * 1900 + 100)); | ||||
|                                 return cb(); | ||||
|                             } | ||||
|                             cb(); | ||||
|  | ||||
|                             if(response.length > 0) { | ||||
|                                 if(resp.headers["content-type"] === "application/json") { | ||||
|                                     try { | ||||
|                                         response = JSON.parse(response); | ||||
|                                     } catch(err) { | ||||
|                                         reject(err); | ||||
|                                         return; | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|  | ||||
|                             let {stack} = _stackHolder; | ||||
|                             if(stack.startsWith("Error\n")) { | ||||
|                                 stack = stack.substring(6); | ||||
|                             } | ||||
|                             let err; | ||||
|                             if(response.code) { | ||||
|                                 err = new DiscordRESTError(req, resp, response, stack); | ||||
|                             } else { | ||||
|                                 err = new DiscordHTTPError(req, resp, response, stack); | ||||
|                             } | ||||
|                             reject(err); | ||||
|                             return; | ||||
|                         } | ||||
|  | ||||
|                         if(response.length > 0) { | ||||
|                             if(resp.headers["content-type"] === "application/json") { | ||||
|                                 try { | ||||
|                                     response = JSON.parse(response); | ||||
|                                 } catch(err) { | ||||
|                                     cb(); | ||||
|                                     reject(err); | ||||
|                                     return; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         cb(); | ||||
|                         resolve(response); | ||||
|                     }); | ||||
|                 }); | ||||
|  | ||||
|                 req.setTimeout(this.options.requestTimeout, () => { | ||||
|                     reqError = new Error(`Request timed out (>${this.options.requestTimeout}ms) on ${method} ${url}`); | ||||
|                     req.abort(); | ||||
|                 }); | ||||
|  | ||||
|                 if(Array.isArray(data)) { | ||||
|                     for(const chunk of data) { | ||||
|                         req.write(chunk); | ||||
|                     } | ||||
|                     req.end(); | ||||
|                 } else { | ||||
|                     req.end(data); | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             if(this.globalBlock && auth) { | ||||
|                 this.readyQueue.push(() => { | ||||
|                     if(!this.ratelimits[route]) { | ||||
|                         this.ratelimits[route] = new SequentialBucket(1, this.latencyRef); | ||||
|                     } | ||||
|                     this.ratelimits[route].queue(actualCall, short); | ||||
|                 }); | ||||
|             } else { | ||||
|                 if(!this.ratelimits[route]) { | ||||
|                     this.ratelimits[route] = new SequentialBucket(1, this.latencyRef); | ||||
|                 } | ||||
|                 this.ratelimits[route].queue(actualCall, short); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     routefy(url, method) { | ||||
|         let route = url.replace(/\/([a-z-]+)\/(?:[0-9]{17,19})/g, function(match, p) { | ||||
|             return p === "channels" || p === "guilds" || p === "webhooks" ? match : `/${p}/:id`; | ||||
|         }).replace(/\/reactions\/[^/]+/g, "/reactions/:id").replace(/\/reactions\/:id\/[^/]+/g, "/reactions/:id/:userID").replace(/^\/webhooks\/(\d+)\/[A-Za-z0-9-_]{64,}/, "/webhooks/$1/:token"); | ||||
|         if(method === "DELETE" && route.endsWith("/messages/:id")) { | ||||
|             const messageID = url.slice(url.lastIndexOf("/") + 1); | ||||
|             const createdAt = Base.getCreatedAt(messageID); | ||||
|             if(Date.now() - this.latencyRef.latency - createdAt >= 1000 * 60 * 60 * 24 * 14) { | ||||
|                 method += "_OLD"; | ||||
|             } else if(Date.now() - this.latencyRef.latency - createdAt <= 1000 * 10) { | ||||
|                 method += "_NEW"; | ||||
|             } | ||||
|             route = method + route; | ||||
|         } else if(method === "GET" && /\/guilds\/[0-9]+\/channels$/.test(route)) { | ||||
|             route = "/guilds/:id/channels"; | ||||
|         } | ||||
|         if(method === "PUT" || method === "DELETE") { | ||||
|             const index = route.indexOf("/reactions"); | ||||
|             if(index !== -1) { | ||||
|                 route = "MODIFY" + route.slice(0, index + 10); | ||||
|             } | ||||
|         } | ||||
|         return route; | ||||
|     } | ||||
|  | ||||
|     [util.inspect.custom]() { | ||||
|         return Base.prototype[util.inspect.custom].call(this); | ||||
|     } | ||||
|  | ||||
|     toString() { | ||||
|         return "[RequestHandler]"; | ||||
|     } | ||||
|  | ||||
|     toJSON(props = []) { | ||||
|         return Base.prototype.toJSON.call(this, [ | ||||
|             "globalBlock", | ||||
|             "latencyRef", | ||||
|             "options", | ||||
|             "ratelimits", | ||||
|             "readyQueue", | ||||
|             "userAgent", | ||||
|             ...props | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = RequestHandler; | ||||
		Reference in New Issue
	
	Block a user