fix: rest errors preventing startup

This commit is contained in:
Skillz4Killz
2021-04-08 14:14:30 +00:00
committed by GitHub
parent 2e397325f5
commit 3576d98510
10 changed files with 80 additions and 81 deletions
+9 -24
View File
@@ -2,6 +2,7 @@ import { getGatewayBot } from "./helpers/misc/get_gateway_bot.ts";
import { DiscordGatewayIntents } from "./types/gateway/gateway_intents.ts"; import { DiscordGatewayIntents } from "./types/gateway/gateway_intents.ts";
import { DiscordGetGatewayBot } from "./types/gateway/get_gateway_bot.ts"; import { DiscordGetGatewayBot } from "./types/gateway/get_gateway_bot.ts";
import { baseEndpoints, GATEWAY_VERSION } from "./util/constants.ts"; import { baseEndpoints, GATEWAY_VERSION } from "./util/constants.ts";
import { ws } from "./ws/ws.ts";
export let authorization = ""; export let authorization = "";
export let secretKey = ""; export let secretKey = "";
@@ -39,18 +40,14 @@ export async function startBot(config: BotConfig) {
proxyWSURL = botGatewayData.url; proxyWSURL = botGatewayData.url;
identifyPayload.token = config.token; identifyPayload.token = config.token;
identifyPayload.intents = config.intents.reduce( identifyPayload.intents = config.intents.reduce(
( (bits, next) =>
bits, (bits |= typeof next === "string" ? DiscordGatewayIntents[next] : next),
next, 0
) => (bits |= typeof next === "string"
? DiscordGatewayIntents[next]
: next),
0,
); );
lastShardId = botGatewayData.shards; lastShardId = botGatewayData.shards;
identifyPayload.shard = [0, lastShardId]; identifyPayload.shard = [0, lastShardId];
await spawnShards(botGatewayData, identifyPayload, 0, lastShardId); ws.spawnShards();
} }
/** Allows you to dynamically update the event handlers by passing in new eventHandlers */ /** Allows you to dynamically update the event handlers by passing in new eventHandlers */
@@ -92,13 +89,9 @@ export async function startBigBrainBot(data: BigBrainBotConfig) {
} }
identifyPayload.intents = data.intents.reduce( identifyPayload.intents = data.intents.reduce(
( (bits, next) =>
bits, (bits |= typeof next === "string" ? DiscordGatewayIntents[next] : next),
next, 0
) => (bits |= typeof next === "string"
? DiscordGatewayIntents[next]
: next),
0,
); );
// PROXY DOESNT NEED US SPAWNING SHARDS // PROXY DOESNT NEED US SPAWNING SHARDS
@@ -106,15 +99,7 @@ export async function startBigBrainBot(data: BigBrainBotConfig) {
// Initial API connection to get info about bots connection // Initial API connection to get info about bots connection
botGatewayData = await getGatewayBot(); botGatewayData = await getGatewayBot();
proxyWSURL = botGatewayData.url; proxyWSURL = botGatewayData.url;
await spawnShards( ws.spawnShards(data.firstShardId);
botGatewayData,
identifyPayload,
data.firstShardId,
data.lastShardId ||
(botGatewayData.shards >= 25
? (data.firstShardId + 25)
: botGatewayData.shards),
);
} }
} }
+3 -1
View File
@@ -1,3 +1,5 @@
import { USER_AGENT } from "../util/constants.ts";
/** Creates the request body and headers that are necessary to send a request. Will handle different types of methods and everything necessary for discord. */ /** Creates the request body and headers that are necessary to send a request. Will handle different types of methods and everything necessary for discord. */
export function createRequestBody(queuedRequest: QueuedRequest) { export function createRequestBody(queuedRequest: QueuedRequest) {
const headers: { [key: string]: string } = { const headers: { [key: string]: string } = {
@@ -6,7 +8,7 @@ export function createRequestBody(queuedRequest: QueuedRequest) {
}; };
// GET METHODS SHOULD NOT HAVE A BODY // GET METHODS SHOULD NOT HAVE A BODY
if (queuedRequest.request.method === "GET") { if (queuedRequest.request.method.toUpperCase() === "GET") {
queuedRequest.payload.body = undefined; queuedRequest.payload.body = undefined;
} }
+37 -28
View File
@@ -1,4 +1,5 @@
import { DiscordHTTPResponseCodes } from "../types/codes/http_response_codes.ts"; import { DiscordHTTPResponseCodes } from "../types/codes/http_response_codes.ts";
import { delay } from "../util/utils.ts";
import { rest } from "./rest.ts"; import { rest } from "./rest.ts";
/** Processes the queue by looping over each path separately until the queues are empty. */ /** Processes the queue by looping over each path separately until the queues are empty. */
@@ -36,13 +37,19 @@ export async function processQueue(id: string) {
// IF THIS IS A GET REQUEST, CHANGE THE BODY TO QUERY PARAMETERS // IF THIS IS A GET REQUEST, CHANGE THE BODY TO QUERY PARAMETERS
const query = const query =
queuedRequest.request.method === "GET" && queuedRequest.payload.body queuedRequest.request.method.toUpperCase() === "GET" &&
? Object.entries(queuedRequest.payload.body).map(([key, value]) => queuedRequest.payload.body
`${encodeURIComponent(key)}=${encodeURIComponent(value as string)}` ? Object.entries(queuedRequest.payload.body)
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(
value as string
)}`
) )
.join("&") .join("&")
: ""; : "";
const urlToUse = queuedRequest.request.method === "GET" && query const urlToUse =
queuedRequest.request.method.toUpperCase() === "GET" && query
? `${queuedRequest.request.url}?${query}` ? `${queuedRequest.request.url}?${query}`
: queuedRequest.request.url; : queuedRequest.request.url;
@@ -52,45 +59,49 @@ export async function processQueue(id: string) {
try { try {
const response = await fetch( const response = await fetch(
urlToUse, urlToUse,
rest.createRequestBody(queuedRequest), rest.createRequestBody(queuedRequest)
); );
rest.eventHandlers.fetched(queuedRequest.payload); rest.eventHandlers.fetched(queuedRequest.payload);
const bucketIdFromHeaders = rest.processRequestHeaders( const bucketIdFromHeaders = rest.processRequestHeaders(
queuedRequest.request.url, queuedRequest.request.url,
response.headers, response.headers
); );
if (response.status < 200 || response.status >= 400) { if (response.status < 200 || response.status >= 400) {
rest.eventHandlers.error( rest.eventHandlers.error("httpError", queuedRequest.payload, response);
"httpError",
queuedRequest.payload,
response,
);
let error = "REQUEST_UNKNOWN_ERROR"; let error = "REQUEST_UNKNOWN_ERROR";
switch (response.status) { switch (response.status) {
case DiscordHTTPResponseCodes.BadRequest: case DiscordHTTPResponseCodes.BadRequest:
error = error =
"The request was improperly formatted, or the server couldn't understand it."; "The request was improperly formatted, or the server couldn't understand it.";
break;
case DiscordHTTPResponseCodes.Unauthorized: case DiscordHTTPResponseCodes.Unauthorized:
error = "The Authorization header was missing or invalid."; error = "The Authorization header was missing or invalid.";
break;
case DiscordHTTPResponseCodes.Forbidden: case DiscordHTTPResponseCodes.Forbidden:
error = error =
"The Authorization token you passed did not have permission to the resource."; "The Authorization token you passed did not have permission to the resource.";
break;
case DiscordHTTPResponseCodes.NotFound: case DiscordHTTPResponseCodes.NotFound:
error = "The resource at the location specified doesn't exist."; error = "The resource at the location specified doesn't exist.";
break;
case DiscordHTTPResponseCodes.MethodNotAllowed: case DiscordHTTPResponseCodes.MethodNotAllowed:
error = error =
"The HTTP method used is not valid for the location specified."; "The HTTP method used is not valid for the location specified.";
break;
case DiscordHTTPResponseCodes.GatewayUnavailable: case DiscordHTTPResponseCodes.GatewayUnavailable:
error = error =
"There was not a gateway available to process your request. Wait a bit and retry."; "There was not a gateway available to process your request. Wait a bit and retry.";
break;
} }
queuedRequest.request.respond( queuedRequest.request.respond({
{ status: response.status, body: JSON.stringify({ error }) }, status: response.status,
); body: JSON.stringify({ error }),
});
queue.shift(); queue.shift();
continue; continue;
} }
@@ -115,17 +126,13 @@ export async function processQueue(id: string) {
queuedRequest.options.maxRetryCount queuedRequest.options.maxRetryCount
) { ) {
rest.eventHandlers.retriesMaxed(queuedRequest.payload); rest.eventHandlers.retriesMaxed(queuedRequest.payload);
queuedRequest.request.respond( queuedRequest.request.respond({
{
status: 200, status: 200,
body: JSON.stringify( body: JSON.stringify({
{
error: error:
"The request was rate limited and it maxed out the retries limit.", "The request was rate limited and it maxed out the retries limit.",
}, }),
), });
},
);
// REMOVE ITEM FROM QUEUE TO PREVENT RETRY // REMOVE ITEM FROM QUEUE TO PREVENT RETRY
queue.shift(); queue.shift();
continue; continue;
@@ -142,16 +149,18 @@ export async function processQueue(id: string) {
rest.eventHandlers.fetchSuccess(queuedRequest.payload); rest.eventHandlers.fetchSuccess(queuedRequest.payload);
// REMOVE FROM QUEUE // REMOVE FROM QUEUE
queue.shift(); queue.shift();
queuedRequest.request.respond( queuedRequest.request.respond({
{ status: 200, body: JSON.stringify(json) }, status: 200,
); body: JSON.stringify(json),
});
} }
} catch (error) { } catch (error) {
// SOMETHING WENT WRONG, LOG AND RESPOND WITH ERROR // SOMETHING WENT WRONG, LOG AND RESPOND WITH ERROR
rest.eventHandlers.fetchFailed(queuedRequest.payload, error); rest.eventHandlers.fetchFailed(queuedRequest.payload, error);
queuedRequest.request.respond( queuedRequest.request.respond({
{ status: 404, body: JSON.stringify({ error }) }, status: 404,
); body: JSON.stringify({ error }),
});
// REMOVE FROM QUEUE // REMOVE FROM QUEUE
queue.shift(); queue.shift();
} }
+12 -9
View File
@@ -1,8 +1,10 @@
import { BASE_URL } from "../util/constants.ts";
import { rest } from "./rest.ts";
/** Processes a request and assigns it to a queue or creates a queue if none exists for it. */ /** Processes a request and assigns it to a queue or creates a queue if none exists for it. */
export function processRequest( export function processRequest(
request: ServerRequest, request: ServerRequest,
payload: RunMethodOptions, payload: RunMethodOptions
options: RestServerOptions,
) { ) {
const route = request.url.substring(request.url.indexOf("api/")); const route = request.url.substring(request.url.indexOf("api/"));
const parts = route.split("/"); const parts = route.split("/");
@@ -11,23 +13,24 @@ export function processRequest(
// REMOVES THE VERSION NUMBER // REMOVES THE VERSION NUMBER
if (parts[0]?.startsWith("v")) parts.shift(); if (parts[0]?.startsWith("v")) parts.shift();
// SET THE NEW REQUEST URL // SET THE NEW REQUEST URL
request.url = `${BASE_URL}/v${options.apiVersion || 8}/${parts.join("/")}`; request.url = `${BASE_URL}/v${rest.apiVersion}/${parts.join("/")}`;
// REMOVE THE MAJOR PARAM // REMOVE THE MAJOR PARAM
parts.shift(); parts.shift();
const [id] = parts; const [id] = parts;
const queue = restCache.pathQueues.get(id); const queue = rest.pathQueues.get(id);
// IF THE QUEUE EXISTS JUST ADD THIS TO THE QUEUE // IF THE QUEUE EXISTS JUST ADD THIS TO THE QUEUE
if (queue) { if (queue) {
queue.push({ request, payload, options }); queue.push({ request, payload });
} else { } else {
// CREATES A NEW QUEUE // CREATES A NEW QUEUE
restCache.pathQueues.set(id, [{ rest.pathQueues.set(id, [
{
request, request,
payload, payload,
options, },
}]); ]);
processQueue(id); rest.processQueue(id);
} }
} }
+1
View File
@@ -9,6 +9,7 @@ import { processRequestHeaders } from "./process_request_headers.ts";
import { runMethod } from "./run_method.ts"; import { runMethod } from "./run_method.ts";
export const rest = { export const rest = {
apiVersion: "8",
/** The secret authorization key to confirm that this was a request made by you and not a DDOS attack. */ /** The secret authorization key to confirm that this was a request made by you and not a DDOS attack. */
authorization: "discordeno_best_lib_ever", authorization: "discordeno_best_lib_ever",
pathQueues: new Map(), pathQueues: new Map(),
+2 -7
View File
@@ -46,8 +46,7 @@ export function runMethod(
} }
// No proxy so we need to handle all rate limiting and such // No proxy so we need to handle all rate limiting and such
// deno-lint-ignore no-async-promise-executor return new Promise((resolve, reject) => {
return new Promise(async (resolve, reject) => {
const callback = async () => { const callback = async () => {
try { try {
const rateLimitResetIn = rest.checkRateLimits(url); const rateLimitResetIn = rest.checkRateLimits(url);
@@ -143,14 +142,10 @@ export function runMethod(
} }
}; };
rest.addToQueue({ rest.processRequest({ url, method, respond: (data: { status: number, body?: string; }) => resolve(JSON.parse(data.body || "{}")) }, {
callback, callback,
bucketId, bucketId,
url, url,
}); });
if (!rest.queueInProcess) {
rest.queueInProcess = true;
await rest.processQueue();
}
}); });
} }
+1 -1
View File
@@ -11,7 +11,7 @@ export async function createShard(shardId: number) {
ws.log("ERROR", { shardId, error: errorEvent }); ws.log("ERROR", { shardId, error: errorEvent });
}; };
socket.onmessage = ({ data: message }) => handleOnMessage(message, shardId); socket.onmessage = ({ data: message }) => ws.handleOnMessage(message, shardId);
socket.onclose = (event) => { socket.onclose = (event) => {
ws.log("CLOSED", { shardId, payload: event }); ws.log("CLOSED", { shardId, payload: event });
+1
View File
@@ -15,6 +15,7 @@ export async function identify(shardId: number, maxShards: number) {
sessionId: "", sessionId: "",
previousSequenceNumber: 0, previousSequenceNumber: 0,
resuming: false, resuming: false,
unavailableGuildIds: new Set(),
heartbeat: { heartbeat: {
lastSentAt: 0, lastSentAt: 0,
lastReceivedAt: 0, lastReceivedAt: 0,
+1
View File
@@ -28,6 +28,7 @@ export async function resume(shardId: number) {
sessionId, sessionId,
previousSequenceNumber, previousSequenceNumber,
resuming: false, resuming: false,
unavailableGuildIds: new Set(),
heartbeat: { heartbeat: {
lastSentAt: 0, lastSentAt: 0,
lastReceivedAt: 0, lastReceivedAt: 0,
+2
View File
@@ -116,6 +116,8 @@ export interface DiscordenoShard {
previousSequenceNumber: number | null; previousSequenceNumber: number | null;
/** Whether the shard is currently resuming. */ /** Whether the shard is currently resuming. */
resuming: boolean; resuming: boolean;
/** The list of guild ids that are currently unavailable due to an outage. */
unavailableGuildIds: Set<string>;
heartbeat: { heartbeat: {
/** The exact timestamp the last heartbeat was sent */ /** The exact timestamp the last heartbeat was sent */
lastSentAt: number; lastSentAt: number;