This commit is contained in:
Slipstream 2025-05-03 18:05:06 -06:00
parent c26fbda939
commit 34f94ca462
Signed by: slipstream
GPG Key ID: 13E498CE010AC6FD
2 changed files with 168 additions and 15 deletions

View File

@ -585,7 +585,28 @@ async def verify_dashboard_guild_admin(guild_id: int, current_user: dict = Depen
async with http_session.get(DISCORD_USER_GUILDS_URL, headers=user_headers) as resp:
if resp.status == 429: # Rate limited
retry_count += 1
retry_after = int(resp.headers.get('Retry-After', 1))
# Get the most accurate retry time from headers
retry_after = float(resp.headers.get('X-RateLimit-Reset-After',
resp.headers.get('Retry-After', 1)))
# Check if this is a global rate limit
is_global = resp.headers.get('X-RateLimit-Global') is not None
# Get the rate limit scope if available
scope = resp.headers.get('X-RateLimit-Scope', 'unknown')
log.warning(
f"Dashboard: Discord API rate limit hit. "
f"Global: {is_global}, Scope: {scope}, "
f"Reset after: {retry_after}s, "
f"Retry: {retry_count}/{max_retries}"
)
# For global rate limits, we might want to wait longer
if is_global:
retry_after = max(retry_after, 5) # At least 5 seconds for global limits
continue
if resp.status == 401:
@ -923,9 +944,47 @@ async def dashboard_get_guild_channels(
async with http_session.get(f"https://discord.com/api/v10/guilds/{guild_id}/channels", headers=bot_headers) as resp:
if resp.status == 429: # Rate limited
retry_count += 1
retry_after = int(resp.headers.get('Retry-After', 1))
# Get the most accurate retry time from headers
retry_after = float(resp.headers.get('X-RateLimit-Reset-After',
resp.headers.get('Retry-After', 1)))
# Check if this is a global rate limit
is_global = resp.headers.get('X-RateLimit-Global') is not None
# Get the rate limit scope if available
scope = resp.headers.get('X-RateLimit-Scope', 'unknown')
log.warning(
f"Dashboard: Discord API rate limit hit. "
f"Global: {is_global}, Scope: {scope}, "
f"Reset after: {retry_after}s, "
f"Retry: {retry_count}/{max_retries}"
)
# For global rate limits, we might want to wait longer
if is_global:
retry_after = max(retry_after, 5) # At least 5 seconds for global limits
continue
# Check rate limit headers and log them for monitoring
rate_limit = {
'limit': resp.headers.get('X-RateLimit-Limit'),
'remaining': resp.headers.get('X-RateLimit-Remaining'),
'reset': resp.headers.get('X-RateLimit-Reset'),
'reset_after': resp.headers.get('X-RateLimit-Reset-After'),
'bucket': resp.headers.get('X-RateLimit-Bucket')
}
# If we're getting close to the rate limit, log a warning
if rate_limit['remaining'] and int(rate_limit['remaining']) < 5:
log.warning(
f"Dashboard: Rate limit warning: {rate_limit['remaining']}/{rate_limit['limit']} "
f"requests remaining in bucket {rate_limit['bucket']}. "
f"Resets in {rate_limit['reset_after']}s"
)
resp.raise_for_status()
channels = await resp.json()
@ -982,7 +1041,28 @@ async def dashboard_get_guild_roles(
async with http_session.get(f"https://discord.com/api/v10/guilds/{guild_id}/roles", headers=bot_headers) as resp:
if resp.status == 429: # Rate limited
retry_count += 1
retry_after = int(resp.headers.get('Retry-After', 1))
# Get the most accurate retry time from headers
retry_after = float(resp.headers.get('X-RateLimit-Reset-After',
resp.headers.get('Retry-After', 1)))
# Check if this is a global rate limit
is_global = resp.headers.get('X-RateLimit-Global') is not None
# Get the rate limit scope if available
scope = resp.headers.get('X-RateLimit-Scope', 'unknown')
log.warning(
f"Dashboard: Discord API rate limit hit. "
f"Global: {is_global}, Scope: {scope}, "
f"Reset after: {retry_after}s, "
f"Retry: {retry_count}/{max_retries}"
)
# For global rate limits, we might want to wait longer
if is_global:
retry_after = max(retry_after, 5) # At least 5 seconds for global limits
continue
resp.raise_for_status()
@ -1050,7 +1130,28 @@ async def dashboard_get_guild_commands(
async with http_session.get(f"https://discord.com/api/v10/applications/{application_id}/guilds/{guild_id}/commands", headers=bot_headers) as resp:
if resp.status == 429: # Rate limited
retry_count += 1
retry_after = int(resp.headers.get('Retry-After', 1))
# Get the most accurate retry time from headers
retry_after = float(resp.headers.get('X-RateLimit-Reset-After',
resp.headers.get('Retry-After', 1)))
# Check if this is a global rate limit
is_global = resp.headers.get('X-RateLimit-Global') is not None
# Get the rate limit scope if available
scope = resp.headers.get('X-RateLimit-Scope', 'unknown')
log.warning(
f"Dashboard: Discord API rate limit hit. "
f"Global: {is_global}, Scope: {scope}, "
f"Reset after: {retry_after}s, "
f"Retry: {retry_count}/{max_retries}"
)
# For global rate limits, we might want to wait longer
if is_global:
retry_after = max(retry_after, 5) # At least 5 seconds for global limits
continue
# Handle 404 specially - it's not an error, just means no commands are registered

View File

@ -175,15 +175,49 @@ const API = {
const response = await fetch(url, options);
console.log(`API Response: ${response.status} ${response.statusText}`);
// Check rate limit headers and log them for monitoring
const rateLimit = {
limit: response.headers.get('X-RateLimit-Limit'),
remaining: response.headers.get('X-RateLimit-Remaining'),
reset: response.headers.get('X-RateLimit-Reset'),
resetAfter: response.headers.get('X-RateLimit-Reset-After'),
bucket: response.headers.get('X-RateLimit-Bucket')
};
// If we're getting close to the rate limit, log a warning
if (rateLimit.remaining && parseInt(rateLimit.remaining) < 5) {
console.warn(`API Rate limit warning: ${rateLimit.remaining}/${rateLimit.limit} requests remaining in bucket ${rateLimit.bucket}. Resets in ${rateLimit.resetAfter}s`);
}
// Handle rate limiting with automatic retry
if (response.status === 429 && retryCount < MAX_RETRIES) {
// Get retry-after header or default to increasing backoff
const retryAfter = parseInt(response.headers.get('Retry-After') || Math.pow(2, retryCount));
console.log(`Rate limited. Retrying in ${retryAfter} seconds...`);
// Get the most accurate retry time from headers
let retryAfter = parseFloat(
response.headers.get('X-RateLimit-Reset-After') ||
response.headers.get('Retry-After') ||
Math.pow(2, retryCount)
);
// Show toast only on first retry
// Check if this is a global rate limit
const isGlobal = response.headers.get('X-RateLimit-Global') !== null;
// Get the rate limit scope if available
const scope = response.headers.get('X-RateLimit-Scope') || 'unknown';
// For global rate limits, we might want to wait longer
if (isGlobal) {
retryAfter = Math.max(retryAfter, 5); // At least 5 seconds for global limits
}
console.log(`Rate limited (${isGlobal ? 'Global' : 'Route'}, Scope: ${scope}). Retrying in ${retryAfter} seconds...`);
// Show toast with more detailed information
if (retryCount === 0) {
Toast.warning(`Rate limited by Discord API. Retrying in ${retryAfter} seconds...`, 'Please Wait');
const message = isGlobal
? `Discord API global rate limit hit. Retrying in ${retryAfter} seconds...`
: `Rate limited by Discord API. Retrying in ${retryAfter} seconds...`;
Toast.warning(message, 'Please Wait');
}
// Wait for the specified time
@ -233,13 +267,31 @@ const API = {
}, 2000);
}
else if (response.status === 429) {
// Rate limiting - show a more user-friendly message
Toast.warning('Discord API rate limit reached. Please wait a moment before trying again.', 'Rate Limited');
// Get rate limit information from headers
const retryAfter = parseFloat(
response.headers.get('X-RateLimit-Reset-After') ||
response.headers.get('Retry-After') ||
'60'
);
// If Retry-After header is present, we could use it to show a countdown
const retryAfter = response.headers.get('Retry-After');
if (retryAfter) {
console.log(`Rate limited. Retry after ${retryAfter} seconds`);
const isGlobal = response.headers.get('X-RateLimit-Global') !== null;
const scope = response.headers.get('X-RateLimit-Scope') || 'unknown';
const bucket = response.headers.get('X-RateLimit-Bucket') || 'unknown';
// Log detailed rate limit information
console.log(`Rate limit hit: Global=${isGlobal}, Scope=${scope}, Bucket=${bucket}, Retry=${retryAfter}s`);
// Show appropriate message based on rate limit type
if (isGlobal) {
Toast.warning(
`Discord API global rate limit reached. Please wait ${Math.ceil(retryAfter)} seconds before trying again.`,
'Global Rate Limit'
);
} else {
Toast.warning(
`Discord API rate limit reached. Please wait ${Math.ceil(retryAfter)} seconds before trying again.`,
'Rate Limited'
);
}
}