fix/economy-money-safety #4

Merged
renkar merged 6 commits from fix/economy-money-safety into master 2026-08-19 17:36:16 +00:00
2 changed files with 31 additions and 29 deletions
Showing only changes of commit 254992c642 - Show all commits

View File

@@ -222,7 +222,7 @@ If a member joins and their birthday is within `BIRTHDAY_WINDOW_DAYS` days, a bi
## TipiCOIN Economy
All economy data is stored in **PocketBase** (`economy_users` collection - see `core/pb_client.py`). The currency is **TipiCOIN** (⬡), displayed as a custom Discord emoji configured in `core/economy.py → COIN`.
All economy data is stored in **PocketBase** (`economy_users` collection - see `core/pb_client.py`). The currency is **TipiCOIN** (⬡), displayed as a custom Discord emoji configured in `core/economy/store.py → COIN`.
---
@@ -457,7 +457,7 @@ All items are **permanent** once purchased **except Anticheat**, which expires a
Commands that accept a coin amount (`/give`, `/roulette`, `/rps`, `/slots`, `/blackjack`) accept `"all"` as the amount to wager your entire balance.
### Custom emoji
Change `COIN` in `core/economy.py` to any Discord emoji string:
Change `COIN` in `core/economy/store.py` to any Discord emoji string:
```python
COIN = "<:tipicoin:YOUR_EMOJI_ID>"
```

View File

