import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq, inArray } from "drizzle-orm"; import { findDiscordIdsByUserIds } from "~/models/account"; import { getUserDisplayName } from "~/models/user"; import { sendQualifyingPointsUpdateNotification, type QPEventEntry, } from "~/services/discord"; export async function notifyQualifyingPointsUpdate( sportsSeasonId: string, scoringEventId: string, db: ReturnType ): Promise { const event = await db.query.scoringEvents.findFirst({ where: eq(schema.scoringEvents.id, scoringEventId), with: { sportsSeason: { with: { sport: true }, }, }, }); if (!event) return; const eventName = event.name; const sportName = event.sportsSeason?.sport?.name; const seasonSports = await db.query.seasonSports.findMany({ where: eq(schema.seasonSports.sportsSeasonId, sportsSeasonId), }); if (seasonSports.length === 0) return; // QP earned in this specific event const eventResultRows = await db.query.eventResults.findMany({ where: eq(schema.eventResults.scoringEventId, scoringEventId), }); const qpEarnedById = new Map( eventResultRows .filter((r) => r.qualifyingPointsAwarded !== null) .map((r) => [r.seasonParticipantId, parseFloat(r.qualifyingPointsAwarded!)]) ); if (qpEarnedById.size === 0) return; // Current running QP totals for the season const qpTotals = await db.query.seasonParticipantQualifyingTotals.findMany({ where: eq(schema.seasonParticipantQualifyingTotals.sportsSeasonId, sportsSeasonId), }); const qpTotalById = new Map( qpTotals.map((t) => [t.participantId, parseFloat(t.totalQualifyingPoints)]) ); for (const { seasonId } of seasonSports) { const season = await db.query.seasons.findFirst({ where: eq(schema.seasons.id, seasonId), with: { league: true }, }); const webhookUrl = season?.league?.discordWebhookUrl; if (!webhookUrl) continue; const seasonName = `${season!.league!.name} ${season!.year}`; // Map drafted participants → teams in this season const picks = await db.query.draftPicks.findMany({ where: eq(schema.draftPicks.seasonId, seasonId), with: { team: true }, }); const teamByParticipantId = new Map< string, { teamName: string; ownerId: string | null } >(); for (const pick of picks) { teamByParticipantId.set(pick.participantId, { teamName: pick.team.name, ownerId: pick.team.ownerId, }); } // Only include participants that appear in both the event and this league's draft const relevantParticipantIds = [...qpEarnedById.keys()].filter((id) => teamByParticipantId.has(id) ); if (relevantParticipantIds.length === 0) continue; // Participant display names const participants = await db.query.seasonParticipants.findMany({ where: inArray(schema.seasonParticipants.id, relevantParticipantIds), }); const participantNameById = new Map(participants.map((p) => [p.id, p.name])); // Owner usernames + Discord IDs const ownerIds = [ ...new Set( relevantParticipantIds .map((id) => teamByParticipantId.get(id)?.ownerId) .filter((id): id is string => id !== null && id !== undefined) ), ]; const users = ownerIds.length ? await db.query.users.findMany({ where: inArray(schema.users.id, ownerIds), }) : []; const usernameByUserId = new Map( users .map((u) => [u.id, getUserDisplayName(u)] as [string, string | null]) .filter((entry): entry is [string, string] => entry[1] !== null) ); const optedInUserIds = users.filter((u) => u.discordPingEnabled).map((u) => u.id); const discordIdByUserId = await findDiscordIdsByUserIds(optedInUserIds); const entries: QPEventEntry[] = relevantParticipantIds.map((participantId) => { const teamInfo = teamByParticipantId.get(participantId)!; const ownerId = teamInfo.ownerId; return { participantName: participantNameById.get(participantId) ?? participantId, qpEarned: qpEarnedById.get(participantId) ?? 0, qpTotal: qpTotalById.get(participantId) ?? 0, ownerUsername: ownerId ? (usernameByUserId.get(ownerId) ?? undefined) : undefined, ownerDiscordUserId: ownerId ? discordIdByUserId.get(ownerId) : undefined, }; }); await sendQualifyingPointsUpdateNotification({ webhookUrl, seasonName, sportName, eventName, entries, }); } }