13 KiB
TipiLAN Bot - Developer Reference
File Structure
The codebase is split into core/ (domain logic), commands/ (Discord slash command handlers), and a thin bot.py that wires everything together.
Top level
| File | Purpose |
|---|---|
bot.py |
Discord client, event handlers (on_ready, on_member_join, ...), background tasks (presence rotation, daily birthday loop), shared helpers (_award_exp, _maybe_remind, _parse_amount, _PAUSED), and register_*_commands(...) wiring for every command module |
strings.py |
Single source of truth for all user-facing text. Edit here to change any message. |
config.py |
Environment variables (TOKEN, GUILD_ID, PB_URL, etc.) |
core/ - domain logic, no Discord coupling
| File | Purpose |
|---|---|
core/economy.py |
All economy business logic (do_daily, do_work, ...), data model, constants (SHOP, COOLDOWNS, LEVEL_ROLES, EXP_REWARDS, JAIL_DURATION, ...) |
core/pb_client.py |
Async PocketBase REST client - auth token cache, CRUD on economy_users collection |
core/sheets.py |
Google Sheets integration (member sync) |
core/member_sync.py |
Birthday/member sync helpers |
commands/ - one slash-command group per file
Each file exposes a register_<group>_commands(tree, bot, ...) function. bot.py calls them all once on startup, passing in shared helpers (coin, cd_ts, award_exp, maybe_remind, parse_amount, ...).
| File | Commands / Responsibility |
|---|---|
commands/dev_member_commands.py |
/check, /member (dev profile only) |
commands/dev_member_runtime.py |
on_member_join flow + birthday_daily task body |
commands/economy_income_commands.py |
/daily, /work, /beg, /crime, /rob |
commands/economy_games_commands.py |
/roulette, /slots, /blackjack, /rps |
commands/economy_extra_commands.py |
/heist, /jailbreak, /reminders, /request, ... |
commands/economy_fish_commands.py |
/fish, /fishbook, /fishsell |
commands/economy_profile_commands.py |
/balance, /rank, /stats, /cooldowns, /leaderboard |
commands/economy_support_commands.py |
/shop, /buy, /give, /economysetup |
commands/economy_prestige_commands.py |
/prestige, /prestigeshop, /prestigebuy |
commands/economy_admin_commands.py |
/admincoins, /adminexp, /adminitem, /adminjail, /adminban, /adminreset, /adminview |
commands/ops_admin_commands.py |
/sync, /restart, /shutdown, /pause, /send, /status |
commands/ops_channel_commands.py |
Channel allowlist commands |
commands/info_commands.py |
/patchnotes and other lightweight info commands |
scripts/
| File | Purpose |
|---|---|
scripts/migrate_to_pb.py |
One-time legacy migration: data/economy.json → PocketBase. Only relevant if you still have a pre-PB JSON store. |
scripts/add_stats_fields.py |
Schema migration: adds new fields to the economy_users collection. Idempotent. |
scripts/reset_pb_collections.py |
Destructive - deletes and recreates the dev + economy collections. Requires --confirm. Use only in dev. |
Adding a New Economy Command
Pick the commands/economy_*_commands.py file that matches the new command's category (income, games, profile, ...) and add the handler inside its register_*_commands function. If none fit, create a new module and register it from bot.py.
Checklist - do all of these, in order:
core/economy.py- add thedo_<cmd>async function with cooldown check, logic,_commit, and_txnloggingcore/economy.py- add the cooldown toCOOLDOWNSdict if it has onecore/economy.py- add the EXP reward toEXP_REWARDSdictstrings.pyCMD- add the slash command descriptionstrings.pyOPT- add any parameter descriptionsstrings.pyTITLE- add embed title(s) for success/fail statesstrings.pyERR- add any error messages (banned, cooldown usesCD_MSG, jailed usesCD_MSG["jailed"])strings.pyCD_MSG- add cooldown message if command has a cooldownstrings.pyHELP_CATEGORIES["tipibot"]["fields"]- add the command to the help embedcommands/economy_<group>_commands.py- insideregister_*_commands, add@tree.command(name="<cmd>", ...)cmd_<name>; handle allres["reason"]casescommands/economy_<group>_commands.py- callmaybe_remind(user_id, "<cmd>")if the command has a cooldown and reminders make sense (the helper is passed in via theregister_*signature)commands/economy_<group>_commands.py- callawait award_exp(interaction, economy.EXP_REWARDS["<cmd>"])on successstrings.pyREMINDER_OPTS- add a reminder option if the command needs onebot.py_maybe_remind- if the command has an item-modified cooldown, add anelifbranch (this helper still lives inbot.pyand is shared across all command modules)
Adding a New Shop Item
Checklist:
core/economy.pySHOP- add the item dict{name, emoji, cost, description: strings.ITEM_DESCRIPTIONS["key"]}core/economy.pySHOP_TIERS- add the key to the correct tier list (1/2/3)core/economy.pySHOP_LEVEL_REQ- add minimum level if it is T2 (≥10) or T3 (≥20)strings.pyITEM_DESCRIPTIONS- add the item description (Estonian flavour + English effect)strings.pyHELP_CATEGORIES["shop"]["fields"]- add display entry (sorted by cost)- If the item modifies a cooldown:
core/economy.py- add theif "item" in user["items"]branch in the relevantdo_<cmd>functionbot.py_maybe_remind- addelif cmd == "<cmd>" and "<item>" in items:branch with the new delaycommands/economy_profile_commands.pycmd_cooldowns- add the item annotation to the relevant status line
Adding a New Level Role
core/economy.pyLEVEL_ROLES- add(min_level, "RoleName")in descending level order (highest first)bot.py_ensure_level_role- no changes needed (usesLEVEL_ROLESdynamically)- Run
/economysetupin the server to create the role and set its position
Adding a New Admin Command
strings.pyCMD- add"[Admin] ..."descriptionstrings.pyHELP_CATEGORIES["admin"]["fields"]- add the entrycommands/economy_admin_commands.py(orcommands/ops_admin_commands.pyfor non-economy ops) - add the handler with@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). core/pb_client.py owns all reads/writes. Each do_* function in core/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⬡)
karikasitem: 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 indo_*functions via_is_jailed)
EXP Rewards (from EXP_REWARDS in core/economy.py)
EXP is awarded on every successful command use. Level formula: level = max(1, floor(sqrt(exp / 6))) (see get_level / exp_for_level). Thresholds: Level 5 = 150 EXP, Level 10 = 600, Level 20 = 2 400, Level 30 = 5 400.
Gambling EXP is bet-scaled via gamble_exp(bet); fish EXP is per-species in FISH (common 2–3, uncommon 6–7, rare 10, epic 14–15, legendary 25).
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 core/economy.py controls per-item lock thresholds.
strings.py Organisation
Imported as import strings as S everywhere. Dicts are read from bot.py and from every commands/*.py module.
| Section | Dict | Typical usage |
|---|---|---|
| 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 (in bot.py) |
| Banned message | MSG_BANNED |
All banned checks |
| Maintenance mode | MSG_MAINTENANCE |
Shown when _PAUSED=True in bot.py (toggled by /pause in commands/ops_admin_commands.py) |
| Reminder options | REMINDER_OPTS |
RemindersSelect dropdown |
| Slots outcomes | SLOTS_TIERS["tier"] → (title, color) |
cmd_slots (in commands/economy_games_commands.py) |
| 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 (cd_ts helper passed in by bot.py) |
| Shop UI | SHOP_UI["key"] |
_shop_embed (in commands/economy_support_commands.py) |
| Item descriptions | ITEM_DESCRIPTIONS["item_key"] |
core/economy.py SHOP[key]["description"] |
| Patch notes UI | PATCHNOTES_UI["key"] |
commands/info_commands.py (/patchnotes) |
Constants Location Quick-Reference
| Constant | File | Description |
|---|---|---|
SHOP |
core/economy.py |
All shop items (name, emoji, cost, description) |
SHOP_TIERS |
core/economy.py |
Which items are in T1/T2/T3 |
SHOP_LEVEL_REQ |
core/economy.py |
Min level per item |
COOLDOWNS |
core/economy.py |
Base cooldown per command |
JAIL_DURATION |
core/economy.py |
How long jail lasts |
LEVEL_ROLES |
core/economy.py |
[(min_level, "RoleName"), ...] highest first |
ECONOMY_ROLE |
core/economy.py |
Name of the base economy participation role |
EXP_REWARDS |
core/economy.py |
EXP per command |
FISH |
core/economy.py |
Fish species table (rarity, weight, coins, exp) |
HOUSE_ID |
core/economy.py |
Bot's user ID (house account for /rob) |
MIN_BAIL |
core/economy.py |
Minimum bail payment (350⬡) |
COIN |
core/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 investmentgaming_laptop(1500⬡) 5% interest, capped 500⬡/day - snowballs with large balanceanticheatis consumable (2 uses) - only item that can be re-boughtkarikas(T3) is the only item that preserves a daily streak across missed daysreguleeritav_laud(T2) stacks withgaming_hiir: combined ×1.5 × ×1.25 = ×1.875