mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-06 13:19:31 -05:00
56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
import { logger } from "~/utils/logger";
|
|
import { getToken, purgeCachedToken } from "./token";
|
|
import { getTwitchEnvVars } from "./utils";
|
|
|
|
const MAX_RATE_LIMIT_RETRIES = 5;
|
|
const MAX_RATE_LIMIT_WAIT_MS = 60_000;
|
|
|
|
export async function twitchFetch(
|
|
url: string,
|
|
{ isRetry = false, rateLimitRetries = 0 } = {},
|
|
): Promise<Response> {
|
|
const { TWITCH_CLIENT_ID } = getTwitchEnvVars();
|
|
const token = await getToken();
|
|
|
|
const res = await fetch(url, {
|
|
headers: [
|
|
["Authorization", `Bearer ${token}`],
|
|
["Client-Id", TWITCH_CLIENT_ID],
|
|
],
|
|
});
|
|
|
|
if (res.status === 401 && !isRetry) {
|
|
purgeCachedToken();
|
|
return twitchFetch(url, { isRetry: true });
|
|
}
|
|
|
|
if (res.status === 429) {
|
|
if (rateLimitRetries >= MAX_RATE_LIMIT_RETRIES) {
|
|
throw new Error(
|
|
`Twitch API rate limited after ${MAX_RATE_LIMIT_RETRIES} retries`,
|
|
);
|
|
}
|
|
|
|
const resetHeader = res.headers.get("Ratelimit-Reset");
|
|
const resetTimestamp = resetHeader ? Number(resetHeader) : 0;
|
|
const waitMs = Math.max(resetTimestamp * 1000 - Date.now(), 1000);
|
|
|
|
if (waitMs > MAX_RATE_LIMIT_WAIT_MS) {
|
|
throw new Error(
|
|
`Twitch API rate limit reset too far in the future (${Math.ceil(waitMs / 1000)}s)`,
|
|
);
|
|
}
|
|
|
|
logger.warn(`Twitch rate limited, waiting ${Math.ceil(waitMs / 1000)}s`);
|
|
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
|
|
return twitchFetch(url, { rateLimitRetries: rateLimitRetries + 1 });
|
|
}
|
|
|
|
if (!res.ok) {
|
|
throw new Error(`Twitch API request failed with status: ${res.status}`);
|
|
}
|
|
|
|
return res;
|
|
}
|