gaganb.pageResume

CASE STUDY

Sisyphus

A ranked-game chronicle for a Discord squad

Year
2026
Type
Community Product / Automation / Data
Role
Creator / Sole Builder
Status
Shipped · live since May 19, 2026

Sisyphus was created under Riot Games' “Legal Jibber Jabber” policy using assets owned by Riot Games. Riot Games does not endorse or sponsor this project.

Snapshot

Goal Turn ranked games that would normally disappear into match history and Discord messages into a persistent shared record for the squad.

Challenge Reconcile delayed and imperfect third-party game state while keeping Discord interaction, history, markets and private analytics consistent.

Outcome A live friend-group product with 13 tracked players and 1,507 games recorded as of 13 November 2026.

Sisyphus did not start as a platform. It started because a friend said he was going to grind ranked.

01.1The joke that became the product

A friend said he was going to start a League of Legends ranked grind. I was bored and thought: "what if I build something to track him and potentially make fun of him :p".

That was basically the first product brief. The earliest version only needed to notice the climb and give the group something to react to.

Then more people in the squad were playing ranked and asked to be tracked too. The project stopped being about one friend and became a shared record of the group.

A shared record
STARTED AS A JOKEA friend said he was going to grind ranked.One friend → the squad
Sisyphus-BotAPPToday at 9:41 PM
Juno#0000
Ahri champion icon

✅ Victory · Ahri (MID) · 30-25

Ranked Solo/Duo · 35m 42s
gold rank GOLD IV — 42 LP +18 LP

Core Line8/3/11 KDA · 6.33 ratio
212 CS · 7.1/min
Level 17
OutputDamage 24,812 · 31% share
Gold 13,420 · 24% share
Kill participation 63%
Map WorkVision score 27
Wards 12
Cleared 4
StoryClean win: Juno gained +18 LP on Ahri with 8/3/11.
Spotlight• Juno moved the boulder +18 LP.
• Juno joined 63% of team kills.
• Juno lit the map with 27 vision score.
ContextTeam kills 30 · Enemy kills 25
Damage share 31% · Gold share 24%
ItemsShadowflame · Blackfire Torch · Swiftmarch · Cosmic Drive · Oblivion Orb · Needlessly Large Rod
📊 Overview🔵 Blue Team🔴 Red Team🏅 Full LeaderboardRemember
FIG. 01 · STARTED AS A JOKE · sisyphus/views.py · recap embed

01.2The real value was the shared story

Without Sisyphus, ranked progress is split across match history, Discord messages and memory. You can look up a game, but the group loses the surrounding story: who was grinding, how LP moved, what people predicted, what everyone argued about afterwards and which matches became memorable.

That is why I think of Sisyphus as a chronicle rather than a stat lookup. A game becomes a recap. The recap becomes a conversation. The result affects fictional markets and records. The history accumulates into something the squad can revisit.

MATCH ROWJuno · Win +18 LP
LP DELTA🥇 Gold IV · +18 LP
RECAP▣ Win · 8 / 3 / 11
CHRONICLETHE BOULDER CHRONICLE
Juno · Win
The shared story
FIG. 02 · THE PRODUCT LOOP · sisyphus/views.py · recap and dashboard/web/src/App.tsx

01.3Who it is for

Sisyphus is deliberately shaped around one private Discord squad. Anyone in the group can opt in to tracking, and the main product surface is Discord because that is where the group is already talking while games happen.

The dashboard is secondary. It gives the same history a richer analytical view, but the product would feel wrong if the group had to leave Discord for every interaction.

Most of the architecture choices are intentionally small-product choices: use the surfaces and storage that fit the squad, and harden the parts where external state is unreliable.

02.1Discord first, dashboard second

Discord is where the social moment happens, so recaps, queue state, live-room updates, predictions and reactions belong there. The website is better for long-lived exploration: LP journeys, activity, markets, records and the Boulder Archive.

This division kept the product from becoming a dashboard that happens to have a bot. The bot is the product surface; the dashboard is the deeper history.

Discord first
Sisyphus-BotAPPToday at 8:22 PM
platinum rankKairo#0000

Today's Stats

Rankplatinum rank PLATINUM IV — 42 LP
Today✅ 2 ❌ 1 ➖ 0
+18 LP
📊 Today🏆 All-time
THE BOULDER CHRONICLERANKED RECORD
THE BOULDER CHRONICLE / THE SQUAD LEDGER

The Squad Ledger

Tracked players13
Games recorded1,507as of 13 November 2026
FIG. 03 · TWO SURFACES · sisyphus/views.py · dashboard/web/src/App.tsx overview

02.2Why JSON stayed the source of truth

The bot still uses JSON as its primary state store. For one small private guild, that has been a deliberate fit-for-scale choice rather than something I am trying to hide.

A heavier database would add operational complexity without solving the main problems I have today. The migration trigger would be a real change in the product: more users, multiple guilds, significantly more history or enough concurrency that whole-file state updates become a limitation.

The rule has been to scale the architecture when the product needs it, not because a hypothetical future version might.

02.3Riot and OP.GG are complementary, not interchangeable

The hardest technical theme in Sisyphus is external truth. Riot is useful for live presence, account information and LP-related state. OP.GG provides ranked history and match-detail information that fills different gaps.

The problem is timing. A match can finish before every source reflects the new state. If the bot posts the first value it sees, LP can be wrong, an old match can appear as new, or a downtime recovery can flood the channel with stale recaps.

So the ingestion path is built around reconciliation: bounded first-run backfill, recap age caps, duplicate guards and delayed LP re-fetch after match completion.

External truth arrives late
Live detection
History + detail
LP re-fetch
Canonical outcome
Recap
FIG. 04 · RECONCILIATION · sisyphus/match_processing.py · processing states

02.4Private community data stays private

The raw bot state contains identifiers and operational information that should never become public just because the code is on GitHub. Historical state even included PUUIDs, Discord IDs, message/channel IDs and other private records.

The dashboard therefore consumes a sanitized, allowlisted export rather than raw bot state. Member identifiers are replaced by opaque keys, and dashboard access is scoped through Discord OAuth and guild membership.

Keep the squad private
PRIVATE BOT STATEPUUID · Discord ID · message ID
ALLOWLISTED EXPORTresult · lp_change · kda · vision
CHRONICLE ACCESSDiscord OAuth · guild membership
FIG. 05 · PRIVACY BOUNDARY · dashboard/export_model.py · SAFE_MATCH_FIELDS

The system grew from a match tracker into a stateful community product with reconciliation, live interaction, fictional markets, analytics and operations.

03.1From a tracked player to a recap

The bot polls tracked players for active Ranked Solo/Duo games. Riot live state helps identify active games, while OP.GG supplies recent ranked history and detail. The system identifies unseen matches, rejects stale recap candidates and re-fetches LP after a match to reduce timing errors.

Remakes under two minutes are canonicalized as draws. Related fictional markets are voided and refunded rather than settled as a normal win or loss. Otherwise the outcome updates history, records and market state before the recap is posted.

The recap itself is intentionally dense but social: result, rank/LP transition, KDA, CS/min, damage, gold, vision, items, team context and drill-down views. Friends use it both as a summary and as evidence in the inevitable "that loss was not my fault" argument.

One outcome, one recap
01 · MATCH FINAL
Juno · Ahri · Win +18 LP
02 · RECAP POSTED
Sisyphus-BotAPPToday at 9:41 PM
Juno#0000
Ahri champion icon

✅ Victory · Ahri (MID) · 30-25

Ranked Solo/Duo · 35m 42s
gold rank GOLD IV — 42 LP +18 LP

Core Line8/3/11 KDA · 6.33 ratio
212 CS · 7.1/min
Level 17
OutputDamage 24,812 · 31% share
Gold 13,420 · 24% share
Kill participation 63%
Map WorkVision score 27
Wards 12
Cleared 4
StoryClean win: Juno gained +18 LP on Ahri with 8/3/11.
Spotlight• Juno moved the boulder +18 LP.
• Juno joined 63% of team kills.
• Juno lit the map with 27 vision score.
ContextTeam kills 30 · Enemy kills 25
Damage share 31% · Gold share 24%
ItemsShadowflame · Blackfire Torch · Swiftmarch · Cosmic Drive · Oblivion Orb · Needlessly Large Rod
📊 Overview🔵 Blue Team🔴 Red Team🏅 Full LeaderboardRemember
03 · FULL SCOREBOARD
Sisyphus-BotAPPToday at 9:42 PM
Juno#0000
Ahri champion icon

🏅 Full Scoreboard · 35m 42s

🔵 Blue Team ✅ VictoryAhri 8/3/11 CS:212
🔴 Red Team ❌ DefeatRenekton 3/7/10 CS:254
📊 Overview🔵 Blue Team🔴 Red Team🏅 Full LeaderboardRemember
FIG. 06 · COMPLETED MATCH · sisyphus/views.py · interactive recap

03.2Queue Beacon to Live Game Room

One of the more product-like state transitions is the Queue Beacon. Instead of posting a new message for every phase, one Discord message can become the Live Game Room and update in place.

The room tracks the game, teams, watcher count, voice presence, streaming state and a final snapshot. Watchers are aggregated from the relevant voice channel and the tracked player is not counted as their own watcher.

When the game disappears, the room is finalized and the historical record can preserve things such as peak watchers and stream time.

One message changes in place
Sisyphus-BotAPPToday at 9:41 PM

Juno is pushing the boulder.

Queue Beacon

Juno is pushing the boulder on Renekton.

QueueRanked Solo/Duo
StreamNo stream yet
Watching0 watching
Watcher IntentNo watchers yet
Sisyphus-BotAPPToday at 9:41 PM

Juno is pushing the boulder.

Queue Beacon

Juno is pushing the boulder on Renekton.

QueueRanked Solo/Duo
StreamNo stream yet
Watching0 watching
Watcher IntentNo watchers yet
Sisyphus-BotAPPToday at 9:41 PM

Juno is pushing the boulder.

Queue Beacon

Juno is pushing the boulder on Renekton.

QueueRanked Solo/Duo
StreamStreaming
Watching2 watching
Watcher Intent🫡 1 · 💀 0 · 👀 1
Sisyphus-BotAPPToday at 9:41 PM

Juno is pushing the boulder.

Queue Beacon

Juno is pushing the boulder on Renekton.

QueueRanked Solo/Duo
StreamStreaming
Watching2 watching
Watcher Intent🫡 1 · 💀 0 · 👀 1
Finalized
FIG. 07 · QUEUE TO ROOM · sisyphus/live.py · Queue Beacon and Live Game Room

03.3Fictional markets and community state

The betting layer uses fictional points only. It exists because lightweight stakes make the squad pay attention to games they are already watching, not because I wanted to build a finance product.

Markets have explicit open, locked, settled and void/refund states. That state has to agree with the same canonical game outcome used by recaps and history, otherwise the social layer becomes inconsistent.

Profiles, records, goals, rivalries, memories and recurring recaps use the same accumulated history to turn repeated ranked games into a longer-running community record.

Explicit market states
Sisyphus-BotAPPToday at 10:17 PM

🔔 Market Resolution Notification!

✅ Market Settled — WIN

MarketNext Ranked Match — Wren
Tracked PlayerWren#0000
Total Staked450 pts
Winners1
ReasonMATCH_RESOLVED
THE BOULDER CHRONICLERANKED RECORD
THE BOULDER CHRONICLE / POINTS MARKETS

Points Markets

WalletsJuno · 650 pts; Kairo · 420 pts
MarketsOpen · locked · settled · void/refund
Market historyJuno · Ranked Solo/Duo SettledKairo · Ranked Solo/Duo Void / refunded
FIG. 08 · FICTIONAL POINTS · sisyphus/betting.py · dashboard/web/src/App.tsx betting

03.4Dashboard and sanitized export

The Boulder Chronicle dashboard is a separate FastAPI and React/Vite application. It does not read the raw bot state directly. A sanitized export defines the fields the dashboard is allowed to see and rejects forbidden keys.

The dashboard then turns that safe export into overview, player journeys, markets and community/archive views. Discord OAuth plus guild-membership checks keep the real dashboard private to the group.

Games recorded
THE BOULDER CHRONICLERANKED RECORD
THE BOULDER CHRONICLE / THE SQUAD LEDGER

The Squad Ledger

Tracked players13
Games recorded1,507as of 13 November 2026
Known-match LP+18 LP
Squad LP trajectoriesSolid recorded · dashed estimated

Compare players · Juno / Kairo / Wren

600 LP400 LP
19 May 2026
Juno Kairo Wren
Recorded LP · daily snapshots  View data table
Last resultsJuno · Win +18 LPKairo · Loss −12 LP
THE BOULDER CHRONICLERANKED RECORD
THE BOULDER CHRONICLE / PLAYER JOURNEYS

Player Journeys

Juno · LP journeyRank changes · match form
LP over timeSolid recorded · dashed estimated
600 LP400 LP
19 May 2026
Juno · recorded daily LP  Peak 600 LP · current 590 LP
THE BOULDER CHRONICLERANKED RECORD
THE BOULDER CHRONICLE / THE BOULDER ARCHIVE

The Boulder Archive

RecordsJuno · Ahri
KDA ratioJuno / Ahri / 29 August 20266.33 KDA
Vision scoreJuno / Ahri / 29 August 202627 vision
Known LP gainJuno / Ahri / 29 August 2026+18 LP
FIG. 09 · THE BOULDER CHRONICLE · dashboard/web/src/App.tsx overview · player · community

03.5Operations for a passion project

The bot runs on my Mac. The dashboard and uptime tooling run on Oracle infrastructure that I also administer. That split is pragmatic, but it also means I own the operational edges on both sides.

The project includes process locking, health snapshots, backup validation and rotation, supervisor restart/backoff, deployment coordination, status publishing and log monitoring. None of that makes Sisyphus "enterprise software"; it is simply what was needed to keep a small service I care about running reliably.

Own the edges
MAC BOTprivate state · process lock
SANITIZED EXPORTallowlisted fields
ORACLE DASHBOARDaccess · uptime · logs
FIG. 10 · RUNNING THE SERVICE · sisyphus/process_lock.py · dashboard/export_model.py

03.6Refactoring and hardening as the project grew

The original bot became harder to maintain as features accumulated, so I split the monolith into modules. The motivation was straightforward: make new features easier to add without turning every change into a risk to unrelated behaviour.

A separate hardening pass replaced an earlier dynamic evaluation path with a strict fail-closed AST allowlist for OP.GG-derived expressions. I treat that as security hardening, not as a story about a known exploit.

Mistral also exists in a very bounded place: summarizing Git commits for release notes with a local fallback and approval gate. It does not drive match tracking, markets or game decisions.

The outcome I care about is not hypothetical scale. It is that a joke turned into software the group actually uses.

04.1Live since May

Sisyphus went live on May 19, 2026. It has 13 tracked players and 1,507 games recorded as of 13 November 2026.

I use "games in view" deliberately rather than calling that a lifetime count. The live number is useful context, but the stronger outcome is that the system is still running and the squad still uses it.

Games recorded
THE BOULDER CHRONICLERANKED RECORD
THE BOULDER CHRONICLE / SISYPHUS STATUS

Sisyphus status

Live sinceMay 19, 2026
Tracked players13
Games recorded1,507as of 13 November 2026
FIG. 11 · LIVE SINCE MAY · dashboard/web/src/App.tsx overview · live count

04.2What the squad actually values

The features that matter are not always the most technically complicated ones. Friends use the win/loss and LP view, fictional betting, recaps and the dashboard. The recap is especially social because it gives everyone a shared artifact immediately after a game.

That is the product test I get with a friend-group project: if a feature does not become part of the routine, there is no abstract user story to hide behind.

04.3What I learned

Sisyphus taught me that small real products still need state correctness. External APIs can disagree or update at different times, and a wrong recap or LP change is immediately visible to the people using the bot.

It also reinforced a pragmatic architecture lesson. JSON is fine until it is not. A Mac plus an Oracle host is fine until the product needs something else. I would rather change those decisions in response to real scale than design a multi-guild platform nobody asked for.

For a small product, correctness and usefulness matter long before theoretical scale.

04.4If it grows

If Sisyphus eventually supports more guilds, much larger history or significantly more concurrent state, the storage and deployment model should change with it. A database and a more centralized service topology would make sense then.

For now, I want to preserve what made the project fun in the first place: it is software built around one group of people and the way they actually play together.