Don't ping owners on rank-only standings changes in Discord #130

Merged
chrisp merged 1 commit from claude/no-ping-rank-only-standings into main 2026-07-05 07:00:46 +00:00
2 changed files with 48 additions and 5 deletions

View file

@ -277,6 +277,43 @@ describe("sendStandingsUpdateNotification", () => {
expect(desc).not.toContain("Gamma"); expect(desc).not.toContain("Gamma");
}); });
it("pings scorers but not rank-only shufflers", async () => {
await sendStandingsUpdateNotification({
webhookUrl: WEBHOOK_URL,
seasonName: "My League 2025",
standings: [
// Alpha scored — points changed, moved up.
{ teamId: "a", teamName: "Alpha", totalPoints: 150, rank: 1, discordUserId: "111" },
// Beta was displaced down without scoring — rank changed only.
{ teamId: "b", teamName: "Beta", totalPoints: 125, rank: 2, username: "beta_owner", discordUserId: "222" },
],
previousStandings: new Map([
["a", 125],
["b", 125],
]),
previousRanks: new Map([
["a", 2],
["b", 1],
]),
});
const desc = getDescription();
// Beta is still displayed with its rank movement…
expect(desc).toContain("Beta");
expect(desc).toContain("↓1");
// …but by name, not as an @-mention.
expect(desc).not.toContain("<@222>");
// Alpha scored, so it keeps its mention.
expect(desc).toContain("<@111>");
const payload = getPayload();
// Only the scorer is pinged.
expect(payload.content).toContain("111");
expect(payload.content).not.toContain("222");
expect(payload.allowed_mentions.users).toContain("111");
expect(payload.allowed_mentions.users).not.toContain("222");
});
it("omits rank delta when no previousRanks provided", async () => { it("omits rank delta when no previousRanks provided", async () => {
await sendStandingsUpdateNotification({ await sendStandingsUpdateNotification({
webhookUrl: WEBHOOK_URL, webhookUrl: WEBHOOK_URL,

View file

@ -175,13 +175,19 @@ export async function sendStandingsUpdateNotification({
const isTied = buildTiedRankChecker(standings.map((s) => s.rank)); const isTied = buildTiedRankChecker(standings.map((s) => s.rank));
const rankLabel = (rank: number) => (isTied(rank) ? `T${rank}` : `${rank}`); const rankLabel = (rank: number) => (isTied(rank) ? `T${rank}` : `${rank}`);
// A team's points genuinely changed this event (vs. merely being displaced in
// rank because someone else scored). Only real scorers are pinged; rank-only
// shufflers are still displayed, but by name and without an @-mention.
const pointsChanged = (s: StandingEntry) => {
const prevPoints = previousStandings.get(s.teamId);
return prevPoints !== undefined && prevPoints !== s.totalPoints;
};
// Standings changes section — show teams whose points or rank changed. // Standings changes section — show teams whose points or rank changed.
const changedTeams = standings.filter((s) => { const changedTeams = standings.filter((s) => {
const prevPoints = previousStandings.get(s.teamId);
const pointsChanged = prevPoints !== undefined && prevPoints !== s.totalPoints;
const prevRank = previousRanks?.get(s.teamId); const prevRank = previousRanks?.get(s.teamId);
const rankChanged = prevRank !== undefined && prevRank !== s.rank; const rankChanged = prevRank !== undefined && prevRank !== s.rank;
return pointsChanged || rankChanged; return pointsChanged(s) || rankChanged;
}); });
if (changedTeams.length > 0) { if (changedTeams.length > 0) {
@ -206,7 +212,7 @@ export async function sendStandingsUpdateNotification({
} }
const escapedName = escapeMarkdown(s.teamName); const escapedName = escapeMarkdown(s.teamName);
const managerLabel = s.discordUserId const managerLabel = s.discordUserId && pointsChanged(s)
? `<@${s.discordUserId}>` ? `<@${s.discordUserId}>`
: s.username : s.username
? escapeMarkdown(s.username) ? escapeMarkdown(s.username)
@ -225,7 +231,7 @@ export async function sendStandingsUpdateNotification({
// Collect Discord user IDs of all opted-in managers appearing in this notification. // Collect Discord user IDs of all opted-in managers appearing in this notification.
const pingUserIds = new Set<string>(); const pingUserIds = new Set<string>();
for (const s of changedTeams) { for (const s of changedTeams) {
if (s.discordUserId) pingUserIds.add(s.discordUserId); if (s.discordUserId && pointsChanged(s)) pingUserIds.add(s.discordUserId);
} }
for (const m of relevantMatches ?? []) { for (const m of relevantMatches ?? []) {
if (m.winnerDiscordUserId) pingUserIds.add(m.winnerDiscordUserId); if (m.winnerDiscordUserId) pingUserIds.add(m.winnerDiscordUserId);