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
3 changed files with 40 additions and 32 deletions
Showing only changes of commit 191726721c - Show all commits

View File

@@ -483,7 +483,15 @@ Every slash command invocation is logged with the user ID, display name, and all
```
├── bot.py # Discord client, event handlers, shared helpers; wires command modules together
├── strings.py # All user-facing strings (command descriptions, help text, errors)
├── strings/ # All user-facing strings, split by domain; re-exported so `import strings` works unchanged
│ ├── __init__.py # Re-exports every name from the submodules
│ ├── common.py # System messages, embed TITLEs, ERR, CD_MSG, status/send/patchnotes/reminders
│ ├── commands.py # CMD + OPT descriptions, HELP_CATEGORIES, HELP_UI
│ ├── member.py # /check, member/birthday/channel/economy-setup UI
│ ├── economy.py # Income flavour + income/profile/shop/quests/leaderboard/request UI
│ ├── admin.py # Admin responses, season reset, prestige
│ ├── games.py # Slots, roulette, RPS, blackjack, heist, jailbreak
│ └── fishing.py # Fish catalogue, rarities, /fish UI
├── config.py # Environment variable loader
├── core/
│ ├── economy/ # TipiCOIN business logic (re-exported via core/economy/__init__.py)

View File

@@ -9,7 +9,7 @@ The codebase is split into **`core/`** (domain logic), **`commands/`** (Discord
| 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. |
| `strings/` | **Single source of truth for all user-facing text**, split into domain submodules (`common`, `commands`, `member`, `economy`, `admin`, `games`, `fishing`) and re-exported via `strings/__init__.py` so `import strings` / `strings.NAME` works unchanged. Edit the relevant submodule to change any message. |
| `config.py` | Environment variables (TOKEN, GUILD_ID, PB_URL, etc.) |
### `core/` - domain logic, no Discord coupling
@@ -60,16 +60,16 @@ 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
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
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
7. **`strings/common.py` `ERR`** - add any error messages (banned, cooldown uses `CD_MSG`, jailed uses `CD_MSG["jailed"]`)
8. **`strings/common.py` `CD_MSG`** - add cooldown message if command has a cooldown
9. **`strings/commands.py` `HELP_CATEGORIES["tipibot"]["fields"]`** - add the command to the help embed
10. **`commands/economy_<group>_commands.py`** - inside `register_*_commands`, add `@tree.command(name="<cmd>", ...)` `cmd_<name>`; handle all `res["reason"]` cases
11. **`commands/economy_<group>_commands.py`** - call `maybe_remind(user_id, "<cmd>")` if the command has a cooldown and reminders make sense (the helper is passed in via the `register_*` signature)
12. **`commands/economy_<group>_commands.py`** - call `await award_exp(interaction, economy.EXP_REWARDS["<cmd>"])` on success
13. **`strings.py` `REMINDER_OPTS`** - add a reminder option if the command needs one
13. **`strings/common.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 (this helper still lives in `bot.py` and is shared across all command modules)
---
@@ -81,8 +81,8 @@ 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)
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)
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
- **`bot.py` `_maybe_remind`** - add `elif cmd == "<cmd>" and "<item>" in items:` branch with the new delay
@@ -100,8 +100,8 @@ Checklist:
## Adding a New Admin Command
1. **`strings.py` `CMD`** - add `"[Admin] ..."` description
2. **`strings.py` `HELP_CATEGORIES["admin"]["fields"]`** - add the entry
1. **`strings/commands.py` `CMD`** - add `"[Admin] ..."` description
2. **`strings/commands.py` `HELP_CATEGORIES["admin"]["fields"]`** - add the entry
3. **`commands/economy_admin_commands.py`** (or `commands/ops_admin_commands.py` for non-economy ops) - add the handler with `@app_commands.default_permissions(manage_guild=True)` and `@app_commands.guild_only()`
---
@@ -183,26 +183,26 @@ The `SHOP_LEVEL_REQ` dict in `core/economy.py` controls per-item lock thresholds
---
## strings.py Organisation
## strings/ Organisation
Imported as `import strings as S` everywhere. Dicts are read from `bot.py` and from every `commands/*.py` module.
Imported as `import strings as S` everywhere. `strings/` is a package: the names below live in domain submodules and are re-exported from `strings/__init__.py`, so `S.CMD`, `S.ERR`, etc. resolve unchanged regardless of which submodule they live in. Dicts are read from `bot.py` and from every `commands/*.py` module. Edit the submodule shown in the **Module** column.
| 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`) |
| Section | Dict | Module | Typical usage |
|---|---|---|---|
| Flavour text | `WORK_JOBS`, `BEG_LINES`, `CRIME_WIN`, `CRIME_LOSE` | `economy.py` | Randomised descriptions |
| Command descriptions | `CMD["key"]` | `commands.py` | `@tree.command(description=S.CMD["key"])` |
| Parameter descriptions | `OPT["key"]` | `commands.py` | `@app_commands.describe(param=S.OPT["key"])` |
| Help embed | `HELP_CATEGORIES["cat"]` | `commands.py` | `cmd_help` (in `bot.py`) |
| Banned message | `MSG_BANNED` | `common.py` | All banned checks |
| Maintenance mode | `MSG_MAINTENANCE` | `common.py` | Shown when `_PAUSED=True` in `bot.py` (toggled by `/pause` in `commands/ops_admin_commands.py`) |
| Reminder options | `REMINDER_OPTS` | `common.py` | `RemindersSelect` dropdown |
| Slots outcomes | `SLOTS_TIERS["tier"]``(title, color)` | `games.py` | `cmd_slots` (in `commands/economy_games_commands.py`) |
| Embed titles | `TITLE["key"]` | `common.py` | `discord.Embed(title=S.TITLE["key"])` |
| 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"]` |
| Patch notes UI | `PATCHNOTES_UI["key"]` | `common.py` | `commands/info_commands.py` (`/patchnotes`) |
---

View File

@@ -314,7 +314,7 @@ COOLDOWNS_UI: dict[str, str] = {
"note_monitor": " *(monitor: 40min)*",
"note_hiirematt": " *(hiirematt: 3min)*",
"note_ussipurk": " *(ussipurk: 90s)*",
"jailed": "\n<EFBFBD> **Vanglas** - vabaneb <t:{ts}:R>",
"jailed": "\n🔒 **Vanglas** - vabaneb <t:{ts}:R>",
"jail_expired": "\n🔓 Vangla lõppes",
}