diff --git a/packages/rest/src/invalidBucket.ts b/packages/rest/src/invalidBucket.ts index 12c65a755..a80b284a1 100644 --- a/packages/rest/src/invalidBucket.ts +++ b/packages/rest/src/invalidBucket.ts @@ -9,19 +9,24 @@ import { delay, logger } from '@discordeno/utils' */ export function createInvalidRequestBucket(options: InvalidRequestBucketOptions): InvalidRequestBucket { const bucket: InvalidRequestBucket = { - current: options.current ?? 0, + invalidRequests: options.current ?? 0, max: options.max ?? 10000, - interval: options.interval ?? 600000, - timeoutId: options.timeoutId, + interval: options.interval ?? 600_000, // 10 minutes + resetAt: options.resetAt, safety: options.safety ?? 1, errorStatuses: options.errorStatuses ?? [401, 403, 429], - requested: options.requested ?? 0, + activeRequests: options.requested ?? 0, processing: false, waiting: [], requestsAllowed: function () { - return bucket.max - bucket.current - bucket.requested - bucket.safety + if (bucket.resetAt !== undefined && Date.now() > bucket.resetAt) { + bucket.invalidRequests = 0 + bucket.resetAt = Date.now() + bucket.interval + } + + return bucket.max - bucket.invalidRequests - bucket.activeRequests - bucket.safety }, isRequestAllowed: function () { @@ -33,7 +38,7 @@ export function createInvalidRequestBucket(options: InvalidRequestBucketOptions) return await new Promise(async (resolve) => { // If whatever amount of requests is left is more than the safety margin, allow the request if (bucket.isRequestAllowed()) { - bucket.requested++ + bucket.activeRequests += 1 resolve() } else { bucket.waiting.push(resolve) @@ -53,13 +58,14 @@ export function createInvalidRequestBucket(options: InvalidRequestBucketOptions) while (bucket.waiting.length > 0) { logger.info(`[InvalidBucket] processing waiting queue while loop ran with ${bucket.waiting.length} remaining.`) - if (bucket.isRequestAllowed()) { - bucket.requested++ - // Resolve the next item in the queue - bucket.waiting.shift()?.() - } else { - await delay(1000) + + if (bucket.resetAt !== undefined && !bucket.isRequestAllowed()) { + await delay(bucket.resetAt - Date.now()) } + + bucket.activeRequests += 1 + // Resolve the next item in the queue + bucket.waiting.shift()?.() } // Mark as false so next pending request can be triggered by new loop. @@ -68,23 +74,18 @@ export function createInvalidRequestBucket(options: InvalidRequestBucketOptions) handleCompletedRequest: function (code, sharedScope) { // Since request is complete, we can remove one from requested. - bucket.requested-- + bucket.activeRequests -= 1 // Since it is as a valid request, we don't need to do anything if (!bucket.errorStatuses.includes(code)) return // Shared scope is not considered invalid if (code === 429 && sharedScope) return // INVALID REQUEST WAS MADE - - // Mark a request has been invalid - bucket.current++ - // If a timeout was not started, start a timeout to reset this bucket - if (bucket.timeoutId === undefined) { - bucket.timeoutId = setTimeout(() => { - bucket.current = 0 - bucket.timeoutId = undefined - }, bucket.interval) + if (bucket.resetAt === undefined) { + bucket.resetAt = Date.now() + bucket.interval } + + bucket.invalidRequests += 1 }, } @@ -98,8 +99,8 @@ export interface InvalidRequestBucketOptions { max?: number /** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */ interval?: number - /** timer to reset to 0 */ - timeoutId?: NodeJS.Timeout + /** When the timeout for the bucket has started at. */ + resetAt?: number /** how safe to be from max. Defaults to 1 */ safety?: number /** The request statuses that count as an invalid request. */ @@ -110,19 +111,19 @@ export interface InvalidRequestBucketOptions { export interface InvalidRequestBucket { /** current invalid amount */ - current: number + invalidRequests: number /** max invalid requests allowed until ban. Defaults to 10,000 */ max: number /** The time that discord allows to make the max number of invalid requests. Defaults to 10 minutes */ interval: number - /** timer to reset to 0 */ - timeoutId: NodeJS.Timeout | undefined + /** When the timeout for this bucket has started at. */ + resetAt: number | undefined /** how safe to be from max. Defaults to 1 */ safety: number /** The request statuses that count as an invalid request. */ errorStatuses: number[] /** The amount of requests that were requested from this bucket. */ - requested: number + activeRequests: number /** The requests that are currently pending. */ waiting: Array<(value: void | PromiseLike) => void> /** Whether or not the waiting queue is already processing. */ diff --git a/packages/rest/tests/e2e/automod.spec.ts b/packages/rest/tests/e2e/automod.spec.ts index b9d285329..4c4686f99 100644 --- a/packages/rest/tests/e2e/automod.spec.ts +++ b/packages/rest/tests/e2e/automod.spec.ts @@ -12,7 +12,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { e2ecache.deletedGuild = true await rest.deleteGuild(e2ecache.guild.id) diff --git a/packages/rest/tests/e2e/emoji.spec.ts b/packages/rest/tests/e2e/emoji.spec.ts index a490a4f67..e81768728 100644 --- a/packages/rest/tests/e2e/emoji.spec.ts +++ b/packages/rest/tests/e2e/emoji.spec.ts @@ -16,7 +16,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { e2ecache.deletedGuild = true await rest.deleteGuild(e2ecache.guild.id) diff --git a/packages/rest/tests/e2e/guild.spec.ts b/packages/rest/tests/e2e/guild.spec.ts index f5f87169d..2280bc588 100644 --- a/packages/rest/tests/e2e/guild.spec.ts +++ b/packages/rest/tests/e2e/guild.spec.ts @@ -14,7 +14,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { e2ecache.deletedGuild = true await rest.deleteGuild(e2ecache.guild.id) diff --git a/packages/rest/tests/e2e/member.spec.ts b/packages/rest/tests/e2e/member.spec.ts index 70b8c6b86..2c9008fb7 100644 --- a/packages/rest/tests/e2e/member.spec.ts +++ b/packages/rest/tests/e2e/member.spec.ts @@ -13,7 +13,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { e2ecache.deletedGuild = true await rest.deleteGuild(e2ecache.guild.id) diff --git a/packages/rest/tests/e2e/message.spec.ts b/packages/rest/tests/e2e/message.spec.ts index 8346d66e8..d3ffe4cff 100644 --- a/packages/rest/tests/e2e/message.spec.ts +++ b/packages/rest/tests/e2e/message.spec.ts @@ -12,7 +12,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { e2ecache.deletedGuild = true await rest.deleteGuild(e2ecache.guild.id) diff --git a/packages/rest/tests/e2e/role.spec.ts b/packages/rest/tests/e2e/role.spec.ts index b5f03326b..d845bfbf5 100644 --- a/packages/rest/tests/e2e/role.spec.ts +++ b/packages/rest/tests/e2e/role.spec.ts @@ -12,7 +12,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { await rest.deleteGuild(e2ecache.guild.id) } diff --git a/packages/rest/tests/e2e/stickers.spec.ts b/packages/rest/tests/e2e/stickers.spec.ts index b0119caa3..d05156c42 100644 --- a/packages/rest/tests/e2e/stickers.spec.ts +++ b/packages/rest/tests/e2e/stickers.spec.ts @@ -14,7 +14,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { e2ecache.deletedGuild = true await rest.deleteGuild(e2ecache.guild.id) diff --git a/packages/rest/tests/e2e/user.spec.ts b/packages/rest/tests/e2e/user.spec.ts index 84f61d6b4..b388ae165 100644 --- a/packages/rest/tests/e2e/user.spec.ts +++ b/packages/rest/tests/e2e/user.spec.ts @@ -13,7 +13,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { await rest.deleteGuild(e2ecache.guild.id) } diff --git a/packages/rest/tests/e2e/webhook.spec.ts b/packages/rest/tests/e2e/webhook.spec.ts index 2a3c3c25c..8c357da83 100644 --- a/packages/rest/tests/e2e/webhook.spec.ts +++ b/packages/rest/tests/e2e/webhook.spec.ts @@ -13,7 +13,6 @@ before(async () => { }) after(async () => { - if (rest.invalidBucket.timeoutId) clearTimeout(rest.invalidBucket.timeoutId) if (e2ecache.guild.id && !e2ecache.deletedGuild) { e2ecache.deletedGuild = true await rest.deleteGuild(e2ecache.guild.id)