Skip to content

Discord bot · Beta with the group · LoL, trolls, sounds and points

BogaBot

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.

Read the wikiSource code ↗See commands
Python · discord.py[voice] · aiohttpLAS (la2 / americas) by default, configurable per regionDiscord as a databaseYAML scoring and trollsFree to use — clone the repo and run it on your server

On the server

What it looks like in Discord

Real, untouched screenshots from the group's server: what the bot posts after a match and when someone asks for the ranking.

Carry + /ranking
When someone carries the team, the bot celebrates it with every play and what it scored. Below, /ranking with the day's table: score, KDA, damage, vision and farm per player.
Match summary
After every Ranked game with people from the group: both teams, the lane-by-lane matchup and the two meters, troll-o-meter and carry-o-meter.

What it does

A ranking that builds itself, and a bit more

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.

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).

Configurable score

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.

Troll detector

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.

Live alerts

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.

Voice sounds

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.

Redeemable points

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.

Hot-swappable Riot key

/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.

Discord as a database

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.

Modular by cogs

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

Trolls and blowouts

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

Nothing extra

Adds to the troll ranking and shows in the match alert's troll-o-meter.

8+

🤡 Trolled game

In the trolls channel: a line with the anecdote tagging the player, plus the details.

15+

💀 Blowout

Also, a single line in #general: “Historic troll! @player they were taking our base and he was farming…”.

See the 25 rules in the wiki →

Reference

Commands

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.

🎮 LoL ranking

/link<Name#TAG>everyone

Links your Riot account to your Discord. It's validated against the Riot API; if the Riot ID doesn't exist, nothing is stored.

/unlinkeveryone

Unlinks your account. Your matches stop counting toward the ranking.

/ranking[Today | Week]everyone

Shows the group ranking on demand, without waiting for the automatic post. Only Ranked games count (Flex and Solo/Duo).

/link-admin<user> <Name#TAG>dev role

Links another server user's Riot account.

/unlink-admin<user>dev role

Unlinks another server user's account.

/ingest-nowdev role

Forces a match ingestion. It's idempotent: the dedup by (match_id, discord_id) skips what's already stored.

/riot-key[key]dev role

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.

🤡 Trolls

/trolls[Week | Last week | Month | All-time]everyone

Troll ranking by index (average troll points per match): tier, specialty, worst game and trend versus the previous period.

/troll-analizar[user] [match]everyone

Judges a match (the latest stored one by default): which troll charges it has, how many points each adds and why.

/trolls-reglaseveryone

What the bot detects and how many points each thing adds, with the active config.

/trolls-reiniciardev role

The troll ranking starts from zero as of now. Old matches stay stored.

/trolls-recalculardev role

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.

🔊 Sounds

/sonido[name]everyone

The bot joins your voice channel, plays the sound (or a random one) and leaves.

/sonidoseveryone

Lists the loaded sounds.

/canjear-sonido<name> <audio>points role

Trade points to upload a temporary sound of your own (100 points, up to 8 s, lasts 7 days by default).

/sonido-del<name>points role

Deletes a sound. The owner can delete the one they redeemed; a dev, any of them.

/sonido-add<name> <audio>dev role

Uploads a permanent sound (mp3, ogg or wav, up to 2 MB).

/sonido-forzar[name] [channel]dev role

The bot joins a voice channel and plays that sound. If another one is playing, it waits its turn.

💰 Points

/puntos[user]everyone

How many points you have (or someone else) and how they're earned.

/puntos-topeveryone

The server's points leaderboard.

/puntos-dar<user> <amount>dev role

Adds or removes points from someone (negative amount to subtract).

ℹ️ Help

/help · /ayudaeveryone

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

How the ranking is calculated

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

5.0carry_rate — share of carried games (win + KDA ≥ 5)
4.0troll_rate — share of trolled games (KDA < 0.5) (lower is better)
3.0kda — (kills + assists) / deaths
2.0win_rate — win percentage
1.5avg_deaths — average deaths per match (lower is better)
1.0damage_per_min — damage to champions per minute
0.5avg_vision — average vision score

It's tuned without touching code: change the weights in the YAML and the next /ranking comes out with the new formula.

Automatic job once a day (23:55 local time recommended): ingestion → the day's ranking. On Mondays, also the weekly “Trolls and Pros” recap for the week that just closed (Ranked Flex only) and the crowning of the Troll of the week.