2026-03-22 01:57:39 -07:00
|
|
|
|
import type { FetchedStandingsRecord, StandingsSyncAdapter } from "./types";
|
|
|
|
|
|
|
|
|
|
|
|
const AFL_STANDINGS_URL = "https://api.squiggle.com.au/?q=standings;year=2026;format=json";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Squiggle API response shape for a single team's ladder entry.
|
|
|
|
|
|
* Docs: https://api.squiggle.com.au/
|
|
|
|
|
|
*/
|
|
|
|
|
|
interface SquiggleStandingsEntry {
|
|
|
|
|
|
id: number; // Squiggle team ID (1–18)
|
|
|
|
|
|
name: string; // Full team name, e.g. "Western Bulldogs"
|
|
|
|
|
|
wins: number;
|
|
|
|
|
|
losses: number;
|
|
|
|
|
|
draws: number;
|
|
|
|
|
|
played: number; // Games played
|
|
|
|
|
|
for: number; // Points scored for
|
|
|
|
|
|
against: number; // Points scored against
|
|
|
|
|
|
percentage: number;// (for / (for + against)) * 100
|
|
|
|
|
|
pts: number; // Ladder points (4=win, 2=draw, 0=loss)
|
|
|
|
|
|
rank: number; // Current ladder position (1-based)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface SquiggleStandingsResponse {
|
|
|
|
|
|
standings: SquiggleStandingsEntry[];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export class AflStandingsAdapter implements StandingsSyncAdapter {
|
|
|
|
|
|
async fetchStandings(): Promise<FetchedStandingsRecord[]> {
|
|
|
|
|
|
const response = await fetch(AFL_STANDINGS_URL, {
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
// Squiggle asks all clients to identify themselves for rate-limit contact.
|
|
|
|
|
|
// Set SQUIGGLE_CONTACT_EMAIL in your environment to identify this client.
|
|
|
|
|
|
"User-Agent": `Brackt.com AFL standings sync - ${process.env.SQUIGGLE_CONTACT_EMAIL ?? "admin@brackt.com"}`,
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
|
throw new Error(
|
|
|
|
|
|
`AFL Squiggle standings API returned ${response.status}: ${response.statusText}`
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const json = (await response.json()) as SquiggleStandingsResponse;
|
|
|
|
|
|
const entries = json.standings;
|
|
|
|
|
|
|
|
|
|
|
|
if (!entries || entries.length === 0) {
|
|
|
|
|
|
throw new Error(
|
|
|
|
|
|
"AFL Squiggle standings API returned no entries — response shape may have changed"
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Squiggle already includes `rank` (ladder position), sorted by ladder position.
|
|
|
|
|
|
// Sort ascending by rank so leagueRank matches the AFL ladder order.
|
2026-03-22 20:41:44 -07:00
|
|
|
|
const sorted = entries.toSorted((a, b) => a.rank - b.rank);
|
2026-03-22 01:57:39 -07:00
|
|
|
|
|
|
|
|
|
|
return sorted.map((entry): FetchedStandingsRecord => ({
|
|
|
|
|
|
teamName: entry.name,
|
|
|
|
|
|
externalTeamId: String(entry.id),
|
|
|
|
|
|
wins: entry.wins,
|
|
|
|
|
|
losses: entry.losses,
|
|
|
|
|
|
ties: entry.draws, // AFL draws are stored in the `ties` column
|
|
|
|
|
|
winPct: entry.percentage / 100, // Squiggle returns e.g. 62.5; store as 0.625
|
|
|
|
|
|
gamesPlayed: entry.played,
|
|
|
|
|
|
leagueRank: entry.rank,
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|