@@ -16,7 +16,7 @@ The codebase is split into **`core/`** (domain logic), **`commands/`** (Discord
| 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/economy/` | Economy business logic **package**, re-exported via `core/economy/__init__.py` so callers use `from core import economy` + attribute access (`economy.do_daily`, `economy.SHOP`, ...). Submodules: `store.py` (user records, per-user locks, `COOLDOWNS`, `JAIL_DURATION`, `COIN`, `get_user`/`_commit`/`_txn`), `income.py`, `gambling.py`, `fishing.py`, `jail.py`, `heist.py`, `prestige.py`, `shop.py`, `levels.py`, `quests.py`, `leaderboards.py`, `house.py`, `admin.py` |
| `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 |
@@ -57,9 +57,9 @@ Pick the `commands/economy_*_commands.py` file that matches the new command's ca
Checklist - do all of these, in order:
1. **`core/economy.py`** - add the `do_<cmd>` async function with cooldown check, logic, `_commit`, and `_txn` logging
2. **`core/economy.py`** - add the cooldown to `COOLDOWNS` dict if it has one
3. **`core/economy.py`** - add the EXP reward to `EXP_REWARDS` dict
1. **`core/economy/<area>.py`** (e.g. `income.py`, `gambling.py`, `fishing.py`) - add the `do_<cmd>` async function with cooldown check, logic, `_commit`, and `_txn` logging (`get_user`/`_commit`/`_txn` live in `store.py`)
2. **`core/economy/store.py`** - add the cooldown to `COOLDOWNS` dict if it has one
3. **`core/economy/levels.py`** - add the EXP reward to `EXP_REWARDS` dict
4. **`strings/commands.py` `CMD`** - add the slash command description
5. **`strings/commands.py` `OPT`** - add any parameter descriptions
6. **`strings/common.py` `TITLE`** - add embed title(s) for success/fail states
@@ -78,13 +78,13 @@ Checklist - do all of these, in order:
Checklist:
1. **`core/economy.py` `SHOP`** - add the item dict `{name, emoji, cost, description: strings.ITEM_DESCRIPTIONS["key"]}`
2. **`core/economy.py` `SHOP_TIERS`** - add the key to the correct tier list (1/2/3)
3. **`core/economy.py` `SHOP_LEVEL_REQ`** - add minimum level if it is T2 (≥10) or T3 (≥20)
1. **`core/economy/shop.py` `SHOP`** - add the item dict `{name, emoji, cost, description: strings.ITEM_DESCRIPTIONS["key"]}`
2. **`core/economy/shop.py` `SHOP_TIERS`** - add the key to the correct tier list (1/2/3)
3. **`core/economy/shop.py` `SHOP_LEVEL_REQ`** - add minimum level if it is T2 (≥10) or T3 (≥20)
4. **`strings/economy.py` `ITEM_DESCRIPTIONS`** - add the item description (Estonian flavour + English effect)
5. **`strings/commands.py` `HELP_CATEGORIES["shop"]["fields"]`** - add display entry (sorted by cost)
6. If the item modifies a cooldown:
- **`core/economy.py`** - add the `if "item" in user["items"]` branch in the relevant `do_<cmd>` function
- **`core/economy/<area>.py`** - add the `if "item" in user["items"]` branch in the relevant `do_<cmd>` function
- **`bot.py` `_maybe_remind`** - add `elif cmd == "<cmd>" and "<item>" in items:` branch with the new delay
- **`commands/economy_profile_commands.py` `cmd_cooldowns`** - add the item annotation to the relevant status line
@@ -92,7 +92,7 @@ Checklist:
## Adding a New Level Role
1. **`core/economy.py` `LEVEL_ROLES`** - add `(min_level, "RoleName")` in descending level order (highest first)
1. **`core/economy/levels.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
@@ -110,7 +110,7 @@ Checklist:
### 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.
All economy state is stored in **PocketBase** (`economy_users` collection). `core/pb_client.py` owns all reads/writes. Each `do_*` function in `core/economy/` calls `get_user()` (from `store.py`) → mutates the local dict → calls `_commit()`. `_commit` does a `PATCH` to PocketBase.
### Currency & Income Sources
@@ -141,10 +141,10 @@ Commands that accept a coin amount (`/give`, `/roulette`, `/rps`, `/slots`, `/bl
- `/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 `core/economy.py`)
### EXP Rewards (from `EXP_REWARDS` in `core/economy/levels.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 23, uncommon 67, rare 10, epic 1415, legendary 25).
Gambling EXP is bet-scaled via `gamble_exp(bet)`; fish EXP is per-species in `FISH_CATALOGUE` (common 23, uncommon 67, rare 10, epic 1415, legendary 25).
---
@@ -179,7 +179,7 @@ Role assignment:
| 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.
The `SHOP_LEVEL_REQ` dict in `core/economy/shop.py` controls per-item lock thresholds.
---
@@ -201,27 +201,29 @@ Imported as `import strings as S` everywhere. `strings/` is a package: the names
| Error messages | `ERR["key"]` | `common.py` | `send_message(S.ERR["key"])` - use `.format(**kwargs)` for dynamic parts |
| Cooldown messages | `CD_MSG["cmd"].format(ts=cd_ts(...))` | `common.py` | Cooldown responses (`cd_ts` helper passed in by `bot.py`) |
| Shop UI | `SHOP_UI["key"]` | `economy.py` | `_shop_embed` (in `commands/economy_support_commands.py`) |
| Item descriptions | `ITEM_DESCRIPTIONS["item_key"]` | `economy.py` | `core/economy.py` `SHOP[key]["description"]` |
| Item descriptions | `ITEM_DESCRIPTIONS["item_key"]` | `economy.py` | `core/economy/shop.py` `SHOP[key]["description"]` |
| Patch notes UI | `PATCHNOTES_UI["key"]` | `common.py` | `commands/info_commands.py` (`/patchnotes`) |
---
## Constants Location Quick-Reference
| Constant | File | Description |
All are re-exported from `core/economy/__init__.py`, so code reads them as `economy.<NAME>` regardless of which submodule defines them. Edit the file in the **Defined in** column.
| Constant | Defined in | 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 |
| `SHOP` | `core/economy/shop.py` | All shop items (name, emoji, cost, description) |
| `SHOP_TIERS` | `core/economy/shop.py` | Which items are in T1/T2/T3 |
| `SHOP_LEVEL_REQ` | `core/economy/shop.py` | Min level per item |
| `COOLDOWNS` | `core/economy/store.py` | Base cooldown per command |
| `JAIL_DURATION` | `core/economy/store.py` | How long jail lasts |
| `LEVEL_ROLES` | `core/economy/levels.py` | `[(min_level, "RoleName"), ...]` highest first |
| `ECONOMY_ROLE` | `core/economy/levels.py` | Name of the base economy participation role |
| `EXP_REWARDS` | `core/economy/levels.py` | EXP per command |
| `FISH_CATALOGUE` | `core/economy/fishing.py` | Fish species table (rarity, weight, coins, exp) |
| `HOUSE_ID` | `core/economy/house.py` | Bot's user ID (house account for /rob) |
| `MIN_BAIL` | `core/economy/jail.py` | Minimum bail payment (350⬡) |
| `COIN` | `core/economy/store.py` | The coin emoji string |
| `_PAUSED` | `bot.py` | In-memory maintenance flag; toggled by `/pause`; blocks all non-admin commands |
---