← All Writing
March 21, 202610 min read

How I Automated My Soccer Club with a Bot

A Python bot on a headless Linux server that manages a TrophyManager.com soccer club — scouting the transfer market, placing bids, setting lineups, assigning training, listing players for sale, and grading its own decisions every Sunday

YieldA fully automated club manager that runs 9 cron jobs, makes dozens of decisions per day, and writes itself a weekly report card — all without opening a browser
DifficultyAdvanced (Playwright browser automation, AJAX reverse-engineering, SQLite, valuation modeling, cron orchestration)
Total Cook Time~15 hours across 10 sessions over 10 days — from first login script to v0.6.0 with self-grading AI

Ingredients

What Is TrophyManager?

TrophyManager is a browser-based soccer management sim. You run a club — setting lineups, buying and selling players on a live transfer market, assigning training, managing finances. Matches happen on a fixed schedule (Tuesdays, Thursdays, Saturdays for league; Wednesdays and Sundays for cups). The transfer market runs 24/7.

The game rewards consistency. Checking the transfer market twice a day, setting lineups before every deadline, rotating training groups — it’s a lot of small, repetitive decisions. The kind of thing a bot was made for.

I manage a club in a mid-tier division. Sixty players on the roster, most of them youth prospects aged 17–19. The strategy is simple: develop cheap young players, sell them at peak value, and reinvest in the next batch. It’s a volume game, and it’s exactly the kind of thing you don’t want to do manually 60 players at a time.

The First Wall: Login

Most web automation starts with a simple POST request to a login endpoint. TrophyManager doesn’t work that way. The login form submits via JavaScript that sets session cookies client-side. If you POST directly with Python’s requests library, you get a valid response but no session cookies — and every subsequent request fails silently.

🔧 Developer section: Playwright login

This was the hardest single problem in the project. Every other module — bidding, lineup, training — is just HTTP requests with the right cookies. Getting those cookies required reverse-engineering the login flow in browser DevTools and realizing that only a real browser submission works.

Mapping the Game’s AJAX Endpoints

TrophyManager has no public API. Everything happens through internal AJAX endpoints — hidden URLs that the game’s JavaScript calls behind the scenes to fetch and save data. I opened the browser’s DevTools (the built-in developer panel that shows every network request a page makes), clicked through every feature in the game, and logged every request. The result was a map of POST endpoints that the bot uses for everything:

🔧 Developer section: Key endpoint categories

One gotcha: skill values of 19 and 20 are returned as HTML <img> star tags instead of numbers. The parser has to read the alt attribute to get the numeric value. This is the kind of thing that only shows up when your bot tries to sort players by skill and everything above 18 comes back as NaN.

Nine Modules, Nine Cron Jobs

Each bot responsibility is a separate module with its own cron schedule:

The 2-minute bid sniper

Transfer bids in TrophyManager have a countdown. Running the bid module every 2 minutes means the bot can outbid competitors in the final minutes of an auction. It’s the most aggressive cron interval on the server — 720 runs per day — but each run is a single lightweight HTTP request.

The Valuation Model

The bot doesn’t just buy cheap players. It estimates what a player is worth based on age, skill index, and a hidden stat called “routine” — a composite training discipline score that ranges from 1 to 60+. Higher routine means the player develops faster and is worth more long-term.

🔧 Developer section: Valuation logic

The valuation model was rewritten twice. The first version used a flat rate per ASI point. The second added routine awareness after competitive analysis showed that the top clubs in the division were winning through squad quality (average routine 42.9) rather than tactical variety — they all used the same mentality every match. The gap was player development, not strategy.

The Bot Grades Itself

Every Sunday at 11am, the grader module runs. It pulls the week’s decisions from the SQLite database — bids placed, players sold, lineup choices, training assignments — and passes them to Claude CLI with a prompt asking for an honest assessment.

Claude looks at outcomes: did the bid win? Was the sale price reasonable? Did the lineup choice result in a win? The grader has per-module “grace days” because some outcomes take time to materialize — a scout dispatch takes 10 days to return results, so the grader doesn’t judge scouting decisions until they’ve had time to play out.

Decisions are logged to a local file with timestamps, module names, and Claude’s assessment. Over time, this creates a decision history that I can review to see if the bot is getting better or repeating mistakes.

Final Output

The bot went from first commit to v0.6.0 in 10 days and 10 sessions. It now runs 9 cron jobs, makes dozens of automated decisions per day, and emails me when something needs human judgment (like selling a starter without a backup).

What went fast

What needed patience

AI overconfidence is real

Claude wrote every module with high confidence — correct syntax, clean structure, reasonable logic. But confidence isn’t correctness. When the AI is guessing at undocumented API contracts, it produces code that looks professional and does nothing. The only defense is verifying outcomes in the real system, not just checking that the code runs.

This is the most fun project on the server. Not because it’s the most useful — the market briefing and alert system have more real-world value. But there’s something satisfying about taking Goose for a walk, coming back, and finding an email that says the bot bought three youth prospects overnight. Then checking in on Sunday to read Claude’s honest assessment of whether those purchases were smart.

← Back to all writing