尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证A Football Trivia and Fantasy app by Culers
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗A private, free-to-play fantasy football app for a small group of friends, built around FC Barcelona matches only (LaLiga, Champions League, Copa del Rey). There's no real money, no payments and no app store listing.
For every Barça match you pick 11 players from Barça and that day's opponent. Points arrive after full time, and there's a leaderboard for each match plus one for the season.
Not affiliated with FC Barcelona. The app uses player names and initials only, with no crests, logos, kits or photos.
All values live in src/features/fantasy/scoring/rules.ts.
| Event | GK | DEF | MID | FWD |
|---|---|---|---|---|
| Appearance, 60+ minutes | 2 | 2 | 2 | 2 |
| Appearance, under 60 minutes | 1 | 1 | 1 | 1 |
| Goal | 6 | 6 | 5 | 4 |
| Assist | 3 | 3 | 3 | 3 |
| Clean sheet (needs 60+ minutes) | 4 | 4 | 1 | 0 |
| Every 3 saves | 1 | – | – | – |
| Penalty saved | 5 | 5 | 5 | 5 |
| Penalty missed | −2 | −2 | −2 | −2 |
| Every 2 goals conceded | −1 | −1 | – | – |
| Yellow card | −1 | −1 | −1 | −1 |
| Red card | −3 | −3 | −3 | −3 |
| Own goal | −2 | −2 | −2 | −2 |
| Captain ×2 · Vice-captain ×1.5 if the captain played 0 minutes |
A player with 0 minutes scores 0. If a saved team breaks the rules (for example, someone bypassed the UI), it scores 0 and the reason is shown.
You'll need Node 20.9 or later (22 recommended) and pnpm 10 (npm i -g pnpm).
pnpm install
http://localhost:3000 for now)http://localhost:3000/auth/callback to Redirect URLs, plus your Vercel URL with
/auth/callback laterThe sign-in page shows these buttons automatically once they're switched on in Supabase.
https://<your-project-ref>.supabase.co/auth/v1/callback as an Authorized redirect
URI.Sign up at highlightly.net, choose the free Basic plan for the Football API and copy your API key. Use Highlightly's own site, not RapidAPI: the two have separate accounts and keys.
Why not API-Football? Its free plan only covers the 2022–2024 seasons. Highlightly's free plan includes the current season.
Copy the example file and fill in the values:
cp .env.example .env.local
Each variable is explained in .env.example. .env.local is git-ignored, so
never commit it. If a required variable is missing, the app stops with a clear list of what to
fix.
Hosted project: use the Supabase CLI:
pnpm dlx supabase login
pnpm dlx supabase link --project-ref <your-project-ref>
pnpm dlx supabase db push
Alternatively, paste the files from supabase/migrations/ into the SQL editor, in order.
Local development (optional, needs Docker):
pnpm dlx supabase start
pnpm dlx supabase db reset
Then use the local URL and keys that supabase start prints.
db reset applies the migrations and loads supabase/seed.sql, which contains fictional
users, players, two matches and three teams. Magic-link emails land in Inbucket at
http://localhost:54324. The seed's invite code is local-dev-code, and [email protected]
is the seeded admin.
Don't run the seed against your real project. It creates fake users.
pnpm check:api
Leave BARCA_TEAM_ID and the league ids blank the first time. The script:
.env.localCURRENT_SEASON returns Barça matches, with a count per competitionIt uses 2 requests, plus 1 for each id lookup still needed (5 at most). Run it again after pasting the ids to confirm.
pnpm dev
ADMIN_EMAILS. You're
made an admin on sign-in.INVITE_CODE on /join. The first join creates the league./admin and click Sync fixtures, then Sync squads.Every call goes through one client,
src/lib/highlightly/client.ts. It adds the key header,
logs the remaining quota, caches responses in the api_cache table and never retries
after a rate-limit or quota error. There's no live polling.
| Job | When | Cost |
|---|---|---|
/api/sync/fixtures | daily | 2 requests (Barça home + away matches, all competitions; cached 12h) |
/api/sync/squads | daily, for matches ≤10 days away | Up to 3 per team: its last five games (cached 1 day) + line-ups of its last 2 (cached forever) |
/api/sync/lineups | every 15 min near kickoff | 1 per match. Empty responses aren't cached, so retrying before line-ups are out is safe. |
/api/sync/stats | every 15 min after full time | 2 per match (match details with events + player box score), cached forever |
That comes to roughly 8 requests per match, well inside the free 100/day.
Player pools: Highlightly has no squad endpoint, so each team's pickable players come from the line-ups (starters and full bench) of its last 2 matches. A player missing from both, for example someone back from injury, appears once they're named in a line-up.
Each route accepts Authorization: Bearer $CRON_SECRET or a signed-in admin, plus optional
?matchId=<id>. Example:
curl -H "Authorization: Bearer $CRON_SECRET" https://your-app.vercel.app/api/sync/fixtures
Import the GitHub repo into Vercel. It detects the framework automatically.
Add every variable from .env.example under Settings → Environment Variables, and set
APP_URL to your Vercel URL.
Add https://<your-app>.vercel.app/auth/callback to Supabase's Redirect URLs.
Scheduled syncs run on GitHub Actions (.github/workflows/sync.yml),
not Vercel crons (Vercel's free plan only allows daily ones). In the GitHub repo, open
Settings → Secrets and variables → Actions and add:
APP_URL with your Vercel URL, e.g. https://cule-xi.vercel.appCRON_SECRET with the same value as in VercelThe workflow runs line-ups and stats every 15 minutes; fixtures, squads (with form-based prices) and player photos daily at 05:30 UTC; and deletes guest accounts nobody has used for 7 days (checked every 2 hours). Each job only calls Highlightly when something is due, so most runs use no requests. You can also run it by hand from the Actions tab (Sync match data → Run workflow).
src/
app/ # routes: /, /join, /matches, /matches/[id]/pick, /leaderboard, /admin, /trivia
actions/ # server actions (auth, league, team, admin)
api/sync/*/route.ts # protected sync endpoints
features/
fantasy/
scoring/ # rules.ts, pure scoring engine (+ tests)
validation/ # team validation (+ tests)
sync/ # fixtures / squads / lineups / stats sync, points recalculation
trivia/ # Culé Ten daily game: rules, lists, matching, server
lib/
highlightly/ # client, types, mappers, __fixtures__ (mock responses)
wikipedia/ # player photo lookup (Wikimedia Commons) with attribution
supabase/ # server, admin (service role) and proxy clients
env/ # zod-validated env (server vs public)
supabase/
migrations/ # schema, RLS, save_fantasy_team RPC, bigint provider ids
seed.sql # fictional local data
scripts/check-api.ts # pnpm check:api
| Script | What it does |
|---|---|
pnpm dev | Start the dev server |
pnpm build / pnpm start | Production build and serve |
pnpm lint | ESLint |
pnpm format | Prettier (write); pnpm format:check to verify |
pnpm typecheck | tsc --noEmit |
pnpm test | Vitest: scoring, validation, API client and mappers, plus migrations and RLS on PGlite |
pnpm check:api | Verify the API key, look up team/league ids, check season access (2–5 requests) |
/admin. Own goals do come through, from the match events.src/features/fantasy/pricing.ts and the /rules page. Admins can
lock a price on /admin. Saved teams keep the prices they paid./credits). A photo is only used when the
Wikipedia article is clearly the same footballer at the same club and the image file is named
after them; otherwise initials show. Admins can set, fix or remove a photo on /admin.supabase gen types typescript --linked > src/lib/supabase/database.types.ts to generate
them.One tap: Play now (a guest account with an automatic football name like "Tiki Kubala 27")
or Continue with Google; email links are under "Use email instead". Everyone joins the
Global league automatically; an invite code adds a private league on top (shown first on
the leaderboard, which can switch between leagues). Names are unique (case-insensitive).
Change your name or join a league on /join.
/trivia has a daily top-10 game: name the 10 players in today's list, with a nationality
hint per slot and 3 lives. It draws on a local repository of every Barça player, imported once
from Wikidata (free), plus managers and presidents. Lists span the club's whole history: club
records, 100+ appearance players by nation, position and era, every season's top scorer,
managers and presidents, parsed once from Wikipedia and stored. History lists that pass every
check go live automatically; the rest wait for approval on /admin/trivia. Current-season
lists are generated automatically.
/trivia/grid has Tiki-Taka-Toe: a daily 3×3 grid across all of La Liga where each square
needs a player who fits its row and column: a club (La Liga's 35 biggest plus famous foreign
ones), nationality, position, a trophy (Pichichi, Zamora, 100+ La Liga goals) or a decade at
Barça. 9 guesses, a rarity score, and a table with your league-mates.
Mystery Player (/trivia/mystery, daily): guess the hidden La Liga player; every guess
shows nation, position, birth year (older/younger), clubs in common and Barça. 8 guesses.
More games: Connections (/trivia/connections, daily: 16 players, 4 hidden groups),
Missing XI (/trivia/missing-xi, daily: name a famous European final's starting XI on a
pitch; 60 line-ups of Spanish clubs from Wikipedia) and Odd One Out
(/trivia/odd-one-out, endless: which of four never played for the club / won the trophy?).
Ballon d'Or podiums (Wikipedia) are a Tiki-Taka-Toe trophy square. Import with
NODE_OPTIONS=--conditions=react-server pnpm exec tsx scripts/trivia-more.ts (also fetches club
careers for every La Liga player).
/trivia/career has Career Path (daily: whose club career is this? 5 guesses, a hint per
miss) and /trivia/higher-lower has Higher or Lower (endless: more or fewer La Liga goals?
streaks judged on the server). Club badges are free logos from Wikimedia Commons, credited on
/credits; FC Barcelona's crest is never shown.
All of La Liga (63 clubs, ~12k players, ~27k club links, records and trophies) is imported
once from Wikipedia and Wikidata and stored locally, then logos and careers:
NODE_OPTIONS=--conditions=react-server pnpm exec tsx scripts/trivia-extras.ts (~1 min) after:
NODE_OPTIONS=--conditions=react-server pnpm exec tsx scripts/trivia-la-liga.ts (~2 min). See
src/features/trivia/README.md.
MIT © Sayan Choudhury