Initial commit
This commit is contained in:
429
README.md
Normal file
429
README.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# TipiLAN Bot
|
||||
|
||||
Discord bot for the TipiLAN community. Manages member roles and nicknames via Google Sheets, announces birthdays, and runs the TipiCOIN economy.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Setup](#setup)
|
||||
2. [Member Management](#member-management)
|
||||
3. [Admin Commands](#admin-commands)
|
||||
4. [Birthday System](#birthday-system)
|
||||
5. [TipiCOIN Economy](#tipicoin-economy)
|
||||
6. [Project Structure](#project-structure)
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Discord Application
|
||||
|
||||
- **Bot token** - Settings → Bot → Token → Copy
|
||||
- **Server Members Intent** must be **ON** - Settings → Bot → Privileged Gateway Intents
|
||||
- Bot invite scopes: `bot` + `applications.commands`
|
||||
- Required permissions: Manage Roles, Manage Nicknames, View Channels, Send Messages, Embed Links, Read Message History
|
||||
|
||||
> Permissions integer: `402738176`
|
||||
|
||||
### 2. Google Service Account
|
||||
|
||||
1. [Google Cloud Console](https://console.cloud.google.com/) → create/select project
|
||||
2. Enable **Google Sheets API** and **Google Drive API**
|
||||
3. Credentials → Create Credentials → Service Account
|
||||
4. Download JSON key → save as `credentials.json` in the project root
|
||||
5. Share your Google Sheet with the service account `client_email` - give **Editor** access
|
||||
|
||||
### 3. Google Sheet Format
|
||||
|
||||
Row 1 = headers (exact names). Row 2 = formula/stats row (skipped by bot). Data starts row 3.
|
||||
|
||||
| Column | What the bot does with it |
|
||||
|---|---|
|
||||
| **Nimi** | Sets Discord nickname: first name + last initial (`Mari-Liis Tamm` → `Mari-Liis T`) |
|
||||
| **Organisatsioon** | Maps to a Discord role (comma-separated for multiple) |
|
||||
| **Meil** | Read-only |
|
||||
| **Discord** | Username used for initial matching |
|
||||
| **User ID** | Bot writes numeric Discord ID here once matched |
|
||||
| **Sünnipäev** | Birthday - accepts `DD/MM/YYYY`, `YYYY-MM-DD`, `MM-DD`. Years outside 1920-now ignored |
|
||||
| **Telefon** | Read-only |
|
||||
| **Valdkond** | Maps to a Discord role (comma-separated) |
|
||||
| **Roll** | Maps to a Discord role (comma-separated) |
|
||||
| **Discordis synced?** | Bot writes `TRUE`/`FALSE` checkbox after each sync |
|
||||
| **Groupi lisatud?** | Managed externally |
|
||||
|
||||
**Empty cell values** - the bot treats blank cells, `"-"`, `"x"`, `"n/a"`, `"none"`, `"ei"` as empty/skipped.
|
||||
|
||||
**Role name mapping** - some sheet values map to different Discord role names. Trailing punctuation (`.`, `,`, `;` etc.) is stripped from sheet values before lookup, so `"Messiala."` correctly matches the `Messiala` Discord role:
|
||||
|
||||
| Sheet value | Discord role |
|
||||
|---|---|
|
||||
| `Juht` | `Tiimijuht` |
|
||||
| `Admin` | `+` |
|
||||
|
||||
**Base roles** - two role IDs in `config.py → BASE_ROLE_IDS` are added to every synced member automatically.
|
||||
|
||||
### 4. PocketBase (Economy Database)
|
||||
|
||||
The economy system stores all player data in [PocketBase](https://pocketbase.io/).
|
||||
|
||||
1. Download `pocketbase.exe` (Windows) from https://pocketbase.io/docs/ and place it in the project root.
|
||||
2. Start PocketBase: `.\pocketbase.exe serve`
|
||||
3. Open the admin UI at http://127.0.0.1:8090/_/ and create a superuser account.
|
||||
4. Create a collection named `economy_users` - see `docs/POCKETBASE_SETUP.md` for the full schema.
|
||||
5. Set `PB_URL`, `PB_ADMIN_EMAIL`, `PB_ADMIN_PASSWORD` in `.env`.
|
||||
6. **One-time data migration** (only if you have an existing `data/economy.json`): `python scripts/migrate_to_pb.py`
|
||||
|
||||
> PocketBase must be running before the bot starts. `pocketbase.exe` and `pb_data/` are gitignored.
|
||||
|
||||
### 5. Environment Variables
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
copy .env.example .env
|
||||
|
||||
# macOS/Linux
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
| Variable | Description |
|
||||
|---|---|
|
||||
| `DISCORD_TOKEN` | Bot token from Discord Developer Portal |
|
||||
| `SHEET_ID` | ID from the Google Sheet URL |
|
||||
| `GOOGLE_CREDS_PATH` | Path to `credentials.json` (default: `credentials.json`) |
|
||||
| `GUILD_ID` | Right-click server → Copy Server ID (Developer Mode on) |
|
||||
| `BIRTHDAY_CHANNEL_ID` | Channel for birthday `@here` pings |
|
||||
| `BIRTHDAY_WINDOW_DAYS` | Days before birthday that on-join check flags it (default: 7) |
|
||||
| `PB_URL` | PocketBase base URL (default: `http://127.0.0.1:8090`) |
|
||||
| `PB_ADMIN_EMAIL` | PocketBase superuser e-mail |
|
||||
| `PB_ADMIN_PASSWORD` | PocketBase superuser password |
|
||||
|
||||
### 6. Install & Run
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
.venv\Scripts\activate # Windows
|
||||
# source .venv/bin/activate # macOS/Linux
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Terminal 1 - keep running
|
||||
.\pocketbase.exe serve
|
||||
|
||||
# Terminal 2
|
||||
python bot.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Member Management
|
||||
|
||||
### On member join
|
||||
- Bot looks the member up in the sheet by **Discord username**
|
||||
- If found → sets nickname, assigns roles, writes back User ID, marks synced
|
||||
- If **not found** → creates a new sheet row with their `Discord` username and `User ID` pre-filled. Admin fills in the rest, then runs `/check`
|
||||
|
||||
### Matching logic
|
||||
1. Match by **User ID** (numeric, reliable - IDs never change)
|
||||
2. Fall back to **Discord username** (case-insensitive) if no ID yet
|
||||
3. Once matched by username, the bot writes the ID back so future matches are by ID
|
||||
|
||||
### Nickname format
|
||||
`Nimi` column → first name + last name initial. Hyphenated first names preserved.
|
||||
|
||||
| Nimi | Nickname |
|
||||
|---|---|
|
||||
| Mari Tamm | Mari T |
|
||||
| Mari-Liis Tamm | Mari-Liis T |
|
||||
| Jaan | Jaan |
|
||||
|
||||
### Synced status
|
||||
A member is marked `Discordis synced? = TRUE` when their sync completes with no errors.
|
||||
Admins (bot lacks permission to modify them) are silently skipped and still marked synced.
|
||||
|
||||
---
|
||||
|
||||
## Admin Commands
|
||||
|
||||
> These commands are hidden from users who don't have the required permission. Override per-role in **Server Settings → Integrations → Bot**.
|
||||
|
||||
| Command | Permission | What it does |
|
||||
|---|---|---|
|
||||
| `/check` | Manage Roles | Refreshes sheet data, backfills missing User IDs, syncs nicknames + roles for every member, reports stats |
|
||||
| `/member @user` | Manage Roles | Shows a member's full sheet data + calculated age |
|
||||
| `/sync` | Manage Guild | Re-registers slash commands with Discord |
|
||||
| `/restart` | Manage Guild | Gracefully restarts the bot process; posts ✅ in the same channel when back up |
|
||||
| `/shutdown` | Manage Guild | Shuts the bot down cleanly without restarting |
|
||||
| `/pause` | Manage Guild | Toggles maintenance mode — blocks all non-admin commands; calling again unpauses |
|
||||
| `/send #channel message` | Manage Guild | Sends a message to any channel as the bot |
|
||||
| `/status` | Manage Guild | Bot uptime, RAM, CPU, latency, cache stats, economy user count |
|
||||
| `/admincoins @user <kogus> <põhjus>` | Manage Guild | Give (positive) or take (negative) TipiCOINi. Balance floored at 0. User gets a DM with reason. |
|
||||
| `/adminjail @user <minutid> <põhjus>` | Manage Guild | Manually jail a user for N minutes. User gets a DM. |
|
||||
| `/adminunjail @user` | Manage Guild | Release a user from jail immediately. |
|
||||
| `/adminban @user <põhjus>` | Manage Guild | Ban a user from all economy commands. User gets a DM. |
|
||||
| `/adminunban @user` | Manage Guild | Lift an economy ban. |
|
||||
| `/adminreset @user <põhjus>` | Manage Guild | Wipe a user's balance, items, and streak to zero. User gets a DM. |
|
||||
| `/adminview @user` | Manage Guild | Inspect a user's full economy profile: balance, streak, items, jail status, ban status. |
|
||||
|
||||
### `/check` output example
|
||||
```
|
||||
🔑 Täideti 3 puuduvat kasutaja ID-d.
|
||||
✅ Korras: 54
|
||||
🔧 Parandatud: 3
|
||||
❓ Ei leitud: 1
|
||||
⚠️ Vead: 2
|
||||
|
||||
Üksikasjad:
|
||||
🔧 Mari T: +rollid: TipiSÕBER
|
||||
⚠️ Rolli 'Messiala' ei leitud serverist
|
||||
|
||||
📊 Tabeli statistika - 58 liiget
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Birthday System
|
||||
|
||||
### Daily announcement
|
||||
Every day at **09:00 Tallinn time** the bot checks all sheet rows and pings `@here` in `BIRTHDAY_CHANNEL_ID` for anyone whose birthday is today.
|
||||
|
||||
**Duplicate prevention** - announcements are logged to `birthday_sent.json` keyed by date. Bot restart on a birthday day does **not** re-ping. Log is auto-cleaned after 2 days.
|
||||
|
||||
### `/birthdays`
|
||||
Paginated embed with **12 pages** - one per calendar month. Opens on the **current month**. Navigate with ◀/▶. Each entry shows:
|
||||
- Member mention (if their User ID is known) or name
|
||||
- Birthday date
|
||||
- Days until next birthday (or 🎉 if today)
|
||||
|
||||
### On member join
|
||||
If a member joins and their birthday is within `BIRTHDAY_WINDOW_DAYS` days, a birthday announcement is sent.
|
||||
|
||||
---
|
||||
|
||||
## TipiCOIN Economy
|
||||
|
||||
All economy data is stored in **PocketBase** (`economy_users` collection - see `pb_client.py`). The currency is **TipiCOIN** (⬡), displayed as a custom Discord emoji configured in `economy.py → COIN`.
|
||||
|
||||
---
|
||||
|
||||
### House account
|
||||
|
||||
The bot has its own TipiCOIN balance (the "house"). Coins flow **into** the house when players lose:
|
||||
|
||||
- `/roulette` - lost bets
|
||||
- `/slots` - missed bets
|
||||
- `/blackjack` - lost bets
|
||||
- `/rps` - lost bets (vs bot or PvP)
|
||||
- `/crime` - failure fines
|
||||
- `/rob` - failure fines and anticheat counter-fines
|
||||
|
||||
The house is listed at **#0** on the leaderboard. Players can attempt to rob it via `/rob @TipiBOT` with special jackpot odds (35% success, 5–40% of the house balance).
|
||||
|
||||
---
|
||||
|
||||
### Earning coins
|
||||
|
||||
| Command | Cooldown | Base payout | Notes |
|
||||
|---|---|---|---|
|
||||
| `/daily` | 20h | 150 ⬡ | Streak multiplier applied (see below). Kõrvaklapid reduces cooldown to 18h. LAN Pilet doubles the reward. Botikoobas adds 5% interest on your balance (capped at 500 ⬡/day). |
|
||||
| `/work` | 1h | 15–75 ⬡ | Random job flavour text. Mängurihiir +50%, Reguleeritav laud +25% (stacks). Red Bull: 30% chance of ×3. Ultralai monitor reduces cooldown to 40min. |
|
||||
| `/beg` | 5min | 10–40 ⬡ | XL hiirematt reduces cooldown to 3min. Mehhaaniline klaviatuur multiplies earnings ×2. |
|
||||
| `/crime` | 2h | 200–500 ⬡ | 60% success rate (75% with CAT6). +30% earnings with Mikrofon on win. Fail = fine + 30min jail. Mänguritool skips jail on fail. |
|
||||
|
||||
### Daily streak
|
||||
|
||||
The streak increments each time you claim `/daily` within the cooldown window. Missing a day resets it to 1 **unless** you own the TipiLAN trofee item.
|
||||
|
||||
| Streak | Multiplier | Payout (base) |
|
||||
|---|---|---|
|
||||
| 1–2 days | ×1.0 | 150 ⬡ |
|
||||
| 3–6 days | ×1.5 | 225 ⬡ |
|
||||
| 7–13 days | ×2.0 | 300 ⬡ |
|
||||
| 14+ days | ×3.0 | 450 ⬡ |
|
||||
|
||||
> With LAN Pilet (×2 daily) and a 14-day streak (×3.0) the base payout reaches **900 ⬡**. Add Botikoobas 5% interest on top.
|
||||
|
||||
---
|
||||
|
||||
### EXP & levels
|
||||
|
||||
Every successful economy action awards EXP:
|
||||
|
||||
| Action | EXP |
|
||||
|---|---|
|
||||
| `/daily` claimed | +50 |
|
||||
| `/work` completed | +25 |
|
||||
| `/crime` success | +15 |
|
||||
| `/rob` success | +15 |
|
||||
| Gambling win (`/roulette`, `/slots`, `/blackjack`) | Scaled by bet: <10⬡ = 0, 10–99⬡ = +5, 100–999⬡ = +10, 1 000–9 999⬡ = +15, 10 000–99 999⬡ = +20, 100 000+⬡ = +25 |
|
||||
| `/beg` completed | +5 |
|
||||
|
||||
**Level formula:** `level = floor(√(total_exp ÷ 10))`
|
||||
|
||||
| Level | EXP required | Milestone |
|
||||
|---|---|---|
|
||||
| 1 | 10 | TipiNOOB role |
|
||||
| 5 | 250 | TipiGRINDER role |
|
||||
| 10 | 1 000 | TipiHUSTLER role · **T2 shop unlocks** |
|
||||
| 20 | 4 000 | TipiCHAD role · **T3 shop unlocks** |
|
||||
| 30 | 9 000 | TipiLEGEND role |
|
||||
|
||||
Use `/rank` to see your current EXP, level, progress bar to the next level, and leaderboard position.
|
||||
|
||||
### Level roles
|
||||
|
||||
Roles are assigned automatically on level-up and re-synced when you run `/rank`.
|
||||
|
||||
| Role | Min level |
|
||||
|---|---|
|
||||
| TipiNOOB | 1 |
|
||||
| TipiGRINDER | 5 |
|
||||
| TipiHUSTLER | 10 |
|
||||
| TipiCHAD | 20 |
|
||||
| TipiLEGEND | 30 |
|
||||
|
||||
The **ECONOMY** role is granted on your first EXP award (i.e. first successful economy command). Run `/economysetup` (admin) once to create all roles and position them correctly below the bot's own role.
|
||||
|
||||
---
|
||||
|
||||
### Gambling
|
||||
|
||||
| Command | Notes |
|
||||
|---|---|
|
||||
| `/roulette <panus> <värv>` | Red/black pays ×2 (≈50% each). Green pays ×14 at 1/37 chance. Lost bets go to the house. |
|
||||
| `/slots <panus>` | 3 reels. **Pair** = +50% of bet. **Triple** = tiered by symbol rarity (×4 heart → ×15 skull, ×1.5 with 360hz monitor). **Jackpot** (3 karikas) = ×25 (×37 with 360hz monitor). Miss = lose bet. |
|
||||
| `/blackjack <panus>` | Standard rules. Dealer stands on 17+. Natural blackjack pays 3:2. Double down on first action only. Split identical rank cards (one extra bet). Lost bets go to the house. |
|
||||
| `/rps [panus]` | Rock Paper Scissors vs. the bot with optional bet. Bot picks randomly. |
|
||||
| `/rps [panus] @vastane` | PvP duel - both players pick privately via DM, result posted to the server. Bet transferred to winner. |
|
||||
|
||||
### Social
|
||||
|
||||
| Command | Notes |
|
||||
|---|---|
|
||||
| `/rob @user` | 45% success (60% with Jellyfin). Target must have ≥100 ⬡. Steal 10–25% of target's balance. Fail = fine of 100–250 ⬡ to the house. Anticheat blocks the rob and fines the robber (2 charges per purchase). Cannot rob TipiBOT - use `/heist` instead. |
|
||||
| `/heist` | Start a bank robbery. Solo or group (max 8). 5-minute join window. Success: 35% base + 5% per extra player (cap 65%). Win = split **20–55%** of house balance equally. Fail = **1h 30min jail + ~15% balance fine** for all. 4h personal cooldown + 1h global server cooldown after each event. |
|
||||
| `/give @user <summa>` | Transfer coins directly to another player. **Jailed users cannot use this command.** |
|
||||
| `/request <summa> <põhjus> [@sihtmärk]` | Post a crowdfunding request. Anyone (or a specific target) can click **Rahasta** to contribute via `/give`. Expires in 5 minutes. |
|
||||
|
||||
### Info commands
|
||||
|
||||
| Command | Notes |
|
||||
|---|---|
|
||||
| `/balance [@user]` | Balance, daily streak, owned items (with Anticheat charges remaining), jail status if jailed. |
|
||||
| `/rank [@user]` | EXP total, current level, progress bar to next level, leaderboard rank. |
|
||||
| `/stats [@user]` | Lifetime statistics: economy totals, work/beg counts, gambling records, crime/heist history, social totals, best streak. |
|
||||
| `/cooldowns` | All cooldowns at a glance with live Discord timestamps. Shows jail timer if jailed. |
|
||||
| `/leaderboard` | Paginated coin leaderboard (10/page). House pinned at #0. ◀/▶ to browse; 📍 **Mina** jumps to your page. Has a separate EXP/level tab. |
|
||||
| `/shop` | Browse all items by tier. Shows owned status, Anticheat charges remaining, and level lock for T2/T3. |
|
||||
| `/buy <item>` | Purchase an item by name (partial match accepted). |
|
||||
| `/reminders` | Toggle per-command DM notifications. **All reminders are on by default.** Bot DMs you the moment each cooldown expires. |
|
||||
|
||||
---
|
||||
|
||||
### Jail system
|
||||
|
||||
`/crime` fail (without Mänguritool) jails you for **30 minutes**. While jailed, `/work`, `/beg`, `/crime`, `/rob`, and `/give` are blocked.
|
||||
|
||||
#### `/jailbreak`
|
||||
Roll two dice - matching values (doubles) free you instantly. **3 attempts** per jail sentence. If all 3 fail you pay bail:
|
||||
|
||||
- **20–30% of your current balance** (scales with wealth)
|
||||
- **Minimum 350 ⬡** - if your balance is below this you stay jailed until the timer runs out
|
||||
|
||||
Cooldowns and jail release times display as live Discord relative timestamps.
|
||||
|
||||
---
|
||||
|
||||
### Shop items
|
||||
|
||||
All items are **permanent** once purchased **except Anticheat**, which expires after 2 uses and can be repurchased.
|
||||
|
||||
#### Tier 1 - any level
|
||||
|
||||
| Item | Cost | Effect |
|
||||
|---|---|---|
|
||||
| Mängurihiir | 500 ⬡ | `/work` earns +50% |
|
||||
| XL hiirematt | 600 ⬡ | `/beg` cooldown 5min → 3min |
|
||||
| Anticheat | 750 ⬡ | Rob attempts against you fail and fine the robber. **2 uses**, then repurchase. |
|
||||
| Red Bull | 800 ⬡ | `/work` has 30% chance to earn ×3 |
|
||||
| Kõrvaklapid | 1 200 ⬡ | `/daily` cooldown 20h → 18h |
|
||||
| LAN Pilet | 1 200 ⬡ | `/daily` reward ×2 |
|
||||
| Botikoobas | 1 500 ⬡ | `/daily` adds 5% interest on balance (capped at 500 ⬡/day) |
|
||||
|
||||
#### Tier 2 - level 10 required (TipiHUSTLER+)
|
||||
|
||||
| Item | Cost | Effect |
|
||||
|---|---|---|
|
||||
| Mehhaaniline klaviatuur | 1 800 ⬡ | `/beg` earns ×2 |
|
||||
| Ultralai monitor | 2 500 ⬡ | `/work` cooldown 1h → 40min |
|
||||
| Mikrofon | 2 800 ⬡ | `/crime` win earns +30% |
|
||||
| Reguleeritav laud | 3 500 ⬡ | `/work` earns +25% (stacks with Mängurihiir → ×1.875 combined) |
|
||||
| CAT6 netikaabel | 3 500 ⬡ | `/crime` success rate 60% → 75% |
|
||||
| Jellyfin server | 4 000 ⬡ | `/rob` success rate 45% → 60% |
|
||||
|
||||
#### Tier 3 - level 20 required (TipiCHAD+)
|
||||
|
||||
| Item | Cost | Effect |
|
||||
|---|---|---|
|
||||
| TipiLAN trofee | 6 000 ⬡ | Daily streak survives missed days |
|
||||
| 360hz monitor | 7 500 ⬡ | Slots jackpot 10× → 15×, triple 4× → 6× |
|
||||
| Mänguritool | 9 000 ⬡ | `/crime` fail never sends you to jail |
|
||||
|
||||
---
|
||||
|
||||
### Amount shortcuts
|
||||
|
||||
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 `economy.py` to any Discord emoji string:
|
||||
```python
|
||||
COIN = "<:tipicoin:YOUR_EMOJI_ID>"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
All logs are written to the `logs/` directory (auto-created on startup).
|
||||
|
||||
| File | Rotation | Contents |
|
||||
|---|---|---|
|
||||
| `logs/bot.log` | 5 MB x 5 backups | All INFO+ events: commands, errors, member sync |
|
||||
| `logs/transactions.log` | Daily, 30 days | Economy transactions only: every balance change with user, amount, new balance |
|
||||
|
||||
The terminal output is **colour-coded** by log level (green = INFO, yellow = WARNING, red = ERROR).
|
||||
|
||||
Every slash command invocation is logged with the user ID, display name, and all options passed.
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
├── bot.py # Discord client, all slash commands, event handlers
|
||||
├── economy.py # TipiCOIN business logic, constants (SHOP, COOLDOWNS, etc.)
|
||||
├── pb_client.py # Async PocketBase REST client (auth + CRUD for economy_users)
|
||||
├── strings.py # All user-facing strings, command descriptions, help text
|
||||
├── member_sync.py # Role/nickname/birthday sync logic
|
||||
├── sheets.py # Google Sheets read/write + in-memory cache
|
||||
├── config.py # Environment variable loader
|
||||
├── requirements.txt # Python dependencies
|
||||
├── .env.example # Template for secrets
|
||||
├── .env # Your secrets (gitignored)
|
||||
├── credentials.json # Google service account key (gitignored)
|
||||
├── docs/
|
||||
│ ├── DEV_NOTES.md # Developer reference (architecture, checklists, constants)
|
||||
│ ├── CHANGELOG.md # Version history
|
||||
│ └── POCKETBASE_SETUP.md # PocketBase collection schema + setup instructions
|
||||
├── scripts/
|
||||
│ ├── migrate_to_pb.py # One-time migration: economy.json → PocketBase
|
||||
│ └── add_stats_fields.py # Schema migration: add new fields to economy_users collection
|
||||
├── data/
|
||||
│ └── birthday_sent.json # Birthday dedup log (auto-created)
|
||||
├── pb_data/ # PocketBase database files (auto-created, gitignored)
|
||||
└── logs/
|
||||
├── bot.log # General rotating log (auto-created)
|
||||
└── transactions.log # Daily economy transaction log (auto-created)
|
||||
```
|
||||
Reference in New Issue
Block a user