Add a second user token (thanks Claude)
This commit is contained in:
parent
b3418d1460
commit
062b976569
|
|
@ -10,3 +10,8 @@ DISCORD_BOT_TOKEN=
|
||||||
# WARNING: using a user token is self-botting and against Discord's ToS;
|
# WARNING: using a user token is self-botting and against Discord's ToS;
|
||||||
# it can get the account banned. Leave blank to run bot-token-only.
|
# it can get the account banned. Leave blank to run bot-token-only.
|
||||||
DISCORD_USER_TOKEN=
|
DISCORD_USER_TOKEN=
|
||||||
|
|
||||||
|
# OPTIONAL second user token. If set, rich profile fetches spread across both
|
||||||
|
# tokens and fail over on a 429, doubling the /profile rate-limit headroom.
|
||||||
|
# Same ToS warning applies — more accounts means more accounts at risk.
|
||||||
|
DISCORD_USER_TOKEN2=
|
||||||
|
|
|
||||||
|
|
@ -78,26 +78,47 @@ export interface UserProfileFetch {
|
||||||
retryAfter: number;
|
retryAfter: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Configured user tokens (1 or 2), in order, skipping blanks. */
|
||||||
|
function userTokens(env: Env): string[] {
|
||||||
|
return [env.DISCORD_USER_TOKEN, env.DISCORD_USER_TOKEN2].filter(
|
||||||
|
(t): t is string => !!t && t.trim().length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rich profile via USER token (self-bot — ToS risk). Reports the HTTP status so
|
* Rich profile via USER token(s) (self-bot — ToS risk). If two tokens are
|
||||||
* callers can tell a 429 rate-limit (back off) apart from a 401/403 token issue,
|
* configured, load is spread across them (random start) and a 429 on one fails
|
||||||
* rather than silently degrading to the bot token.
|
* over to the other — doubling the /profile rate-limit headroom. Reports the
|
||||||
|
* HTTP status so callers can tell a 429 (back off) from a 401/403 token issue.
|
||||||
*/
|
*/
|
||||||
export async function fetchUserProfile(env: Env, id: string): Promise<UserProfileFetch> {
|
export async function fetchUserProfile(env: Env, id: string): Promise<UserProfileFetch> {
|
||||||
if (!env.DISCORD_USER_TOKEN) return { data: null, status: 0, retryAfter: 0 };
|
const tokens = userTokens(env);
|
||||||
|
if (tokens.length === 0) return { data: null, status: 0, retryAfter: 0 };
|
||||||
|
|
||||||
const url =
|
const url =
|
||||||
`${apiBase(env)}/users/${id}/profile` +
|
`${apiBase(env)}/users/${id}/profile` +
|
||||||
`?with_mutual_guilds=false&with_mutual_friends=false`;
|
`?with_mutual_guilds=false&with_mutual_friends=false`;
|
||||||
const res = await fetch(url, {
|
|
||||||
headers: { Authorization: env.DISCORD_USER_TOKEN },
|
// Spread load: start on a random token, then rotate to the next on a 429.
|
||||||
});
|
const start = Math.floor(Math.random() * tokens.length);
|
||||||
if (!res.ok) {
|
let lastStatus = 0;
|
||||||
const retryAfter = Number(res.headers.get("retry-after")) || 0;
|
let lastRetryAfter = 0;
|
||||||
console.warn(
|
|
||||||
`[dough-restful] user-token /users/${id}/profile -> HTTP ${res.status}` +
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
(retryAfter ? ` (retry ${retryAfter}s)` : "")
|
const idx = (start + i) % tokens.length;
|
||||||
);
|
const res = await fetch(url, { headers: { Authorization: tokens[idx] } });
|
||||||
return { data: null, status: res.status, retryAfter };
|
if (res.ok) {
|
||||||
}
|
|
||||||
return { data: (await res.json()) as RawProfileResponse, status: 200, retryAfter: 0 };
|
return { data: (await res.json()) as RawProfileResponse, status: 200, retryAfter: 0 };
|
||||||
}
|
}
|
||||||
|
lastStatus = res.status;
|
||||||
|
lastRetryAfter = Number(res.headers.get("retry-after")) || 0;
|
||||||
|
console.warn(
|
||||||
|
`[dough-restful] user-token #${idx + 1} /users/${id}/profile -> HTTP ${res.status}` +
|
||||||
|
(lastRetryAfter ? ` (retry ${lastRetryAfter}s)` : "")
|
||||||
|
);
|
||||||
|
// Only a rate-limit is worth retrying on another token; 401/403/404 would
|
||||||
|
// behave the same (or signal a token problem we'd rather surface).
|
||||||
|
if (res.status !== 429) break;
|
||||||
|
}
|
||||||
|
return { data: null, status: lastStatus, retryAfter: lastRetryAfter };
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ export interface Env {
|
||||||
DISCORD_BOT_TOKEN: string;
|
DISCORD_BOT_TOKEN: string;
|
||||||
/** Optional self-bot token for rich profile data. Off by default. */
|
/** Optional self-bot token for rich profile data. Off by default. */
|
||||||
DISCORD_USER_TOKEN?: string;
|
DISCORD_USER_TOKEN?: string;
|
||||||
|
/** Optional second self-bot token; rich fetches spread across both and fail
|
||||||
|
* over on a 429, doubling the /profile rate-limit headroom. */
|
||||||
|
DISCORD_USER_TOKEN2?: string;
|
||||||
|
|
||||||
DISCORD_API_VERSION?: string;
|
DISCORD_API_VERSION?: string;
|
||||||
TRACKED_GUILD_IDS?: string;
|
TRACKED_GUILD_IDS?: string;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue