Scorigami FC
브라질리그·유럽 주요 리그·아메리카 기타 리그의 역대 최종 점수를 그래프로 제공, 발생 여부와 횟수를 한눈에 보여줍니다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Free tennis courts in Bangkok: live availability where a venue publishes it, a self-check
link where it doesn't, and a LINE bot that tells you when a court frees up.
Zero runtime dependencies — Node 22.5+ only (built-in node:sqlite).
| Website | LINE bot + alerts | Data freshness | |
|---|---|---|---|
Static site (GitHub Pages, .github/workflows/pages.yml) | ✅ | ❌ (buttons still open LINE with the command typed) | every ~15 min, often later |
Server (node server.js — your Mac + tunnel today, Fly.io later) | ✅ | ✅ | every 5 min |
The static site is the same page in "static mode": a scheduled GitHub job runs
lib/poll.js, then scripts/build-static.js puts the page, the pure lib/ modules and
data/snapshot.json into dist/, and the browser does the filtering (public/data.js).
Build it yourself: node lib/poll.js && node scripts/build-static.js.
node server.js
# open http://localhost:3000
npm test
29 offline tests (Node's built-in runner, no dependencies): the slot engine, every
display rule in lib/state.js (past hours, midnight rollover, failed checks,
indoor/outdoor, 🔔 window), change detection, and each adapter against canned
responses shaped like the real APIs — including a regression test for every bug found
so far. Run it before and after any change.
The server polls every venue on startup and then every 5 minutes.
Force a one-off refresh: node lib/poll.js
includeCourts / excludeCourts regex strings; default include is tennis court,
default exclude catches (Kid), U4, junior, mini.types: ['indoor','outdoor'] in the catalog.Commands (English or Thai): alert Sat 18-21 2h goat · แจ้งเตือน เสาร์ 18-21 2ชม ·
my alerts · cancel 2 · cancel all · link · help. Admin only: status, selfcheck.
LINE_BOT_BASIC_ID), and Alert my
group opens LINE's share picker. You tap Send; LINE proves who sent it.
watchToCommand + roundTrips guarantee the text reads back as the same alert.link → the bot replies with a one-tap link
(needs PUBLIC_URL; opens in the normal browser) or a 6-digit code. One use, 10 minutes,
wrong codes rate-limited. Same rules as chat (checkWatch).expectCourts.data/app.db (Node's built-in SQLite). Secrets: .env (see .env.example)..env with only a made-up LINE_CHANNEL_SECRET → dry-run;
every message goes to data/outbox.log. Chat with it: npm run line -- "alert sat 18-21 2h".lib/poll.js reads the previous data/snapshot.json before overwriting it and stamps
each currently-free (venue, date, court, hour) via computeFreed():
freedAt (no reset)freedAt = nowserver.js treats a stamp as fresh for FRESH_WINDOW_MS (30 min) and exposes fresh per
slot + hasFresh per venue/day. No database — the stamps live in the snapshot and survive
restarts. SQLite arrives only with the LINE per-user watch store.catalog.js is the only file you touch.
source: { type: 'hatch', base }source: { type: 'okrabook', base, detailPath, venueId, sportId }source: { type: 'reservationSystem', base, itemType: 'court' }source: { type: 'manual' } + a bookingUrl (or lineUrl)catalog.js venues + how to read each (the only file you edit to add courts)
adapters/
hatch.js bespoke public API (returns BOOKED -> we invert to free; UTC)
okrabook.js multi-tenant platform (returns FREE court ids per slot)
reservation-system.js SvelteKit platform, __data.json in devalue format (local time,
ships its own business hours + holidays). GOAT57 runs on it.
lib/
time.js Bangkok (UTC+7) date helpers + Thai labels
slots.js the engine: free hours -> qualifying starts for N-hour runs
poll.js walk catalog -> data/snapshot.json
lib/state.js pure view model: snapshot -> what the page shows ("now" injectable)
server.js node:http — static site + /api/venues, /api/state?minHours=&place=
public/ minimal frontend (index.html, styles.css, app.js) + Leaflet
data/snapshot.json latest poll (regenerated; safe to delete)
/availability/<type>/__data.json?date= → live adapter. Likely white-label; if another
venue runs it, it's a catalog row only. (GOAT57.)<slug>.okrabook.com) — multi-tenant, availability PUBLIC via
POST /venues/getTimes → live adapter. Real tenants redirect to
<slug>.okrabook.com/login; non-tenants bounce to okrabook.com. (Beat is one.)*-booking.kegroup.co.th) — phone/OTP login → self-check. (Crystal.)getoncourt.app) — nice public venue pages, but booking needs
Google/LINE/email login → self-check. Lists many BKK courts. (Simoorgh.)One Ubuntu VM (free shape E2.1.Micro or A1), Caddy for HTTPS, systemd keeps it running.
<ip-with-dashes>.sslip.io (e.g. 129-150-1-2.sslip.io).curl -fsSLO https://raw.githubusercontent.com/FranceFlapjack/wannatennis/main/deploy/oracle-setup.sh && bash oracle-setup.sh <hostname>.env and data/app.db to /opt/wannatennis/ (owner
wannatennis, .env mode 600) and sudo systemctl restart wannatennis.https://<hostname>/line/webhook, Verify. Logs: journalctl -u wannatennis -f.
Update later: rerun step 3 (it pulls the latest code and restarts).
Only one copy may run — two bots would push every alert twice.server.js to Fly.io (Dockerfile + fly.toml are ready;
secrets via fly secrets import < .env, data on a volume at /app/data), point the LINE
webhook at https://<app>.fly.dev/line/webhook, stop the Mac copy. ~US$3–5/month.Heads-up: GitHub pauses scheduled jobs in a public repo after 60 days without commits — re-enable in the Actions tab (or push anything).
@lottiefiles/dotlottie-wc.public/motion.js — optional,
off for reduced motion or ?motion=0..env, webhook through a Cloudflare quick tunnel whose
address changes on restart — fixed by the Fly.io step). Not built: AI free-text box.