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
+10 -25
View File
@@ -2,6 +2,7 @@ import { getGatewayBot } from "./helpers/misc/get_gateway_bot.ts";
import { DiscordGatewayIntents } from "./types/gateway/gateway_intents.ts";
import { DiscordGetGatewayBot } from "./types/gateway/get_gateway_bot.ts";
import { baseEndpoints, GATEWAY_VERSION } from "./util/constants.ts";
import { ws } from "./ws/ws.ts";
export let authorization = "";
export let secretKey = "";
@@ -39,18 +40,14 @@ export async function startBot(config: BotConfig) {
proxyWSURL = botGatewayData.url;
identifyPayload.token = config.token;
identifyPayload.intents = config.intents.reduce(
(
bits,
next,
) => (bits |= typeof next === "string"
? DiscordGatewayIntents[next]
: next),
0,
(bits, next) =>
(bits |= typeof next === "string" ? DiscordGatewayIntents[next] : next),
0
);
lastShardId = botGatewayData.shards;
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 */
@@ -78,7 +75,7 @@ export function setApplicationId(id: string) {
* Please be aware if you are a beginner developer using this, things will not work as per the guides. This is for advanced developers only!
*
* Advanced Devs: This function will allow you to have an insane amount of customization potential as when you get to large bots you need to be able to optimize every tiny detail to make you bot work the way you need.
*/
*/
export async function startBigBrainBot(data: BigBrainBotConfig) {
authorization = `Bot ${data.token}`;
identifyPayload.token = `Bot ${data.token}`;
@@ -92,13 +89,9 @@ export async function startBigBrainBot(data: BigBrainBotConfig) {
}
identifyPayload.intents = data.intents.reduce(
(
bits,
next,
) => (bits |= typeof next === "string"
? DiscordGatewayIntents[next]
: next),
0,
(bits, next) =>
(bits |= typeof next === "string" ? DiscordGatewayIntents[next] : next),
0
);
// 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
botGatewayData = await getGatewayBot();
proxyWSURL = botGatewayData.url;
await spawnShards(
botGatewayData,
identifyPayload,
data.firstShardId,
data.lastShardId ||
(botGatewayData.shards >= 25
? (data.firstShardId + 25)
: botGatewayData.shards),
);
ws.spawnShards(data.firstShardId);
}
}
+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. */
export function createRequestBody(queuedRequest: QueuedRequest) {
const headers: { [key: string]: string } = {
@@ -6,7 +8,7 @@ export function createRequestBody(queuedRequest: QueuedRequest) {
};
// GET METHODS SHOULD NOT HAVE A BODY
if (queuedRequest.request.method === "GET") {
if (queuedRequest.request.method.toUpperCase() === "GET") {
queuedRequest.payload.body = undefined;
}
+45 -36
View File
@@ -1,4 +1,5 @@
import { DiscordHTTPResponseCodes } from "../types/codes/http_response_codes.ts";
import { delay } from "../util/utils.ts";
import { rest } from "./rest.ts";
/** Processes the queue by looping over each path separately until the queues are empty. */
@@ -36,15 +37,21 @@ export async function processQueue(id: string) {
// IF THIS IS A GET REQUEST, CHANGE THE BODY TO QUERY PARAMETERS
const query =
queuedRequest.request.method === "GET" && queuedRequest.payload.body
? Object.entries(queuedRequest.payload.body).map(([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(value as string)}`
)
.join("&")
queuedRequest.request.method.toUpperCase() === "GET" &&
queuedRequest.payload.body
? Object.entries(queuedRequest.payload.body)
.map(
([key, value]) =>
`${encodeURIComponent(key)}=${encodeURIComponent(
value as string
)}`
)
.join("&")
: "";
const urlToUse = queuedRequest.request.method === "GET" && query
? `${queuedRequest.request.url}?${query}`
: queuedRequest.request.url;
const urlToUse =
queuedRequest.request.method.toUpperCase() === "GET" && query
? `${queuedRequest.request.url}?${query}`
: queuedRequest.request.url;
// CUSTOM HANDLER FOR USER TO LOG OR WHATEVER WHENEVER A FETCH IS MADE
rest.eventHandlers.fetching(queuedRequest.payload);
@@ -52,45 +59,49 @@ export async function processQueue(id: string) {
try {
const response = await fetch(
urlToUse,
rest.createRequestBody(queuedRequest),
rest.createRequestBody(queuedRequest)
);
rest.eventHandlers.fetched(queuedRequest.payload);
const bucketIdFromHeaders = rest.processRequestHeaders(
queuedRequest.request.url,
response.headers,
response.headers
);
if (response.status < 200 || response.status >= 400) {
rest.eventHandlers.error(
"httpError",
queuedRequest.payload,
response,
);
rest.eventHandlers.error("httpError", queuedRequest.payload, response);
let error = "REQUEST_UNKNOWN_ERROR";
switch (response.status) {
case DiscordHTTPResponseCodes.BadRequest:
error =
"The request was improperly formatted, or the server couldn't understand it.";
break;
case DiscordHTTPResponseCodes.Unauthorized:
error = "The Authorization header was missing or invalid.";
break;
case DiscordHTTPResponseCodes.Forbidden:
error =
"The Authorization token you passed did not have permission to the resource.";
break;
case DiscordHTTPResponseCodes.NotFound:
error = "The resource at the location specified doesn't exist.";
break;
case DiscordHTTPResponseCodes.MethodNotAllowed:
error =
"The HTTP method used is not valid for the location specified.";
break;
case DiscordHTTPResponseCodes.GatewayUnavailable:
error =
"There was not a gateway available to process your request. Wait a bit and retry.";
break;
}
queuedRequest.request.respond(
{ status: response.status, body: JSON.stringify({ error }) },
);
queuedRequest.request.respond({
status: response.status,
body: JSON.stringify({ error }),
});
queue.shift();
continue;
}
@@ -112,20 +123,16 @@ export async function processQueue(id: string) {
// IF IT HAS MAXED RETRIES SOMETHING SERIOUSLY WRONG. CANCEL OUT.
if (
queuedRequest.payload.retryCount >=
queuedRequest.options.maxRetryCount
queuedRequest.options.maxRetryCount
) {
rest.eventHandlers.retriesMaxed(queuedRequest.payload);
queuedRequest.request.respond(
{
status: 200,
body: JSON.stringify(
{
error:
"The request was rate limited and it maxed out the retries limit.",
},
),
},
);
queuedRequest.request.respond({
status: 200,
body: JSON.stringify({
error:
"The request was rate limited and it maxed out the retries limit.",
}),
});
// REMOVE ITEM FROM QUEUE TO PREVENT RETRY
queue.shift();
continue;
@@ -142,16 +149,18 @@ export async function processQueue(id: string) {
rest.eventHandlers.fetchSuccess(queuedRequest.payload);
// REMOVE FROM QUEUE
queue.shift();
queuedRequest.request.respond(
{ status: 200, body: JSON.stringify(json) },
);
queuedRequest.request.respond({
status: 200,
body: JSON.stringify(json),
});
}
} catch (error) {
// SOMETHING WENT WRONG, LOG AND RESPOND WITH ERROR
rest.eventHandlers.fetchFailed(queuedRequest.payload, error);
queuedRequest.request.respond(
{ status: 404, body: JSON.stringify({ error }) },
);
queuedRequest.request.respond({
status: 404,
body: JSON.stringify({ error }),
});
// REMOVE FROM QUEUE
queue.shift();
}
+14 -11
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. */
export function processRequest(
request: ServerRequest,
payload: RunMethodOptions,
options: RestServerOptions,
payload: RunMethodOptions
) {
const route = request.url.substring(request.url.indexOf("api/"));
const parts = route.split("/");
@@ -11,23 +13,24 @@ export function processRequest(
// REMOVES THE VERSION NUMBER
if (parts[0]?.startsWith("v")) parts.shift();
// 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
parts.shift();
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 (queue) {
queue.push({ request, payload, options });
queue.push({ request, payload });
} else {
// CREATES A NEW QUEUE
restCache.pathQueues.set(id, [{
request,
payload,
options,
}]);
processQueue(id);
rest.pathQueues.set(id, [
{
request,
payload,
},
]);
rest.processQueue(id);
}
}
+1
View File
@@ -9,6 +9,7 @@ import { processRequestHeaders } from "./process_request_headers.ts";
import { runMethod } from "./run_method.ts";
export const rest = {
apiVersion: "8",
/** The secret authorization key to confirm that this was a request made by you and not a DDOS attack. */
authorization: "discordeno_best_lib_ever",
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
// deno-lint-ignore no-async-promise-executor
return new Promise(async (resolve, reject) => {
return new Promise((resolve, reject) => {
const callback = async () => {
try {
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,
bucketId,
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 });
};
socket.onmessage = ({ data: message }) => handleOnMessage(message, shardId);
socket.onmessage = ({ data: message }) => ws.handleOnMessage(message, shardId);
socket.onclose = (event) => {
ws.log("CLOSED", { shardId, payload: event });
+1
View File
@@ -15,6 +15,7 @@ export async function identify(shardId: number, maxShards: number) {
sessionId: "",
previousSequenceNumber: 0,
resuming: false,
unavailableGuildIds: new Set(),
heartbeat: {
lastSentAt: 0,
lastReceivedAt: 0,
+1
View File
@@ -28,6 +28,7 @@ export async function resume(shardId: number) {
sessionId,
previousSequenceNumber,
resuming: false,
unavailableGuildIds: new Set(),
heartbeat: {
lastSentAt: 0,
lastReceivedAt: 0,
+2
View File
@@ -116,6 +116,8 @@ export interface DiscordenoShard {
previousSequenceNumber: number | null;
/** Whether the shard is currently resuming. */
resuming: boolean;
/** The list of guild ids that are currently unavailable due to an outage. */
unavailableGuildIds: Set<string>;
heartbeat: {
/** The exact timestamp the last heartbeat was sent */
lastSentAt: number;