I'm working on a library to interact with the discord API - and hence I need a ratelimiting system.
I of course need to be able to make REST requests, which I've seperated into a standalone part of my app, for example I might run:
await this.client.rest.get('hello/hello') (Naming unrelated)
I then make a request to my redis cache to fetch the current rate limit, and should we currently be passed it, I place the request in an array (my queue), until the ratelimit resets.
The issue I am having is working out how to return a value to the above function, even if it's been placed in a queue etc? I've attached my code below.
import axios, { AxiosRequestHeaders, AxiosResponse } from "axios";
import { InternalRequest, REST } from ".";
import { BucketHandler } from "./BucketHandler.js";
export class RequestManager {
REST: REST;
ratelimit: any;
handlers: BucketHandler[];
constructor(REST: REST) {
this.REST = REST;
this.ratelimit = {
queue: [],
timer: null,
time: null,
};
this.handlers = [];
}
private async manageQueue() {
if (this.ratelimit.queue.length < 1) return;
if (this.ratelimit.timer) return;
this.ratelimit.time = await this.REST.redisClient.ttl("ratelimit:global");
this.ratelimit.timer = setTimeout(() => {
this.ratelimit.timer = null;
this.processQueue();
}, this.ratelimit.time * 1500);
}
private async processQueue() {
const globalRateLimit = Number(await this.REST.redisClient.incr("ratelimit:global"));
this.request(this.ratelimit.queue.shift());
if (globalRateLimit < 2 && this.ratelimit.queue.length > 0) this.processQueue();
else this.manageQueue();
}
private async parseResponse(res: AxiosResponse) {
return res.data;
}
private formatRequest(options: InternalRequest) {
const url: string = `${this.REST.api}/v${this.REST.version}${options.fullRoute}`;
const headers: AxiosRequestHeaders = {};
if (options.requestOptions.authorization) headers.Authorization = `${options.requestOptions.authorizationPrefix} ${this.REST.token}`;
return { url, headers };
}
public async raw(options: InternalRequest) {
const globalRateLimit = Number(await this.REST.redisClient.incr("ratelimit:global"));
if (globalRateLimit > 2) {
await this.REST.redisClient.decr("ratelimit:global");
const queue = this.ratelimit.queue.push(options);
this.manageQueue();
return queue
} else {
return this.request(options);
}
}
private async request(options: InternalRequest) {
console.log("REQUEST RUN!");
const request = this.formatRequest(options);
const res = await axios(request.url, {
method: options.requestMethod,
headers: request.headers,
}).catch((err) => {
console.log("ERROR!");
});
return res;
}
}