worldcup-tui
A terminal dashboard of live and upcoming FIFA World Cup matches โ emoji
flags, live scores and clocks, and goal/card events โ powered by the public
Sofascore API. Single screen, no config, no API
keys.
๐ FIFA World Cup 2026 updated 17:33:20
โ LIVE
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ ๐ง๐ท Brazil 2 โ 1 France ๐ซ๐ท โ
โ 79' ยท Quarterfinal โ
โ โฝ 23' Vinicius โฝ 51' Mbappรฉ โ
โ โฝ 67' Rodrygo ๐จ 70' Tchouamรฉni โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
UPCOMING
๐ฒ๐ฝ Mexico vs South Africa ๐ฟ๐ฆ
Today 16:00 ยท Group A
๐ฉ๐ช Germany vs England ๐ด๓ ง๓ ข๓ ฅ๓ ฎ๓ ง๓ ฟ
Tomorrow 16:00 ยท Semifinal
โ 6 more
RECENT
๐ฆ๐ท Argentina 3 โ 1 Spain ๐ช๐ธ FT
q quit ยท r refresh ยท โ/โ scroll upcoming
Install
Homebrew:
brew install gmnmedeiros/tap/worldcup-tui
Or with Go (requires Go 1.26+):
go install github.com/gmnmedeiros/worldcup-tui@latest
Or build from a clone:
go build -o worldcup-tui .
./worldcup-tui
Usage
worldcup-tui [flags]
| Flag |
Default |
Description |
-season |
0 |
Sofascore season id (0 resolves the current World Cup season, falling back to 58210) |
-schedule-interval |
5m |
how often to refresh the full schedule |
-live-interval |
30s |
how often to refresh live scores while matches are in progress |
-live-window |
10m |
begin live polling when a kickoff is within this window |
Controls
| Key |
Action |
q / esc / ctrl+c |
quit |
r |
force a refresh now |
โ / โ (or k / j) |
scroll the UPCOMING list |
How it works
- Sections. LIVE shows in-progress matches as rich cards with goal/card
events; UPCOMING lists the next fixtures by kickoff in your local timezone;
RECENT shows the last few finished results.
- Adaptive polling. A slow schedule tick (~5 min) keeps the fixture list
fresh. A fast live tick (~30 s) runs only while a match is in progress or a
kickoff is imminent, and stops otherwise โ so an idle dashboard barely
touches the network. All fetches run off the UI thread; the screen never
blocks.
- Resilience. On a rate-limit or fetch error the dashboard keeps showing the
last good data with a
stale indicator in the header instead of crashing.
- Access. Sofascore blocks plain HTTP clients via TLS fingerprinting at the
CDN. This tool uses
bogdanfinn/tls-client
with a Chrome profile, plus an x-requested-with: XMLHttpRequest header, to
reach the API directly โ no proxy, no keys.
A note on emoji flags
Flags are rendered from Unicode regional-indicator pairs, with the GB home
nations (England/Scotland/Wales) as flag tag sequences. These render correctly
in modern terminals (iTerm2, Ghostty, WezTerm); some terminals show letter
pairs or a plain black flag instead. Match cards pin flags to the outer edges so
the score column stays aligned regardless.
Development
go test ./... # unit tests (HTTP layer via httptest; logic via recorded fixtures)
go vet ./...
go run ./cmd/demo -print # render one dashboard frame from static data
Tests run against recorded JSON fixtures in internal/sofascore/testdata/
captured from the real API โ the TLS impersonation itself is only verifiable
against the live service.
Layout
main.go # flag parsing, program startup
internal/
sofascore/ # API client: TLS impersonation, error mapping,
# backoff, pagination; typed endpoints
domain/ # event -> Match mapping, live clock, incident filtering
flags/ # country code -> emoji flag (+ GB-nation cases)
ui/ # Bubble Tea model, dashboard rendering, polling
cmd/
spike/ # one-shot API reachability check + fixture capture
demo/ # renders the dashboard on static data
Out of scope
Match drill-down views, other tournaments/sports, notifications, persistence,
and historical stats. See docs/PLAN.md for the full design.