mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-16 11:28:15 +00:00
fix: rest errors preventing startup
This commit is contained in:
+9
-24
@@ -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),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user