Refactor Discord notification logic in scoring calculator
- Compute changedTeamIds once up front; derive hasChanges from it instead of duplicating the same iteration - Merge usernameForParticipant and winnerUsernameForParticipant into a single function with a requireScoreChange flag - Replace hasDraftedParticipantMatches with hasScoredMatchesToShow, which checks that at least one scoredMatch has a displayable username — prevents sending a contentless Discord embed when a drafted winner doesn't score any points and the loser isn't drafted - Update inline comment to be sport-agnostic https://claude.ai/code/session_01FciwfdG9Sfr5ZrXUHPeB18
This commit is contained in:
parent
b0c43ef2d3
commit
7c25852dfa
1 changed files with 26 additions and 38 deletions
|
|
@ -1299,10 +1299,15 @@ export async function recalculateAffectedLeagues(
|
|||
orderBy: (ts, { asc }) => [asc(ts.currentRank)],
|
||||
});
|
||||
|
||||
const hasChanges = afterStandings.some((s) => {
|
||||
const prev = previousPointsById.get(s.teamId);
|
||||
return prev === undefined || prev !== parseFloat(s.totalPoints);
|
||||
});
|
||||
const changedTeamIds = new Set(
|
||||
afterStandings
|
||||
.filter((s) => {
|
||||
const prev = previousPointsById.get(s.teamId);
|
||||
return prev === undefined || prev !== parseFloat(s.totalPoints);
|
||||
})
|
||||
.map((s) => s.teamId)
|
||||
);
|
||||
const hasChanges = changedTeamIds.size > 0;
|
||||
|
||||
// Look up the league's Discord webhook URL via the season
|
||||
const season = await db.query.seasons.findFirst({
|
||||
|
|
@ -1333,12 +1338,10 @@ export async function recalculateAffectedLeagues(
|
|||
afterStandings.map((s) => [s.teamId, s.team.ownerId])
|
||||
);
|
||||
|
||||
// Filter matches to only those involving participants drafted in this season.
|
||||
// We compute this BEFORE the hasChanges gate because 0-point eliminations
|
||||
// (e.g. R64 losers whose finalPosition=0 doesn't affect team scores) still
|
||||
// warrant a Discord notification when a drafted team is eliminated.
|
||||
// Build scored matches for the notification — computed before the gate
|
||||
// because an elimination (0 points, no score change) still warrants a
|
||||
// notification when a drafted participant is knocked out.
|
||||
let scoredMatches: ScoredMatch[] | undefined;
|
||||
let hasDraftedParticipantMatches = false;
|
||||
if (allCompletedMatches.length > 0) {
|
||||
const draftPicks = await db.query.draftPicks.findMany({
|
||||
where: eq(schema.draftPicks.seasonId, seasonId),
|
||||
|
|
@ -1351,40 +1354,22 @@ export async function recalculateAffectedLeagues(
|
|||
(m.loserId && draftedIds.has(m.loserId))
|
||||
);
|
||||
|
||||
hasDraftedParticipantMatches = relevant.length > 0;
|
||||
|
||||
if (relevant.length > 0) {
|
||||
const teamIdByParticipantId = new Map(
|
||||
draftPicks.map((p) => [p.participantId, p.teamId])
|
||||
);
|
||||
|
||||
// Teams whose score actually changed after recalculation.
|
||||
const changedTeamIds = new Set(
|
||||
afterStandings
|
||||
.filter((s) => {
|
||||
const prev = previousPointsById.get(s.teamId);
|
||||
return prev === undefined || prev !== parseFloat(s.totalPoints);
|
||||
})
|
||||
.map((s) => s.teamId)
|
||||
);
|
||||
|
||||
const usernameForParticipant = (participantId: string | null | undefined) => {
|
||||
// Look up the fantasy owner's display name for a participant.
|
||||
// Pass requireScoreChange=true for the winner to avoid surfacing wins
|
||||
// that didn't award points this round.
|
||||
const usernameForParticipant = (
|
||||
participantId: string | null | undefined,
|
||||
requireScoreChange = false
|
||||
) => {
|
||||
if (!participantId) return undefined;
|
||||
const teamId = teamIdByParticipantId.get(participantId);
|
||||
if (!teamId) return undefined;
|
||||
const ownerId = ownerIdByTeamId.get(teamId);
|
||||
if (!ownerId) return undefined;
|
||||
return usernameByClerkId.get(ownerId);
|
||||
};
|
||||
|
||||
// Only set winnerUsername if the winner's owner actually scored points
|
||||
// this round. This prevents listing matches where a drafted team won
|
||||
// but the scoring system didn't award points yet (e.g. R64 wins when
|
||||
// points only trigger at Sweet 16+).
|
||||
const winnerUsernameForParticipant = (participantId: string | null | undefined) => {
|
||||
if (!participantId) return undefined;
|
||||
const teamId = teamIdByParticipantId.get(participantId);
|
||||
if (!teamId || !changedTeamIds.has(teamId)) return undefined;
|
||||
if (requireScoreChange && !changedTeamIds.has(teamId)) return undefined;
|
||||
const ownerId = ownerIdByTeamId.get(teamId);
|
||||
if (!ownerId) return undefined;
|
||||
return usernameByClerkId.get(ownerId);
|
||||
|
|
@ -1395,14 +1380,17 @@ export async function recalculateAffectedLeagues(
|
|||
.map((m) => ({
|
||||
winnerName: m.winnerName!,
|
||||
loserName: m.loserName!,
|
||||
winnerUsername: winnerUsernameForParticipant(m.winnerId),
|
||||
winnerUsername: usernameForParticipant(m.winnerId, true),
|
||||
loserUsername: usernameForParticipant(m.loserId),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Skip notification if scores didn't change AND no drafted participants were scored.
|
||||
if (!hasChanges && !hasDraftedParticipantMatches) continue;
|
||||
// Skip notification if scores didn't change AND no match has a displayable username.
|
||||
const hasScoredMatchesToShow = scoredMatches?.some(
|
||||
(m) => m.winnerUsername !== undefined || m.loserUsername !== undefined
|
||||
);
|
||||
if (!hasChanges && !hasScoredMatchesToShow) continue;
|
||||
|
||||
const standings = afterStandings.map((s) => ({
|
||||
teamId: s.teamId,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue