fix(slowmode): stop the local cooldown from outgrowing the channel setting (#1795)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-20 19:56:02 +02:00
1 parent 8f4f9a8601
commit 631bc2307a
15 files changed
+497 -147

No files matched your search

@@ -1,25 +1,15 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
import {
sanitizeRetryAfterDecimalSeconds,
sanitizeRetryAfterSeconds,
} from '@fluxer/errors/src/domains/core/RetryAfterSeconds';
import {ThrottledError} from '@fluxer/errors/src/domains/core/ThrottledError';
import type {FluxerErrorData} from '@fluxer/errors/src/FluxerError';
type RateLimitScope = 'global' | 'shared' | 'user';
function sanitizeRetryAfter(value: number | undefined | null): number {
if (value == null || !Number.isFinite(value) || value < 0) {
return 1;
}
return Math.max(1, Math.ceil(value));
}
function sanitizeRetryAfterDecimal(value: number | undefined | null, fallback: number): number {
if (value == null || !Number.isFinite(value) || value < 0) {
return fallback;
}
return Math.max(0.001, value);
}
function sanitizeResetTime(resetTime: Date): number {
const timestamp = resetTime.getTime();
if (!Number.isFinite(timestamp)) {
@@ -64,8 +54,8 @@ export class RateLimitError extends ThrottledError {
bucketHash?: string;
scope?: RateLimitScope;
}) {
const safeRetryAfter = sanitizeRetryAfter(retryAfter);
const safeRetryAfterDecimal = sanitizeRetryAfterDecimal(retryAfterDecimal, safeRetryAfter);
const safeRetryAfter = sanitizeRetryAfterSeconds(retryAfter);
const safeRetryAfterDecimal = sanitizeRetryAfterDecimalSeconds(retryAfterDecimal, safeRetryAfter);
const safeResetTimestamp = sanitizeResetTime(resetTime);
const safeLimit = Number.isFinite(limit) && limit > 0 ? limit : 1;
const safeResetAfterDecimal =
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
export function sanitizeRetryAfterSeconds(value: number | undefined | null): number {
if (value == null || !Number.isFinite(value) || value < 0) {
return 1;
}
return Math.max(1, Math.ceil(value));
}
export function sanitizeRetryAfterDecimalSeconds(value: number | undefined | null, fallback: number): number {
if (value == null || !Number.isFinite(value) || value < 0) {
return fallback;
}
return Math.max(0.001, value);
}
@@ -2,22 +2,28 @@
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
import {BadRequestError} from '@fluxer/errors/src/domains/core/BadRequestError';
import {
sanitizeRetryAfterDecimalSeconds,
sanitizeRetryAfterSeconds,
} from '@fluxer/errors/src/domains/core/RetryAfterSeconds';
export class SlowmodeRateLimitError extends BadRequestError {
constructor({
retryAfter,
retryAfterDecimal,
}: {
retryAfter: number;
retryAfter: number | undefined;
retryAfterDecimal?: number;
}) {
const safeRetryAfter = sanitizeRetryAfterSeconds(retryAfter);
const safeRetryAfterDecimal = sanitizeRetryAfterDecimalSeconds(retryAfterDecimal, safeRetryAfter);
super({
code: APIErrorCodes.SLOWMODE_RATE_LIMITED,
data: {
retry_after: retryAfterDecimal ?? retryAfter,
retry_after: safeRetryAfterDecimal,
},
headers: {
'Retry-After': retryAfter.toString(),
'Retry-After': safeRetryAfter.toString(),
},
});
}