diff --git a/rest/processGlobalQueue.ts b/rest/processGlobalQueue.ts index 72f071aed..b0af07614 100644 --- a/rest/processGlobalQueue.ts +++ b/rest/processGlobalQueue.ts @@ -118,18 +118,27 @@ export async function processGlobalQueue(rest: RestManager) { // If NOT rate limited remove from queue if (response.status !== 429) { + let json = undefined; if (response.type) { - console.log(JSON.stringify(await response.json())); + json = JSON.stringify(await response.json()); + console.log(json); } - request.request.reject(new Error(`[${response.status}] ${error}`)); + request.request.reject({ + ok: false, + status: response.status, + error, + body: json, + }); } else { if (request.payload.retryCount++ >= rest.maxRetryCount) { rest.debug(`[REST - RetriesMaxed] ${JSON.stringify(request.payload)}`); // REMOVE ITEM FROM QUEUE TO PREVENT RETRY - request.request.reject( - new Error(`[${response.status}] The request was rate limited and it maxed out the retries limit.`), - ); + request.request.reject({ + ok: false, + status: response.status, + error: "The request was rate limited and it maxed out the retries limit.", + }); continue; } @@ -143,21 +152,29 @@ export async function processGlobalQueue(rest: RestManager) { // SOMETIMES DISCORD RETURNS AN EMPTY 204 RESPONSE THAT CAN'T BE MADE TO JSON if (response.status === 204) { rest.debug(`[REST - FetchSuccess] URL: ${request.urlToUse} | ${JSON.stringify(request.payload)}`); - request.request.respond({ status: 204 }); + request.request.respond({ + ok: true, + status: 204, + }); } else { // CONVERT THE RESPONSE TO JSON - const json = await response.json(); + const json = JSON.stringify(await response.json()); rest.debug(`[REST - fetchSuccess] ${JSON.stringify(request.payload)}`); request.request.respond({ + ok: true, status: 200, - body: JSON.stringify(json), + body: json, }); } } catch (error) { // SOMETHING WENT WRONG, LOG AND RESPOND WITH ERROR rest.debug(`[REST - fetchFailed] Payload: ${JSON.stringify(request.payload)} | Error: ${error}`); - request.request.reject(error); + request.request.reject({ + ok: false, + status: 599, + error: "Internal Proxy Error", + }); } } diff --git a/rest/rest.ts b/rest/rest.ts index 78ecd8f7f..184de4c89 100644 --- a/rest/rest.ts +++ b/rest/rest.ts @@ -7,6 +7,7 @@ import { processRateLimitedPaths } from "./processRateLimitedPaths.ts"; import { processRequest } from "./processRequest.ts"; import { processRequestHeaders } from "./processRequestHeaders.ts"; import { runMethod } from "./runMethod.ts"; +import { runProxyMethod } from "./runProxyMethod.ts"; import { simplifyUrl } from "./simplifyUrl.ts"; export const rest = { @@ -49,14 +50,25 @@ export const rest = { processRequest, createRequestBody, runMethod, + runProxyMethod, simplifyUrl, }; export interface RestRequest { url: string; method: string; - respond: (payload: { status: number; body?: string }) => unknown; - reject: (error: unknown) => unknown; + respond: (payload: RestRequestResponse) => unknown; + reject: (payload: RestRequestRejection) => unknown; +} + +export interface RestRequestResponse { + ok: boolean; + status: number; + body?: string; +} + +export interface RestRequestRejection extends RestRequestResponse { + error: string; } export interface RestPayload { diff --git a/rest/runMethod.ts b/rest/runMethod.ts index 559448be8..b072bfb45 100644 --- a/rest/runMethod.ts +++ b/rest/runMethod.ts @@ -1,5 +1,6 @@ import { RestManager } from "../bot.ts"; import { API_VERSION, BASE_URL, IMAGE_BASE_URL } from "../util/constants.ts"; +import { RestRequestRejection, RestRequestResponse } from "./rest.ts"; export async function runMethod( rest: RestManager, @@ -63,11 +64,11 @@ export async function runMethod( { url, method, - reject: (error: unknown) => { - errorStack.message = (error as Error)?.message; + reject: (data: RestRequestRejection) => { + errorStack.message = `[${data.status}] ${data.error}`; reject(errorStack); }, - respond: (data: { status: number; body?: string }) => + respond: (data: RestRequestResponse) => resolve(data.status !== 204 ? JSON.parse(data.body ?? "{}") : (undefined as unknown as T)), }, { diff --git a/rest/runProxyMethod.ts b/rest/runProxyMethod.ts new file mode 100644 index 000000000..f05c03b79 --- /dev/null +++ b/rest/runProxyMethod.ts @@ -0,0 +1,47 @@ +import { RestManager } from "../bot.ts"; +import { RestRequestRejection, RestRequestResponse } from "./rest.ts"; + +export type ProxyMethodResponse = Omit & { body?: T }; + +// Left out proxy request, because it's not needed here +// this file could also be moved to a plugin. +export async function runProxyMethod( + rest: RestManager, + method: "get" | "post" | "put" | "delete" | "patch", + url: string, + body?: unknown, + retryCount = 0, + bucketId?: string, +): Promise> { + rest.debug( + `[REST - RequestCreate] Method: ${method} | URL: ${url} | Retry Count: ${retryCount} | Bucket ID: ${bucketId} | Body: ${ + JSON.stringify( + body, + ) + }`, + ); + + // No proxy so we need to handle all rate limiting and such + return new Promise((resolve, reject) => { + rest.processRequest( + rest, + { + url, + method, + reject: (data: RestRequestRejection) => { + const { body: b, ...r } = data; + reject({ body: data.status !== 204 ? JSON.parse(b ?? "{}") : (undefined as unknown as T), ...r }); + }, + respond: (data: RestRequestResponse) => { + const { body: b, ...r } = data; + resolve({ body: data.status !== 204 ? JSON.parse(b ?? "{}") : (undefined as unknown as T), ...r }); + }, + }, + { + bucketId, + body: body as Record | undefined, + retryCount, + }, + ); + }); +} diff --git a/template/bigbot/src/rest/mod.ts b/template/bigbot/src/rest/mod.ts index 425153a30..08f9d8456 100644 --- a/template/bigbot/src/rest/mod.ts +++ b/template/bigbot/src/rest/mod.ts @@ -42,7 +42,7 @@ async function handleRequest(conn: Deno.Conn) { ); } - const json = (await requestEvent.request.json()); + const json = requestEvent.request.body ? (await requestEvent.request.json()) : undefined; try { const result = await rest.runMethod( diff --git a/types/discordeno.ts b/types/discordeno.ts index f61432c47..3c88f6e92 100644 --- a/types/discordeno.ts +++ b/types/discordeno.ts @@ -6,7 +6,7 @@ export type MessageComponents = ActionRow[]; /** https://discord.com/developers/docs/interactions/message-components#actionrow */ export interface ActionRow { /** Action rows are a group of buttons. */ - type: 1; + type: MessageComponentTypes.ActionRow; /** The components in this row */ components: | [SelectMenuComponent | ButtonComponent | InputTextComponent] @@ -16,21 +16,7 @@ export interface ActionRow { | [ButtonComponent, ButtonComponent, ButtonComponent, ButtonComponent, ButtonComponent]; } -export interface SelectMenuComponent { - type: MessageComponentTypes.SelectMenu; - /** A custom identifier for this component. Maximum 100 characters. */ - customId: string; - /** A custom placeholder text if nothing is selected. Maximum 150 characters. */ - placeholder?: string; - /** The minimum number of items that must be selected. Default 1. Between 1-25. */ - minValues?: number; - /** The maximum number of items that can be selected. Default 1. Between 1-25. */ - maxValues?: number; - /** The choices! Maximum of 25 items. */ - options: SelectOption[]; -} - -/** https://discord.com/developers/docs/interactions/message-components#buttons-button-object */ +/** https://discord.com/developers/docs/interactions/message-components#button-object-button-structure */ export interface ButtonComponent { /** All button components have type 2 */ type: MessageComponentTypes.Button; @@ -55,9 +41,25 @@ export interface ButtonComponent { disabled?: boolean; } +/** https://discord.com/developers/docs/interactions/message-components#select-menu-object-select-menu-structure */ +export interface SelectMenuComponent { + /** SelectMenu Component is of type 3 */ + type: MessageComponentTypes.SelectMenu; + /** A custom identifier for this component. Maximum 100 characters. */ + customId: string; + /** A custom placeholder text if nothing is selected. Maximum 150 characters. */ + placeholder?: string; + /** The minimum number of items that must be selected. Default 1. Between 1-25. */ + minValues?: number; + /** The maximum number of items that can be selected. Default 1. Between 1-25. */ + maxValues?: number; + /** The choices! Maximum of 25 items. */ + options: SelectOption[]; +} + /** https://discord.com/developers/docs/interactions/message-components#text-inputs-text-input-structure */ export interface InputTextComponent { - /** InputText Component is of type 3 */ + /** InputText Component is of type 4 */ type: MessageComponentTypes.InputText; /** The style of the InputText */ style: TextStyles; diff --git a/util/constants.ts b/util/constants.ts index 6afc699e6..1d6c5660b 100644 --- a/util/constants.ts +++ b/util/constants.ts @@ -9,7 +9,7 @@ export const GATEWAY_VERSION = 10; // TODO: update this version /** https://github.com/discordeno/discordeno/releases */ -export const DISCORDENO_VERSION = "13.0.0-rc34"; +export const DISCORDENO_VERSION = "13.0.0-rc35"; /** https://discord.com/developers/docs/reference#user-agent */ export const USER_AGENT = `DiscordBot (https://github.com/discordeno/discordeno, v${DISCORDENO_VERSION})`;