interface DiscordEmbed { title?: string; description?: string; color?: number; footer?: { text: string }; } interface DiscordWebhookPayload { content?: string; embeds?: DiscordEmbed[]; } export async function sendDiscordWebhook( webhookUrl: string, payload: DiscordWebhookPayload ): Promise { const response = await fetch(webhookUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (!response.ok) { const text = await response.text().catch(() => ""); throw new Error(`Discord webhook failed: ${response.status} ${text}`); } } export interface StandingEntry { teamId: string; teamName: string; totalPoints: number; rank: number; } export interface ScoredMatch { winnerName: string; loserName: string; } export async function sendStandingsUpdateNotification({ webhookUrl, seasonName, standings, previousStandings, sportName, eventName, scoredMatches, }: { webhookUrl: string; seasonName: string; standings: StandingEntry[]; previousStandings: Map; sportName?: string; eventName?: string; scoredMatches?: ScoredMatch[]; }): Promise { const RANK_MEDALS = ["🥇", "🥈", "🥉"]; const sections: string[] = []; // Header: "Sport Name — Event Name" if (sportName || eventName) { const parts = [sportName, eventName].filter(Boolean); sections.push(`**${parts.join(" — ")}**`); } // Scored matches section if (scoredMatches && scoredMatches.length > 0) { sections.push("**Scored Matches**"); for (const match of scoredMatches) { sections.push(`• **${match.winnerName}** def. ${match.loserName}`); } } // Standings section sections.push("**Current Standings**"); for (const s of standings) { const prev = previousStandings.get(s.teamId); let delta = ""; if (prev !== undefined && prev !== s.totalPoints) { const diff = Math.round(s.totalPoints - prev); const sign = diff > 0 ? "+" : ""; delta = ` **(${sign}${diff} pts)**`; } const medal = RANK_MEDALS[s.rank - 1] ?? `${s.rank}.`; sections.push(`${medal} ${s.teamName} — ${Math.round(s.totalPoints)} pts${delta}`); } const MAX_DESCRIPTION = 4096; let description = sections.join("\n"); if (description.length > MAX_DESCRIPTION) { description = description.slice(0, MAX_DESCRIPTION - 3) + "..."; } await sendDiscordWebhook(webhookUrl, { embeds: [ { title: `📊 Standings Update — ${seasonName}`, description, color: 0x5865f2, // Discord blurple footer: { text: "brackt.com" }, }, ], }); }