mirror of
https://github.com/mollersuite/monofile.git
synced 2024-11-25 07:06:25 -08:00
mihari.oyama.pictures/Tired
This commit is contained in:
parent
70cad2d753
commit
16db5c3196
|
@ -1,23 +1,47 @@
|
||||||
const base = "https://discord.com/api/v10/"
|
const base = "https://discord.com/api/v10"
|
||||||
const buckets = new Map<string, DiscordAPIBucket>()
|
const buckets = new Map<string, DiscordAPIBucket>()
|
||||||
|
const routeConnections = new Map<string, DiscordAPIBucket>()
|
||||||
|
|
||||||
|
interface RatelimitData {
|
||||||
|
bucket_name : string
|
||||||
|
limit : number
|
||||||
|
remaining : number
|
||||||
|
expires : number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface QueuedRequest {
|
||||||
|
path : `/${string}`
|
||||||
|
params : RequestInit
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractRatelimitData(headers: Headers): RatelimitData {
|
||||||
|
return {
|
||||||
|
bucket_name : headers.get("x-ratelimit-bucket")!,
|
||||||
|
limit : parseInt(headers.get("x-ratelimit-limit")!),
|
||||||
|
remaining : parseInt(headers.get("x-ratelimit-remaining")!),
|
||||||
|
expires : parseFloat(headers.get("x-ratelimit-reset")!),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DiscordAPIBucket {
|
class DiscordAPIBucket {
|
||||||
|
|
||||||
readonly name : string // bucket name (X-Ratelimit-Bucket)
|
readonly name : string // bucket name (X-Ratelimit-Bucket)
|
||||||
// queue : RequestInfo[] = [] // queue of requests to send
|
|
||||||
readonly limit : number // bucket limit (X-Ratelimit-Limit)
|
readonly limit : number // bucket limit (X-Ratelimit-Limit)
|
||||||
remaining : number // requests remaining (X-Ratelimit-Remaining)
|
remaining : number // requests remaining (X-Ratelimit-Remaining)
|
||||||
readonly expires : number // when this ratelimit expires (X-Ratelimit-Reset)
|
readonly expires : number // when this ratelimit expires (X-Ratelimit-Reset)
|
||||||
|
|
||||||
readonly expirationHold : ReturnType<typeof setTimeout> // Timeout which fires after this bucket expires
|
readonly expirationHold : ReturnType<typeof setTimeout> // Timeout which fires after this bucket expires
|
||||||
dead : boolean = false // True if bucket has expired
|
dead : boolean = false // True if bucket has expired
|
||||||
|
linked_routes : string[] = []
|
||||||
|
|
||||||
constructor(base: Response) {
|
constructor(base: Response) {
|
||||||
|
|
||||||
this.name = base.headers.get("x-ratelimit-bucket")!
|
let rd = extractRatelimitData(base.headers)
|
||||||
this.limit = parseInt(base.headers.get("x-ratelimit-limit")!)
|
|
||||||
this.remaining = parseInt(base.headers.get("x-ratelimit-remaining")!)
|
this.name = rd.bucket_name
|
||||||
this.expires = parseFloat(base.headers.get("x-ratelimit-reset")!)
|
this.limit = rd.limit
|
||||||
|
this.remaining = rd.remaining
|
||||||
|
this.expires = rd.expires
|
||||||
|
|
||||||
this.expirationHold =
|
this.expirationHold =
|
||||||
setTimeout(
|
setTimeout(
|
||||||
|
@ -34,6 +58,7 @@ class DiscordAPIBucket {
|
||||||
|
|
||||||
buckets.delete(this.name)
|
buckets.delete(this.name)
|
||||||
this.dead = true
|
this.dead = true
|
||||||
|
this.linked_routes.forEach((v) => routeConnections.delete(v))
|
||||||
Object.freeze(this)
|
Object.freeze(this)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,6 +72,16 @@ class DiscordAPIBucket {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Link a route to this bucket
|
||||||
|
* @param route Route to link
|
||||||
|
*/
|
||||||
|
link(route: string) {
|
||||||
|
if (this.linked_routes.includes(route)) return
|
||||||
|
routeConnections.set(route, this)
|
||||||
|
this.linked_routes.push(route)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,14 +100,18 @@ function checkHeaders(headers: Headers) {
|
||||||
/**
|
/**
|
||||||
* @description Returns or creates a DiscordAPIBucket from a Response
|
* @description Returns or creates a DiscordAPIBucket from a Response
|
||||||
*/
|
*/
|
||||||
function getBucket(response: Response) {
|
function getBucket(response: string): DiscordAPIBucket | undefined
|
||||||
if (!checkHeaders(response.headers)) throw new Error("Required ratelimiting headers not found")
|
function getBucket(response: Response): DiscordAPIBucket
|
||||||
|
function getBucket(response: Response | string) {
|
||||||
|
if (response instanceof Response) {
|
||||||
|
if (!checkHeaders(response.headers)) throw new Error("Required ratelimiting headers not found")
|
||||||
|
|
||||||
if (buckets.has(response.headers.get("x-ratelimit-bucket")!))
|
if (buckets.has(response.headers.get("x-ratelimit-bucket")!))
|
||||||
return buckets.get(response.headers.get("x-ratelimit-bucket")!)!
|
return buckets.get(response.headers.get("x-ratelimit-bucket")!)!
|
||||||
|
|
||||||
else
|
else
|
||||||
return new DiscordAPIBucket(response)
|
return new DiscordAPIBucket(response)
|
||||||
|
} else return routeConnections.get(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
export class REST {
|
export class REST {
|
||||||
|
@ -83,10 +122,47 @@ export class REST {
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetch(options: RequestInfo) {
|
/**
|
||||||
|
* @description Queues a request
|
||||||
|
*/
|
||||||
|
async queue(path: `/${string}`, options?: RequestInit) {
|
||||||
|
// TODO: actually write the queue lmao
|
||||||
|
console.warn(`Request added to queue: ${(options?.method ?? "get").toUpperCase()} ${path}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Make a fetch requests where further requests are automatically queued in case of ratelimit
|
||||||
|
*/
|
||||||
|
async fetch(path: `/${string}`, options?: RequestInit) {
|
||||||
|
|
||||||
|
// check if there's already a bucket, and check if it's full
|
||||||
|
let known_bucket = getBucket( path )
|
||||||
|
|
||||||
|
if (known_bucket && known_bucket.remaining) {
|
||||||
|
return this.queue(path, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
// there's no known bucket for this route; let's carry on with the request
|
||||||
|
let response = await fetch(base+path, options)
|
||||||
|
|
||||||
|
if ( checkHeaders(response.headers) ) {
|
||||||
|
// a ratelimit is attached, let's set up our buckets..
|
||||||
|
|
||||||
|
let bucket = getBucket( response )
|
||||||
|
if (response.status == 429) {
|
||||||
|
bucket.link(path) // link the bucket so that hopefully no future errors occur
|
||||||
|
|
||||||
|
return this.queue(path, options) /* it was ratelimited after all
|
||||||
|
getBucket() would have generated a DiscordAPIBucket
|
||||||
|
so this would be fine */
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's update the bucket...
|
||||||
|
let rd = extractRatelimitData( response.headers )
|
||||||
|
bucket.update(rd.remaining)
|
||||||
|
|
||||||
|
} else return response
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue