Daily and weekly ranking
A once-a-day job ingests the group's matches and posts the day's ranking, counting Ranked only (Flex and Solo/Duo). On Mondays, the weekly "Trolls and Pros" recap for the week that just closed (Ranked Flex only).
Discord bot · Beta with the group · LoL, trolls, sounds and points
League of Legends ranking, troll detector and sounds for your Discord group. It links Riot accounts, ingests matches from the Riot API and posts daily and weekly Ranked leaderboards in Discord. It judges every match with ~25 rules to catch trolled games, alerts live when a match ends, plays sounds in voice channels and hands out redeemable points.
A bogalabs product — a group of programmer friends building stuff for fun.
On the server
Real, untouched screenshots from the group's server: what the bot posts after a match and when someone asks for the ranking.
/ranking with the day's table: score, KDA, damage, vision and farm per player.What it does
The bot links the group's Riot accounts, reads matches from the API, computes a score per player and posts it in Discord. On top of that: it judges every match to find the trolls, plays sounds in voice channels and hands out points you can redeem.
A once-a-day job ingests the group's matches and posts the day's ranking, counting Ranked only (Flex and Solo/Duo). On Mondays, the weekly "Trolls and Pros" recap for the week that just closed (Ranked Flex only).
The formula lives in config/scoring.yaml: it rewards the share of carried games and punishes the share of trolled ones, plus KDA, wins, deaths, damage and vision. Tuned without touching code.
Every match is judged with ~25 rules, from the timeline and the stats: AFK, feeding, sold items, "they were taking our base", throws… It takes a strong signal: a weak game is not a trolled game.
Every few minutes the bot looks for new matches and posts the 10-player board with the troll-o-meter, tagging the group's players. Historic blowouts land in #general.
The bot joins a voice channel, plays a short clip and leaves: on demand with /sonido or at random every so often, with a configurable cooldown and schedule.
Members with the points role earn them by hanging out in voice and by playing LoL (more if they win), and redeem them to upload their own sound for a few days.
/riot-key validates and applies a new key without restarting, and the bot warns in the admin channel before it expires. With the Personal API Key there's nothing to touch.
Storage sits behind an interface; today it stores JSON messages in a private channel. Moving to SQLite or Postgres is writing a class and changing one line.
LoL, sounds, points and help are separate cogs. Adding a feature is dropping a folder into modules/ and registering it, without touching what already works.
The detector
After every match, each group player is judged with ~25 rules: some read the timeline (what happened and when), others the stats. Each charge adds troll points; in ranked they weigh ×1.25 and if the team won anyway, ×0.5. A weak game isn't enough: minor charges add up to 4 at most, so trolling takes a strong signal.
< 8
Adds to the troll ranking and shows in the match alert's troll-o-meter.
8+
In the trolls channel: a line with the anecdote tagging the player, plus the details.
15+
Also, a single line in #general: “Historic troll! @player they were taking our base and he was farming…”.
Reference
Slash commands, grouped by module. Dev role ones require the administration role (and the admin ones, the admin channel too); points role ones, the role that earns points.
Links your Riot account to your Discord. It's validated against the Riot API; if the Riot ID doesn't exist, nothing is stored.
Unlinks your account. Your matches stop counting toward the ranking.
Shows the group ranking on demand, without waiting for the automatic post. Only Ranked games count (Flex and Solo/Duo).
Links another server user's Riot account.
Unlinks another server user's account.
Forces a match ingestion. It's idempotent: the dedup by (match_id, discord_id) skips what's already stored.
Loads a new RIOT_API_KEY without restarting the bot (validates and stores it). With no key, shows when the current one expires. The reply is ephemeral: the key never stays visible.
Troll ranking by index (average troll points per match): tier, specialty, worst game and trend versus the previous period.
Judges a match (the latest stored one by default): which troll charges it has, how many points each adds and why.
What the bot detects and how many points each thing adds, with the active config.
The troll ranking starts from zero as of now. Old matches stay stored.
Re-fetches from Riot the stored matches analyzed with an old version to fill in stats and timeline. It runs by itself on startup; this forces it by hand, silently.
The bot joins your voice channel, plays the sound (or a random one) and leaves.
Lists the loaded sounds.
Trade points to upload a temporary sound of your own (100 points, up to 8 s, lasts 7 days by default).
Deletes a sound. The owner can delete the one they redeemed; a dev, any of them.
Uploads a permanent sound (mp3, ogg or wav, up to 2 MB).
The bot joins a voice channel and plays that sound. If another one is playing, it waits its turn.
How many points you have (or someone else) and how they're earned.
The server's points leaderboard.
Adds or removes points from someone (negative amount to subtract).
List the commands you can use based on your roles. They're two names for the same thing and only you see the reply.
The score
Only Ranked matches count (Flex and Solo/Duo) played with at least one other linked member of the group; solo games and remakes are discarded. In each window (day, or week from Monday) the engine applies the formula from config/scoring.yaml: it normalizes each metric with zscore and weights it. Everything is a per-match average or rate: quality over quantity.
Default weights
carry_rate — share of carried games (win + KDA ≥ 5)troll_rate — share of trolled games (KDA < 0.5) (lower is better)kda — (kills + assists) / deathswin_rate — win percentageavg_deaths — average deaths per match (lower is better)damage_per_min — damage to champions per minuteavg_vision — average vision scoreIt's tuned without touching code: change the weights in the YAML and the next /ranking comes out with the new formula.