# TipiLAN Bot - Developer Reference ## File Structure | File | Purpose | |---|---| | `bot.py` | All Discord commands, views (UI components), event handlers, reminder system | | `economy.py` | All economy logic, data model, constants (SHOP, COOLDOWNS, LEVEL_ROLES, etc.) | | `pb_client.py` | Async PocketBase REST client - auth token cache, CRUD on `economy_users` collection | | `strings.py` | **Single source of truth for all user-facing text.** Edit here to change any message. | | `sheets.py` | Google Sheets integration (member sync) | | `member_sync.py` | Birthday/member sync background task | | `config.py` | Environment variables (TOKEN, GUILD_ID, PB_URL, etc.) | | `scripts/migrate_to_pb.py` | One-time utility: migrate `data/economy.json` → PocketBase | --- ## Adding a New Economy Command Checklist - do all of these, in order: 1. **`economy.py`** - add the `do_` async function with cooldown check, logic, `_commit`, and `_txn` logging 2. **`economy.py`** - add the cooldown to `COOLDOWNS` dict if it has one 3. **`economy.py`** - add the EXP reward to `EXP_REWARDS` dict 4. **`strings.py` `CMD`** - add the slash command description 5. **`strings.py` `OPT`** - add any parameter descriptions 6. **`strings.py` `TITLE`** - add embed title(s) for success/fail states 7. **`strings.py` `ERR`** - add any error messages (banned, cooldown uses `CD_MSG`, jailed uses `CD_MSG["jailed"]`) 8. **`strings.py` `CD_MSG`** - add cooldown message if command has a cooldown 9. **`strings.py` `HELP_CATEGORIES["tipibot"]["fields"]`** - add the command to the help embed 10. **`bot.py`** - implement the `cmd_` function, handle all `res["reason"]` cases 11. **`bot.py`** - call `_maybe_remind` if the command has a cooldown and reminders make sense 12. **`bot.py`** - call `_award_exp(interaction, economy.EXP_REWARDS[""])` on success 13. **`strings.py` `REMINDER_OPTS`** - add a reminder option if the command needs one 14. **`bot.py` `_maybe_remind`** - if the command has an item-modified cooldown, add an `elif` branch --- ## Adding a New Shop Item Checklist: 1. **`economy.py` `SHOP`** - add the item dict `{name, emoji, cost, description: strings.ITEM_DESCRIPTIONS["key"]}` 2. **`economy.py` `SHOP_TIERS`** - add the key to the correct tier list (1/2/3) 3. **`economy.py` `SHOP_LEVEL_REQ`** - add minimum level if it is T2 (≥10) or T3 (≥20) 4. **`strings.py` `ITEM_DESCRIPTIONS`** - add the item description (Estonian flavour + English effect) 5. **`strings.py` `HELP_CATEGORIES["shop"]["fields"]`** - add display entry (sorted by cost) 6. If the item modifies a cooldown: - **`economy.py`** - add the `if "item" in user["items"]` branch in the relevant `do_` function - **`bot.py` `_maybe_remind`** - add `elif cmd == "" and "" in items:` branch with the new delay - **`bot.py` `cmd_cooldowns`** - add the item annotation to the relevant status line --- ## Adding a New Level Role 1. **`economy.py` `LEVEL_ROLES`** - add `(min_level, "RoleName")` in descending level order (highest first) 2. **`bot.py` `_ensure_level_role`** - no changes needed (uses `LEVEL_ROLES` dynamically) 3. Run **`/economysetup`** in the server to create the role and set its position --- ## Adding a New Admin Command 1. **`strings.py` `CMD`** - add `"[Admin] ..."` description 2. **`strings.py` `HELP_CATEGORIES["admin"]["fields"]`** - add the entry 3. **`bot.py`** - add `@app_commands.default_permissions(manage_guild=True)` and `@app_commands.guild_only()` --- ## Economy System Design ### Storage All economy state is stored in **PocketBase** (`economy_users` collection). `pb_client.py` owns all reads/writes. Each `do_*` function in `economy.py` calls `get_user()` → mutates the local dict → calls `_commit()`. `_commit` does a `PATCH` to PocketBase. ### Currency & Income Sources | Command | Cooldown | Base Earn | Notes | |---|---|---|---| | `/daily` | 20h (18h w/ korvaklapid) | 150⬡ | ×streak multiplier, ×2 w/ lan_pass, +5% interest w/ gaming_laptop | | `/work` | 1h (40min w/ monitor) | 15-75⬡ | ×1.5 w/ gaming_hiir, ×1.25 w/ reguleeritav_laud, ×3 30% chance w/ energiajook | | `/beg` | 5min (3min w/ hiirematt) | 10-40⬡ | ×2 w/ klaviatuur | | `/crime` | 2h | 200-500⬡ win | 60% success (75% w/ cat6), +30% w/ mikrofon; fail = fine + jail | | `/rob` | 2h | 10–25% of target | 45% success (60% w/ jellyfin); fail = fine; house rob: 35% success, 5–40% jackpot | | `/slots` | - | varies | jackpot=10× (15× w/ monitor_360), triple=4× (6×), pair=1× | | `/roulette` | - | 2× red/black, 14× green | 1/37 green chance | | `/blackjack` | - | 1:1 win, 3:2 BJ, 2:1 double | Dealer stands on 17+; double down on first action only | ### "all" Keyword Commands that accept a coin amount (`/give`, `/roulette`, `/rps`, `/slots`, `/blackjack`) accept `"all"` to mean the user's full current balance. Parsed by `_parse_amount(value, balance)` in `bot.py`. ### Daily Streak Multipliers - 1-2 days: ×1.0 (150⬡) - 3-6 days: ×1.5 (225⬡) - 7-13 days: ×2.0 (300⬡) - 14+ days: ×3.0 (450⬡) - `karikas` item: streak survives missed days ### Jail - Duration: 30 minutes (`JAIL_DURATION`) - `gaming_tool`: prevents jail on crime fail - `/jailbreak`: 3 dice rolls, need doubles to escape free. On fail - bail = 20-30% of balance, min 350⬡. If balance < 350⬡, player stays jailed until timer. - **Blocked while jailed**: `/work`, `/beg`, `/crime`, `/rob`, `/give` (checked in `do_*` functions via `_is_jailed`) ### EXP Rewards (from `EXP_REWARDS` in economy.py) EXP is awarded on every successful command use. Level formula: `floor(sqrt(exp / 10))`, so Level 5 = 250 EXP, Level 10 = 1000, Level 20 = 4000, Level 30 = 9000. --- ## Role Hierarchy (Discord) Order top to bottom in server roles: ``` [Bot managed role] ← bot's own role, always at top of our stack ECONOMY ← given to everyone who uses any economy command TipiLEGEND ← level 30+ TipiCHAD ← level 20+ TipiHUSTLER ← level 10+ TipiGRINDER ← level 5+ TipiNOOB ← level 1+ ``` Run `/economysetup` to auto-create all roles and set their positions. The command is idempotent - safe to run multiple times. Role assignment: - **ECONOMY** role: given automatically on first EXP award (i.e. first successful economy command) - **Level roles**: given/swapped automatically on level-up; synced on `/rank` --- ## Shop Tiers & Level Requirements | Tier | Level Required | Items | |---|---|---| | T1 | 0 (any) | gaming_hiir, hiirematt, korvaklapid, lan_pass, anticheat, energiajook, gaming_laptop | | T2 | 10 | reguleeritav_laud, jellyfin, mikrofon, klaviatuur, monitor, cat6 | | T3 | 20 | monitor_360, karikas, gaming_tool | Shop display is sorted by cost (ascending) within each tier. The `SHOP_LEVEL_REQ` dict in `economy.py` controls per-item lock thresholds. --- ## strings.py Organisation | Section | Dict | Usage in bot.py | |---|---|---| | Flavour text | `WORK_JOBS`, `BEG_LINES`, `CRIME_WIN`, `CRIME_LOSE` | Randomised descriptions | | Command descriptions | `CMD["key"]` | `@tree.command(description=S.CMD["key"])` | | Parameter descriptions | `OPT["key"]` | `@app_commands.describe(param=S.OPT["key"])` | | Help embed | `HELP_CATEGORIES["cat"]` | `cmd_help` | | Banned message | `MSG_BANNED` | All banned checks | | Maintenance mode | `MSG_MAINTENANCE` | Shown when `_PAUSED=True` in bot.py (toggled by `/pause`) | | Reminder options | `REMINDER_OPTS` | `RemindersSelect` dropdown | | Slots outcomes | `SLOTS_TIERS["tier"]` → `(title, color)` | `cmd_slots` | | Embed titles | `TITLE["key"]` | `discord.Embed(title=S.TITLE["key"])` | | Error messages | `ERR["key"]` | `send_message(S.ERR["key"])` - use `.format(**kwargs)` for dynamic parts | | Cooldown messages | `CD_MSG["cmd"].format(ts=_cd_ts(...))` | Cooldown responses | | Shop UI | `SHOP_UI["key"]` | `_shop_embed` | | Item descriptions | `ITEM_DESCRIPTIONS["item_key"]` | `economy.SHOP[key]["description"]` | --- ## Constants Location Quick-Reference | Constant | File | Description | |---|---|---| | `SHOP` | `economy.py` | All shop items (name, emoji, cost, description) | | `SHOP_TIERS` | `economy.py` | Which items are in T1/T2/T3 | | `SHOP_LEVEL_REQ` | `economy.py` | Min level per item | | `COOLDOWNS` | `economy.py` | Base cooldown per command | | `JAIL_DURATION` | `economy.py` | How long jail lasts | | `LEVEL_ROLES` | `economy.py` | `[(min_level, "RoleName"), ...]` highest first | | `ECONOMY_ROLE` | `economy.py` | Name of the base economy participation role | | `EXP_REWARDS` | `economy.py` | EXP per command | | `HOUSE_ID` | `economy.py` | Bot's user ID (house account for /rob) | | `MIN_BAIL` | `economy.py` | Minimum bail payment (350⬡) | | `COIN` | `economy.py` | The coin emoji string | | `_PAUSED` | `bot.py` | In-memory maintenance flag; toggled by `/pause`; blocks all non-admin commands | --- ## Balance Notes (as of current version) - **Beg** is most efficient for active players (3min cooldown + 2× multiplier w/ `klaviatuur` = high ⬡/hr) - **Work** is best for passive players (1h cooldown, fire and forget) - **Crime** is high risk/reward - best with `cat6` + `mikrofon` - **`lan_pass`** (1200⬡) doubles daily - good long-term investment - **`gaming_laptop`** (1500⬡) 5% interest, capped 500⬡/day - snowballs with large balance - `anticheat` is consumable (2 uses) - only item that can be re-bought - `karikas` (T3) is the only item that preserves a daily streak across missed days - `reguleeritav_laud` (T2) stacks with `gaming_hiir`: combined ×1.5 × ×1.25 = ×1.875