Fix Discord pick announcements arriving out of order and add 429 retry
All checks were successful
🚀 Deploy / 🧪 Test (pull_request) Successful in 1m30s
🚀 Deploy / ʦ TypeScript (pull_request) Successful in 1m16s
🚀 Deploy / 🔍 Lint (pull_request) Successful in 48s
🚀 Deploy / 🐳 Build (pull_request) Has been skipped
🚀 Deploy / 🚀 Deploy (pull_request) Has been skipped
All checks were successful
🚀 Deploy / 🧪 Test (pull_request) Successful in 1m30s
🚀 Deploy / ʦ TypeScript (pull_request) Successful in 1m16s
🚀 Deploy / 🔍 Lint (pull_request) Successful in 48s
🚀 Deploy / 🐳 Build (pull_request) Has been skipped
🚀 Deploy / 🚀 Deploy (pull_request) Has been skipped
- Move notifyPickMadeOnDiscord() before checkAndTriggerNextAutodraft() in both executeAutoPick() and the manual pick action. Previously the triggering pick's announcement fired after all chained autodraft picks had already announced, causing Discord to show e.g. pick #106 before #105 before #104. Now each pick announces itself before the next pick in the chain is triggered. - Add a single retry with Retry-After delay in sendDiscordWebhook() on HTTP 429. A burst of consecutive autodraft picks could hit Discord's per-webhook rate limit and silently drop announcements. Use || 1 (not ?? 1) to handle non-numeric/NaN headers, and cap at 10 s so that awaited callers such as the draft-order settings actions are never hung for a full global rate-limit window. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
425b4724d2
commit
77f520823c
3 changed files with 55 additions and 33 deletions
|
|
@ -827,23 +827,8 @@ export async function executeAutoPick(params: {
|
|||
logger.error("[AutoPick] Error updating Brackt EVs after pick:", error);
|
||||
}
|
||||
|
||||
// Check if next team has autodraft enabled and trigger immediately
|
||||
// Only run the chain from the top-level call to prevent recursion
|
||||
if (!isDraftComplete && params.chainEnabled !== false) {
|
||||
await checkAndTriggerNextAutodraft({
|
||||
seasonId,
|
||||
nextPickNumber,
|
||||
totalTeams,
|
||||
draftSlots,
|
||||
db,
|
||||
});
|
||||
|
||||
// Fire-and-forget: sendOnTheClockEmail fetches the current pick number from
|
||||
// the DB itself, so it always sees the post-chain state without blocking here.
|
||||
sendOnTheClockEmail({ seasonId, totalTeams, draftSlots, db })
|
||||
.catch((err) => logger.error("[AutoPick] On-the-clock email failed:", err));
|
||||
}
|
||||
|
||||
// Announce before triggering the chain so Discord messages arrive in pick-number
|
||||
// order. If the chain ran first, chained picks would announce before this one.
|
||||
const pickedSlot = draftSlots.find((s) => s.teamId === teamId);
|
||||
if (!pickedSlot) {
|
||||
logger.warn(`[AutoPick] Skipping Discord pick announcement: no draft slot found for team ${teamId}`);
|
||||
|
|
@ -865,6 +850,23 @@ export async function executeAutoPick(params: {
|
|||
}).catch((err) => logger.error("[AutoPick] Discord pick announcement failed:", err));
|
||||
}
|
||||
|
||||
// Check if next team has autodraft enabled and trigger immediately
|
||||
// Only run the chain from the top-level call to prevent recursion
|
||||
if (!isDraftComplete && params.chainEnabled !== false) {
|
||||
await checkAndTriggerNextAutodraft({
|
||||
seasonId,
|
||||
nextPickNumber,
|
||||
totalTeams,
|
||||
draftSlots,
|
||||
db,
|
||||
});
|
||||
|
||||
// Fire-and-forget: sendOnTheClockEmail fetches the current pick number from
|
||||
// the DB itself, so it always sees the post-chain state without blocking here.
|
||||
sendOnTheClockEmail({ seasonId, totalTeams, draftSlots, db })
|
||||
.catch((err) => logger.error("[AutoPick] On-the-clock email failed:", err));
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
pick: draftPick,
|
||||
|
|
|
|||
|
|
@ -303,6 +303,24 @@ export async function action(args: ActionFunctionArgs) {
|
|||
logger.error("Socket.IO error:", error);
|
||||
}
|
||||
|
||||
// Announce before triggering the chain so Discord messages arrive in pick-number
|
||||
// order. If the chain ran first, chained picks would announce before this one.
|
||||
notifyPickMadeOnDiscord({
|
||||
seasonId,
|
||||
leagueId: season.leagueId,
|
||||
pickedTeamName: currentDraftSlot.team.name,
|
||||
participantName: participant.name,
|
||||
sportName: participant.sportsSeason.sport.name,
|
||||
pickNumber: currentPickNumber,
|
||||
nextPickNumber,
|
||||
round: currentRound,
|
||||
rawPickInRound,
|
||||
isDraftComplete,
|
||||
totalTeams,
|
||||
draftSlots: draftSlots.map((s) => ({ teamId: s.teamId, draftOrder: s.draftOrder })),
|
||||
db,
|
||||
}).catch((err) => logger.error("Discord pick announcement failed:", err));
|
||||
|
||||
// Check if next team has autodraft enabled and trigger immediately
|
||||
if (!isDraftComplete) {
|
||||
try {
|
||||
|
|
@ -331,22 +349,6 @@ export async function action(args: ActionFunctionArgs) {
|
|||
.catch((err) => logger.error("On-the-clock email failed:", err));
|
||||
}
|
||||
|
||||
notifyPickMadeOnDiscord({
|
||||
seasonId,
|
||||
leagueId: season.leagueId,
|
||||
pickedTeamName: currentDraftSlot.team.name,
|
||||
participantName: participant.name,
|
||||
sportName: participant.sportsSeason.sport.name,
|
||||
pickNumber: currentPickNumber,
|
||||
nextPickNumber,
|
||||
round: currentRound,
|
||||
rawPickInRound,
|
||||
isDraftComplete,
|
||||
totalTeams,
|
||||
draftSlots: draftSlots.map((s) => ({ teamId: s.teamId, draftOrder: s.draftOrder })),
|
||||
db,
|
||||
}).catch((err) => logger.error("Discord pick announcement failed:", err));
|
||||
|
||||
return Response.json({
|
||||
success: true,
|
||||
pick: draftPick,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,24 @@ export async function sendDiscordWebhook(
|
|||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (response.status === 429) {
|
||||
// Cap at 10 s so awaited callers (e.g. settings actions) are never hung for
|
||||
// a full Discord global-rate-limit window. Use || 1 instead of ?? 1 so that
|
||||
// NaN (non-numeric header) and 0 both fall back to a 1-second default.
|
||||
const retryAfterSeconds = Math.min(Number(response.headers.get("Retry-After")) || 1, 10);
|
||||
await new Promise((r) => setTimeout(r, retryAfterSeconds * 1000));
|
||||
const retry = await fetch(webhookUrl, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
if (!retry.ok) {
|
||||
const text = await retry.text().catch(() => "");
|
||||
throw new Error(`Discord webhook failed after retry: ${retry.status} ${text}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => "");
|
||||
throw new Error(`Discord webhook failed: ${response.status} ${text}`);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue