Documentation
Wiki
How BogaBot is used and how to stand up your own instance on your server. The bot is free to use: you clone the repo and run it with your own bot and your own tokens.
What it is
BogaBot is a modular Discord bot for a group of friends. The core module is the League of Legends ranking: link a Riot account → ingest matches from the Riot API → compute a configurable score per player → publish rankings in Discord. Around that there's a troll detector that judges every match, live alerts, sounds in voice channels and a points system you can redeem.
It's built in replaceable layers: storage sits behind an interface (today it's “Discord as a database”; moving to SQLite or Postgres is a new class), each module is a self-contained cog, and both the ranking formula and the troll detector are YAML files you can edit without touching code.
For players
If the bot is already on your server, the only thing you have to do is link yourself:
- Run
/link Name#TAGwith your full Riot ID (the name and tag you see in the LoL client, e.g.Faker#KR1). - The bot validates the account against the Riot API. If it exists, it's linked and your matches this week start counting.
- Use
/rankingwhenever you want to see the table, or wait for the automatic post every night.
Also
/trollsshows who's been trolling the most;/troll-analizartells you why your last match added (or didn't add) troll points./sonidomakes the bot join your voice channel and play a clip.- If you have the points role, you earn points hanging out in voice with friends and playing LoL. Check how many you have with
/puntosand redeem them for your own sound with/canjear-sonido. - If the server set up a commands channel, use them there. The ones marked 🔒 in
/helpreply only to you.
Requirements
Runtime | Python 3.12+ · discord.py[voice] · aiohttp |
|---|---|
ffmpeg | On the PATH, to play and measure the sounds (without it, everything else works) |
Discord account | With permission to create an Application and a bot |
Riot API key | Personal API Key (doesn't expire) or Development (expires every 24 h) |
Discord server | Where you invite the bot, with its own channels and roles |
Hosting | Local (python run.py) or a Linux server with systemd |
Setup on Windows / PowerShell: python -m venv venv → venv\Scripts\activate → pip install -r requirements.txt. On Linux: sudo apt install ffmpeg for the sounds.
Create the bot in Discord
Step 1
Create the bot in Discord
In the Developer Portal: New Application → Bot tab → Reset Token. That value goes in DISCORD_TOKEN. Leave the Privileged Gateway Intents off, the bot doesn't need them.
Step 2
Invite it to your server
OAuth2 → URL Generator. Scopes: bot and applications.commands. Permissions: View Channel, Send Messages, Read Message History and Embed Links; Connect and Speak for the sounds; Manage Roles only if you'll use LOL_ROLE_ID.
Step 3
Turn on developer mode
User Settings → Advanced → Developer Mode. With that, right-clicking any channel, role or server lets you copy its ID.
Step 4
Create channels and roles
At minimum a private storage channel, a public rankings channel, the general one and an administration role. The rest is optional (see the full table in the wiki).
Step 5
Get the Riot API key
At developer.riotgames.com → Register Product → Personal API Key: free and it doesn't expire (Riot approves it by hand). Meanwhile the Development Key works; it expires every 24 h and is renewed with /riot-key without restarting.
Step 6
Fill in the .env and run
With all the IDs and tokens, fill in .env.staging (or .env.production) and run python run.py. On the first start it registers the slash commands. For the sounds, ffmpeg has to be installed.
Permissions when inviting it
In OAuth2 → URL Generator, scopes bot and applications.commands. Bot permissions:
- View Channel, Send Messages, Read Message History and Embed Links on the storage, rankings, general, trolls and alerts channels.
- Connect and Speak for the sounds in voice channels.
- Manage Roles only if you're going to use
LOL_ROLE_ID(so the bot assigns that role on linking). - It needs no privileged intents: leave them off.
Channels and roles
With developer mode on (Settings → Advanced), right-clicking each channel or role lets you copy its ID. You also need the server ID for DISCORD_GUILD_ID.
| Create this | Type | Variable | What for |
|---|---|---|---|
| Private text channel (only the bot sees it) | channel | STORAGE_CHANNEL_ID | The bot uses it as a database: it stores JSON messages here. No one else should see it or write to it. |
| Public channel for the rankings | channel | RANKING_CHANNEL_ID | Daily ranking, the weekly "Trolls and Pros" recap and, if there's no trolls channel, the troll alerts. |
| The server's general channel | channel | GENERAL_CHANNEL_ID | It only gets one short line per historic blowout (15+ troll points). Nothing else. |
| Troll alerts channel · optional | channel | TROLL_CHANNEL_ID | Trolled games with their anecdote and the details, plus the daily troll ranking. If missing, they go to the rankings channel. |
| Match-finished alerts channel · optional | channel | MATCH_NOTIFY_CHANNEL_ID | A live alert for every group match: the 10 players and the troll-o-meter. |
| Bot commands channel · optional | channel | BOT_CHANNEL_ID | If you set it, regular members can only use commands there (or in the admin one). Devs, anywhere. |
| Channel for admin commands · optional | channel | ADMIN_CHANNEL_ID | Administration commands can only be run there, and it's where the Riot key expiry warnings land. |
| Bot log channel · optional | channel | LOG_CHANNEL_ID | The bot forwards its WARNING+ logs here (Riot down, expired key, etc.) on top of the console. |
| Role for whoever administers the bot | role | DEV_ROLE_ID | Enables the administration commands, and the sound and points commands marked as dev. |
| Player / group member role · optional | role | PLAYER_ROLE_ID | Only affects which sections /help and /ayuda show. |
| Role that earns points ("Boguero") · optional | role | POINTS_ROLE_ID | Whoever has it earns points in voice and by playing, and can redeem them. Without this role, the points module isn't loaded. |
| Role assigned automatically on linking · optional | role | LOL_ROLE_ID | On /link, the bot gives you this role (needs the Manage Roles permission). |
STORAGE_CHANNEL_ID channel is the bot's database (JSON messages). No one but the bot should see it or write to it.Riot API key
At developer.riotgames.com you generate the key. It goes in RIOT_API_KEY.
| Key type | Lifetime | When |
|---|---|---|
| Personal API Key | Doesn't expire. Requested under Register Product; Riot approves it by hand (it can take a few days) | The bot running for real. Set RIOT_KEY_TTL_HOURS=0 |
| Development | Expires every 24 h | For testing while the other one arrives. Renewed with /riot-key |
/riot-key <key> (dev role, in the admin channel) validates the new key against Riot, applies it without restarting and stores it in RIOT_KEY_FILE so it survives restarts. With no argument, it shows when the current one expires. With RIOT_KEY_TTL_HOURS above 0, the bot warns in the admin channel (tagging the dev role) RIOT_KEY_WARN_MINUTES before it expires and when it does.
Also set RIOT_PLATFORM and RIOT_REGION to your region. It defaults to LAS: RIOT_PLATFORM=la2, RIOT_REGION=americas (LAS, LAN and NA route to americas).
CRITICAL (with a 3 h cooldown) that reaches the log channel. If you switch to a key from another Riot app, puuids change (they're encrypted per app): the bot re-resolves them on its own by Riot ID.Environment variables
Everything comes from a .env file — the code never hardcodes secrets or IDs, and if a required variable is missing the bot won't start and says which one. None of the real .env.* files are committed.
Discord: token, channels and roles
| Variable | Description |
|---|---|
DISCORD_TOKEN | Bot token (Bot tab → Reset Token). Discord won't show it again. |
DISCORD_GUILD_ID | Server ID. Optional, but registers the slash commands instantly. |
STORAGE_CHANNEL_ID | Private channel the bot uses as a database. |
RANKING_CHANNEL_ID | Channel where it publishes the rankings. |
GENERAL_CHANNEL_ID | General channel: only historic blowouts go there. |
TROLL_CHANNEL_ID | Troll alerts and ranking channel (optional; defaults to RANKING_CHANNEL_ID). |
MATCH_NOTIFY_CHANNEL_ID | Match-finished alerts channel (optional). |
MATCH_POLL_INTERVAL_MINUTES | How many minutes between checks for new matches, for the alerts and troll warnings (default 5). |
BOT_CHANNEL_ID | Channel where regular members can use commands (optional). |
ADMIN_CHANNEL_ID | Channel where the administration commands run (optional). |
DEV_ROLE_ID | Role enabled for the administration commands. |
PLAYER_ROLE_ID | Player role; defines what /help and /ayuda show. |
LOL_ROLE_ID | Role the bot assigns when linking an account (optional). |
Riot API
| Variable | Description |
|---|---|
RIOT_API_KEY | Riot API key. In production, the Personal API Key (doesn't expire). Rotated on the fly with /riot-key. |
RIOT_PLATFORM | Region platform (LAS → la2). |
RIOT_REGION | Regional routing (LAS/LAN/NA → americas). |
RIOT_KEY_FILE | Where the key loaded with /riot-key is stored (default data/riot_key.json, mode 600). |
RIOT_KEY_TTL_HOURS | Key lifetime in hours for the reminder (default 24, the dev key). With the Personal API Key: 0 = never expires. |
RIOT_KEY_WARN_MINUTES | How many minutes before expiry the admin channel is warned (default 120). |
Schedule
| Variable | Description |
|---|---|
TIMEZONE | Time zone for the day/week cutoffs (default America/Argentina/Buenos_Aires). |
DAILY_POST_HOUR / DAILY_POST_MINUTE | Local time of the daily job (default 10:00). 23:55 recommended, so today's ranking isn't empty. |
Config and state files
| Variable | Description |
|---|---|
SCORING_CONFIG_PATH | Ranking formula (default config/scoring.yaml). |
TROLLS_CONFIG_PATH | Troll detector thresholds and points (default config/trolls.yaml). |
TROLLS_STATE_FILE | Since when the troll ranking counts; written by /trolls-reiniciar (default data/trolls_state.json). |
Logs
| Variable | Description |
|---|---|
LOG_LEVEL | Console log level (default INFO). |
LOG_CHANNEL_ID | Channel where the bot sends its own logs (optional). |
LOG_CHANNEL_LEVEL | Minimum level forwarded to that channel (default WARNING). |
Sounds
| Variable | Description |
|---|---|
SOUNDS_ENABLED | Random sounds in voice channels (default true). /sonido works even when it's off. |
SOUNDS_DIR | Folder with the audio files, outside git (default sounds/). |
SOUNDS_CHANCE | Chance that one plays on each check, from 0 to 1 (default 0.15). |
SOUNDS_CHECK_INTERVAL_MINUTES | How often the dice are rolled (default 5). |
SOUNDS_COOLDOWN_MINUTES | Minimum gap between two random sounds (default 45). |
SOUNDS_ACTIVE_FROM / SOUNDS_ACTIVE_TO | Hours when they play (0-23; can wrap past midnight). 0 and 0 = all day. |
Points and redemptions
| Variable | Description |
|---|---|
POINTS_ROLE_ID | Role that earns and redeems points. Without it, the points module isn't loaded. |
POINTS_FILE | Where points are stored (default data/points.json). |
POINTS_VOICE_INTERVAL_MINUTES / POINTS_VOICE_AMOUNT | Points for being in voice: how many and how often (default 1 every 5 min). |
POINTS_VOICE_DAILY_CAP | Daily cap on voice points (default 60). |
POINTS_LOL_GAME / POINTS_LOL_WIN | Points per stored LoL match and extra if you won it (default 5 and 5). |
SOUND_REDEEM_COST | What redeeming a sound costs (default 100). |
SOUND_REDEEM_DAYS | Days a redeemed sound lasts (default 7). |
SOUND_REDEEM_MAX_SECONDS | Maximum length of a redeemed clip (default 8 s). |
SOUND_REDEEM_MAX_ACTIVE | Active redeemed sounds per person (default 1). |
Staging vs. production
The environment is chosen by the shell variable BOGABOT_ENV (set it in the terminal before running, *not* inside the .env):
| BOGABOT_ENV | Loads | Use |
|---|---|---|
unset or staging | .env.staging | Default. Development and testing. |
production | .env.production | The real bot, on the real server. |
# local, default staging python run.py # explicit $env:BOGABOT_ENV = "production" python run.py # with the script (console + log in logs\) .\scripts\run_bot.ps1 -Environment production
The default is staging on purpose: if you forget to set the variable, you never run against production by accident. Recommended: staging points at a different bot and a different server, not the same one, because storage lives in Discord channels and you'd mix test data with real data.
Commands
If BOT_CHANNEL_ID is set, regular members can only use commands in that channel (or the admin one); devs, anywhere. The administration commands (/link-admin, /unlink-admin, /ingest-now, /riot-key, /trolls-reiniciar, /trolls-recalcular) also require the admin channel.
🎮 LoL ranking
| Command | What it does | Who |
|---|---|---|
/link <Name#TAG> | Links your Riot account to your Discord. It's validated against the Riot API; if the Riot ID doesn't exist, nothing is stored. | everyone |
/unlink | Unlinks your account. Your matches stop counting toward the ranking. | everyone |
/ranking [Today | Week] | Shows the group ranking on demand, without waiting for the automatic post. Only Ranked games count (Flex and Solo/Duo). | everyone |
/link-admin <user> <Name#TAG> | Links another server user's Riot account. | dev role |
/unlink-admin <user> | Unlinks another server user's account. | dev role |
/ingest-now | Forces a match ingestion. It's idempotent: the dedup by (match_id, discord_id) skips what's already stored. | dev role |
/riot-key [key] | 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. | dev role |
🤡 Trolls
| Command | What it does | Who |
|---|---|---|
/trolls [Week | Last week | Month | All-time] | Troll ranking by index (average troll points per match): tier, specialty, worst game and trend versus the previous period. | everyone |
/troll-analizar [user] [match] | Judges a match (the latest stored one by default): which troll charges it has, how many points each adds and why. | everyone |
/trolls-reglas | What the bot detects and how many points each thing adds, with the active config. | everyone |
/trolls-reiniciar | The troll ranking starts from zero as of now. Old matches stay stored. | dev role |
/trolls-recalcular | 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. | dev role |
🔊 Sounds
| Command | What it does | Who |
|---|---|---|
/sonido [name] | The bot joins your voice channel, plays the sound (or a random one) and leaves. | everyone |
/sonidos | Lists the loaded sounds. | everyone |
/canjear-sonido <name> <audio> | Trade points to upload a temporary sound of your own (100 points, up to 8 s, lasts 7 days by default). | points role |
/sonido-del <name> | Deletes a sound. The owner can delete the one they redeemed; a dev, any of them. | points role |
/sonido-add <name> <audio> | Uploads a permanent sound (mp3, ogg or wav, up to 2 MB). | dev role |
/sonido-forzar [name] [channel] | The bot joins a voice channel and plays that sound. If another one is playing, it waits its turn. | dev role |
💰 Points
| Command | What it does | Who |
|---|---|---|
/puntos [user] | How many points you have (or someone else) and how they're earned. | everyone |
/puntos-top | The server's points leaderboard. | everyone |
/puntos-dar <user> <amount> | Adds or removes points from someone (negative amount to subtract). | dev role |
ℹ️ Help
| Command | What it does | Who |
|---|---|---|
/help · /ayuda | List the commands you can use based on your roles. They're two names for the same thing and only you see the reply. | everyone |
How the ranking is calculated
- Each match is stored as one record per player, but only if you played it alongside at least one other linked member of the group. Solo games are discarded on ingestion (it checks
metadata.participantsof the match-v5 JSON). - It excludes remakes (under 5 min or early surrender).
- The daily and weekly rankings count Ranked only (Solo/Duo and Flex). Normals, ARAM and rotating modes are still stored (they feed the troll detector and the points), but don't add to the ranking.
- The windows are day and week from Monday 00:00 local time (
TIMEZONE, default Buenos Aires). - In each window the stats are aggregated per player and the scoring engine applies the formula from
config/scoring.yaml. - The daily job runs at
DAILY_POST_HOUR:DAILY_POST_MINUTE(23:55 recommended): ingestion → the day's ranking → if there were trolled games, how the week's troll ranking is going. On Mondays it also posts the weekly “Trolls and Pros” recap for the week that just closed — counting Ranked Flex only (queue 440), to measure the group's serious play — and crowns the Troll of the week. - Dedup by
(match_id, discord_id)with no cursor: each run asks “since Monday” (with a one-day margin, for matches that cross midnight) and skips what's already stored.
Scoring engine
The formula lives in config/scoring.yaml and is edited without touching code. For each player every metric is computed (averaged per match), normalized across all players, and the final score is the sum of weight × normalized_value.
aggregation | per_game_average — per-match average — quality over quantity: playing lots of mediocre games doesn't lift you |
|---|---|
normalization | zscore — (value − mean) / stdev, so no metric dominates just by having bigger numbers |
normalization accepts zscore (recommended), minmax (0..1 scale between worst and best) or none (raw value; weights are calibrated by hand).
Metrics active by default
| Metric | Weight | What it measures |
|---|---|---|
carry_rate | 5.0 | share of carried games (win + KDA ≥ 5) |
troll_rate | 4.0 ↓ | share of trolled games (KDA < 0.5) |
kda | 3.0 | (kills + assists) / deaths |
win_rate | 2.0 | win percentage |
avg_deaths | 1.5 ↓ | average deaths per match |
damage_per_min | 1.0 | damage to champions per minute |
avg_vision | 0.5 | average vision score |
↓ = lower is better (the normalized value is inverted). A match is carried if you won it with KDA ≥ 5 and trolled if you finished with KDA < 0.5. The engine also supports these metrics, off by default — enable them by adding them to the YAML: avg_kills, avg_assists, cs_per_min, games_played.
Troll detector
After every match, each group player is judged with the rules below. The ones marked timeline use one more Riot query (what happened and when); the rest come from the match stats. Everything is tuned in config/trolls.yaml without touching code (restarting the bot), and since the troll ranking is computed on the fly, a change there recalculates the history too.
How points add up
- Each rule that's met is a charge with its troll points.
- In ranked (Solo/Duo or Flex) points are multiplied by ×1.25; if the team won anyway, by ×0.5 (they carried them).
- Minor charges (🔸 in the table: poor performance, low KP, low damage, lost lane, vision, farm, first blood, FF…) add up to 4 at most between all of them. A trolled game takes a strong signal: AFK, 0 kills and 0 assists, sold items, letting the base fall, feeding, a throw or being the team's anchor.
- Thresholds change by mode: ARAM and chaotic modes (URF, One for All…) tolerate more deaths, and the lane and base rules are Summoner's Rift only.
- Rules that measure the same thing don't stack: feeder replaces tragic KDA and time dead; blind replaces no control wards.
What happens depending on the match's points
| Points | What happens |
|---|---|
| under 8 | Nothing extra: adds to the troll ranking and shows in the match alert's troll-o-meter. |
| 8 or more | 🤡 Trolled game: in TROLL_CHANNEL_ID (or the rankings one), a line with the anecdote tagging the player, plus the compact details. |
| 15 or more | 💀 Blowout: also, one short line in `GENERAL_CHANNEL_ID`. It's the only thing that reaches #general. |
Only matches from the last 36 h are announced (alert_max_age_hours): if the bot was down or someone just linked, old games aren't posted.
Troll ranking (/trolls)
It sorts by troll index — points per match —, not by the total, so playing more doesn't add up: whoever trolls hard in 2 matches ranks above whoever played 18 and trolled 2. So a single match doesn't decide by chance, each player starts with 2 “ghost” matches worth the group average (index.prior_games) and one match counts 30 points at most (index.max_game_points). It shows the tier, the share of trolled matches and the trend versus the previous period (📈 / 📉 / ➡️ / 🆕). /trolls-reiniciar resets it to zero from that moment.
Tiers (by index)
The rules
| Rule | Pts | When |
|---|---|---|
| 💤 AFK timeline | 8 | Minutes in a row standing still, alive and gaining no experience. |
| 👻 ¿Estaba AFK? | 5 | 0 kills and 0 assists in a game where their team did get kills. |
| 💸 Liquidación total timeline | 5 | Sold 6 or more items during the game (inting). |
| 🏚️ Nos tiraban la base timeline | 5 | Inhibitors or nexus turrets fell while they were alive and far away, and not split pushing. +3 if they were farming. |
| 🍽️ Feeder profesional | 3 | Deaths relative to game length (≈3.3 per 10 min, at least 8) with KDA < 1. Extra points for every 2 deaths over. |
| 💥 Throw timeline | 3 | Caught alone, with no fight around, and within a minute the team lost Baron, Elder or the nexus. |
| ⚓ Ancla del equipo | 2 | Died more than the other four on their team combined (at least 7 deaths). |
| 📉 KDA de la vergüenza | 2 | KDA under 0.5 with at least 5 deaths (not added if already a feeder). |
| ⚰️ Veraneando en la fuente | 2 | Spent a quarter of the game or more dead. |
| ⏰ Speedrun de muertes timeline | 2 | 3 or more deaths before minute 10. |
| 🚜 Le pasaron el trapo en línea timeline | 2 | 2,500 gold behind their lane opponent at minute 15 (1,500 for supports). |
| 🎁 Delivery a domicilio timeline | 2 | 4 or more deaths to their lane opponent, making up a good share (40%+) of all their deaths. |
| 🪶 Daño de cotillón | 2 | Under 10% of their team's damage, unless they were tanking. |
| 🏝️ Jugando otra partida | 2 | Took part in under 20% of their team's kills. |
| 🩸 Regaló la primera sangre timeline | 1 | First death of the game before minute 5 (+1 before minute 3). |
| 🕊️ Pacifista | 1 | 0 kills in 20 minutes or more, with low damage (not for supports). |
| 🙈 Ciego voluntario | 1 | Very low vision score per minute (supports are held to a higher bar). |
| 🧿 Ni un control ward | 1 | Zero control wards in 25 minutes, and weak vision on top. |
| 🌾 Alérgico al farm | 1 | Low farm per minute for their role, unless they carried with damage. |
| 🤖 Ejecutado timeline | 1 | 2 or more deaths to turrets, minions or monsters. |
| 💰 Ahorrista timeline | 1 | Died holding more than 3,000 gold, before minute 25. |
| 🏳️ FF al 15 | 2 | Lost by surrender before minute 20. Aggravating only: it counts if they already have a charge of their own. |
| 🧹 Barrida histórica | 1 | Lost by 20 or more kills. Aggravating only, like the FF. |
| ❓ Tóxico del '?' | 1 | 15 or more "?" pings. |
| 🥄 Cuchara de madera | 2 | Last place in Arena. |
/trolls-recalcular forces the same by hand.Live alerts and logs
Match finished
If MATCH_NOTIFY_CHANNEL_ID is set, every MATCH_POLL_INTERVAL_MINUTES (5 by default) the bot checks for new matches and, for each one that counts, posts a board with the 10 positions, tagging the group's players who were in it (with each one's result, in case they ended up on opposing teams) and a troll-o-meter with everyone's troll points.
Alerts come out of any ingestion — the periodic check, the daily job or /ingest-now — and once per match: ingestion holds a lock and the dedup means running everything together duplicates nothing.
Logs to Discord
If LOG_CHANNEL_ID is set, logs at level LOG_CHANNEL_LEVEL (WARNING by default) or higher are forwarded to that channel too, on top of the console. Meant to let you catch errors (Riot down, expired key) without watching the terminal. Network errors against Riot are retried with backoff before warning.
Sounds
The bot joins a voice channel, plays a short clip and leaves. One playback at a time across the whole bot, and any clip is cut at 20 s just in case. It needs ffmpeg installed and the Connect and Speak permissions.
Three ways to trigger one
- On demand:
/sonido [name]in your voice channel (empty = a random one). - Forced (dev):
/sonido-forzar [name] [channel]— in the channel you pick, in yours or in a random one with people. If another one is playing, it waits its turn. - At random: every
SOUNDS_CHECK_INTERVAL_MINUTES(5) the bot rolls a die withSOUNDS_CHANCE(15%). If it hits, within the active hours and past the cooldown (SOUNDS_COOLDOWN_MINUTES, 45), it picks a channel with at least one person (the AFK one doesn't count) and a random clip. Turned off withSOUNDS_ENABLED=false.
How they're loaded
/sonido-add(dev): permanent. mp3, ogg or wav, up to 2 MB./canjear-sonido(points role): temporary, paid with points. See below.- Clips live in
SOUNDS_DIR, outside git. Owner and expiry of redeemed ones are kept inSOUNDS_DIR/_meta.json; a sound with no entry there is permanent.
Points
Members with POINTS_ROLE_ID earn points and redeem them. Without that role configured, the module isn't loaded.
How they're earned
- Voice: 1 point every 5 minutes to each member with the role who's in a voice channel with at least one other human (the AFK channel and being deafened don't count). Cap: 60 per day.
- LoL: 5 for each new match stored and 5 more if you won it. The ingestion dedup guarantees each match pays out only once.
- By hand: a dev can add or subtract with
/puntos-dar.
Redeem: your own sound
/canjear-sonido <name> <audio> costs 100 points: the clip can be up to 8 s, stays loaded for 7 days and is deleted on its own when it expires. At most 1 active per person (you can delete it earlier with /sonido-del). The bot measures the length with ffprobe and only charges if the clip passes the check, then announces the redemption in the channel. Every number is changed through environment variables.
Deploy
Local
python run.py directly, or .\scripts\run_bot.ps1 [-Environment production], which opens a visible console and mirrors everything to logs\<environment>\. Don't start two instances against the same environment/server: they'd answer commands twice.
Linux server (systemd)
The repo ships deploy/bogabot.service. Setup is one-time: clone, venv + pip install -r requirements.txt, install ffmpeg, copy the .env.production over scp (mode 600), install the service and systemctl enable --now bogabot. State (points, Riot key, troll ranking reset) lives in data/, outside git.
sudo apt install ffmpeg
sudo cp deploy/bogabot.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now bogabot
sudo journalctl -u bogabot -f # live logsTo update: git pull + pip install if requirements.txt changed + systemctl restart bogabot. Changing the Riot key needs no deploy: /riot-key from Discord. The repo also ships a GitHub Actions workflow that deploys every push to main that passes the tests, meant for when there's a VPS.
Status and limits
- Scope: beta with the group. LoL (ranking and trolls), sounds, points and help modules. No AI features yet — they're in the backlog as a separate cog.
- Storage: today it's Discord (JSON messages in a private channel + an in-memory index). It's O(n) messages; moving to a real DB is a new class in
storage/and one line inbot.py. Points and redeemed sounds go to JSON files indata/andsounds/. - Queues: the daily and weekly ranking counts Ranked only (Solo/Duo and Flex); the “Trolls and Pros” recap, Flex only. The troll detector and the points look at every queue.
- Region: decided for LAS (
la2/americas); configurable for others. - Tests:
python -m pytest— scoring, dedup, match-v5 mapper with timeline, troll rules, config, troll ranking, ingestion and alert routing.
Bugs, ideas and requests → the repo's issues.