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)],
|
orderBy: (ts, { asc }) => [asc(ts.currentRank)],
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasChanges = afterStandings.some((s) => {
|
const changedTeamIds = new Set(
|
||||||
const prev = previousPointsById.get(s.teamId);
|
afterStandings
|
||||||
return prev === undefined || prev !== parseFloat(s.totalPoints);
|
.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
|
// Look up the league's Discord webhook URL via the season
|
||||||
const season = await db.query.seasons.findFirst({
|
const season = await db.query.seasons.findFirst({
|
||||||
|
|
@ -1333,12 +1338,10 @@ export async function recalculateAffectedLeagues(
|
||||||
afterStandings.map((s) => [s.teamId, s.team.ownerId])
|
afterStandings.map((s) => [s.teamId, s.team.ownerId])
|
||||||
);
|
);
|
||||||
|
|
||||||
// Filter matches to only those involving participants drafted in this season.
|
// Build scored matches for the notification — computed before the gate
|
||||||
// We compute this BEFORE the hasChanges gate because 0-point eliminations
|
// because an elimination (0 points, no score change) still warrants a
|
||||||
// (e.g. R64 losers whose finalPosition=0 doesn't affect team scores) still
|
// notification when a drafted participant is knocked out.
|
||||||
// warrant a Discord notification when a drafted team is eliminated.
|
|
||||||
let scoredMatches: ScoredMatch[] | undefined;
|
let scoredMatches: ScoredMatch[] | undefined;
|
||||||
let hasDraftedParticipantMatches = false;
|
|
||||||
if (allCompletedMatches.length > 0) {
|
if (allCompletedMatches.length > 0) {
|
||||||
const draftPicks = await db.query.draftPicks.findMany({
|
const draftPicks = await db.query.draftPicks.findMany({
|
||||||
where: eq(schema.draftPicks.seasonId, seasonId),
|
where: eq(schema.draftPicks.seasonId, seasonId),
|
||||||
|
|
@ -1351,40 +1354,22 @@ export async function recalculateAffectedLeagues(
|
||||||
(m.loserId && draftedIds.has(m.loserId))
|
(m.loserId && draftedIds.has(m.loserId))
|
||||||
);
|
);
|
||||||
|
|
||||||
hasDraftedParticipantMatches = relevant.length > 0;
|
|
||||||
|
|
||||||
if (relevant.length > 0) {
|
if (relevant.length > 0) {
|
||||||
const teamIdByParticipantId = new Map(
|
const teamIdByParticipantId = new Map(
|
||||||
draftPicks.map((p) => [p.participantId, p.teamId])
|
draftPicks.map((p) => [p.participantId, p.teamId])
|
||||||
);
|
);
|
||||||
|
|
||||||
// Teams whose score actually changed after recalculation.
|
// Look up the fantasy owner's display name for a participant.
|
||||||
const changedTeamIds = new Set(
|
// Pass requireScoreChange=true for the winner to avoid surfacing wins
|
||||||
afterStandings
|
// that didn't award points this round.
|
||||||
.filter((s) => {
|
const usernameForParticipant = (
|
||||||
const prev = previousPointsById.get(s.teamId);
|
participantId: string | null | undefined,
|
||||||
return prev === undefined || prev !== parseFloat(s.totalPoints);
|
requireScoreChange = false
|
||||||
})
|
) => {
|
||||||
.map((s) => s.teamId)
|
|
||||||
);
|
|
||||||
|
|
||||||
const usernameForParticipant = (participantId: string | null | undefined) => {
|
|
||||||
if (!participantId) return undefined;
|
if (!participantId) return undefined;
|
||||||
const teamId = teamIdByParticipantId.get(participantId);
|
const teamId = teamIdByParticipantId.get(participantId);
|
||||||
if (!teamId) return undefined;
|
if (!teamId) return undefined;
|
||||||
const ownerId = ownerIdByTeamId.get(teamId);
|
if (requireScoreChange && !changedTeamIds.has(teamId)) return undefined;
|
||||||
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;
|
|
||||||
const ownerId = ownerIdByTeamId.get(teamId);
|
const ownerId = ownerIdByTeamId.get(teamId);
|
||||||
if (!ownerId) return undefined;
|
if (!ownerId) return undefined;
|
||||||
return usernameByClerkId.get(ownerId);
|
return usernameByClerkId.get(ownerId);
|
||||||
|
|
@ -1395,14 +1380,17 @@ export async function recalculateAffectedLeagues(
|
||||||
.map((m) => ({
|
.map((m) => ({
|
||||||
winnerName: m.winnerName!,
|
winnerName: m.winnerName!,
|
||||||
loserName: m.loserName!,
|
loserName: m.loserName!,
|
||||||
winnerUsername: winnerUsernameForParticipant(m.winnerId),
|
winnerUsername: usernameForParticipant(m.winnerId, true),
|
||||||
loserUsername: usernameForParticipant(m.loserId),
|
loserUsername: usernameForParticipant(m.loserId),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip notification if scores didn't change AND no drafted participants were scored.
|
// Skip notification if scores didn't change AND no match has a displayable username.
|
||||||
if (!hasChanges && !hasDraftedParticipantMatches) continue;
|
const hasScoredMatchesToShow = scoredMatches?.some(
|
||||||
|
(m) => m.winnerUsername !== undefined || m.loserUsername !== undefined
|
||||||
|
);
|
||||||
|
if (!hasChanges && !hasScoredMatchesToShow) continue;
|
||||||
|
|
||||||
const standings = afterStandings.map((s) => ({
|
const standings = afterStandings.map((s) => ({
|
||||||
teamId: s.teamId,
|
teamId: s.teamId,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue