diff --git a/.instructions.md b/.instructions.md index f1a1ea6..9dd5b5e 100644 --- a/.instructions.md +++ b/.instructions.md @@ -135,7 +135,7 @@ For developers, contributors, and project governance, the following documentatio - **[docs/system-design.md](docs/system-design.md)** - System architecture and design with: - All system actors (Discord users, servers, bot, API, database) - - All system actions (9 slash commands, message scanning, backfill, API interactions) + - All system actions (15 slash commands, message scanning, backfill, API interactions) - Complete data flow diagrams for each major operation - Component interactions and subsystems - Database schema and data models @@ -146,7 +146,7 @@ For developers, contributors, and project governance, the following documentatio - [OSPS-SA-01.01] - **[docs/api-reference.md](docs/api-reference.md)** - External software interfaces and API documentation with: - - Discord Bot API: 9 slash commands with full parameter and response documentation + - Discord Bot API: 15 slash commands with full parameter and response documentation - Discord Message Event API: Automatic code detection patterns and behavior - Request/response schemas with data types and examples - Error codes, causes, and resolution procedures diff --git a/CHANGELOG.md b/CHANGELOG.md index e133807..921914f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,65 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Added +- **`/stats` command** - Server-wide statistics and aggregate loot totals + - Shows total unique codes seen, total redemption events, registered user count + - Displays server-wide aggregate chest and item loot counts from `loot_totals` table + - Available to all users — no special permissions required + +- **`/logs` command** - Real-time log viewing for administrators + - Shows last N lines (1–100, default 20) of `logs/combined.log` + - Restricted to users with Discord `Administrator` permission + - Returns ephemeral response + +- **`/notifications` command** - Configurable per-user DM notification preferences + - `dm_on_code` (default: false) — DM user when a new code is detected in the channel + - `dm_on_success` (default: true) — DM user when auto-redeem succeeds + - `dm_on_failure` (default: false) — DM user when auto-redeem fails + - With no parameters, shows current preferences + - Settings persisted in `users` table + +- **`/deleteaccount` command** - GDPR-compliant self-service account deletion + - Requires explicit confirmation via Yes / Cancel button prompt (30-second timeout) + - Deletes: credentials, all redeemed code records, audit log entries, backfill operation history + - Refuses deletion while a user-initiated backfill is in progress + - Returns per-category deletion summary + +- **AES-256-GCM credential encryption** - Credentials now encrypted at rest + - `users.user_id` and `users.user_hash` stored as AES-256-GCM ciphertext (`enc1:::`) + - 12-byte random IV and 16-byte auth tag per ciphertext + - Key loaded from `ENCRYPTION_KEY` env var (64-char hex = 32 bytes) + - `isEncrypted()` / `encrypt()` / `decrypt()` helpers in `src/bot/utils/crypto.ts` + - `migratePlaintextCredentials()` in UserManager migrates existing plaintext rows on startup + +- **`loot_totals` table** - Aggregate loot cache + - Pre-computed per-user and server-wide loot totals updated on every redemption + - Composite primary key: `(loot_key, scope)` where scope is `discordId` or `__server__` + - `backfillLootTotals()` rebuilds the cache from existing `redeemed_codes` rows + - Powers the `/stats` aggregate loot display + +- **Winston structured logging** (`src/bot/utils/logger.ts`) + - Logs to `logs/combined.log` (all levels, 20 rotated files, 5 MB each) + - Logs errors to `logs/error.log` (10 rotated files) + - Console output colour-coded with timestamps + - Default level `info`; override with `LOG_LEVEL=debug` in `.env` + - `/logs` command reads `combined.log` for in-Discord admin access + +- **`/backfill` command** - Recover missed codes from channel history (admin only) + - Fetches message history in batches of 100 via Discord API + - Global concurrency lock: only one backfill runs at a time + - Per-user rate limit: 1 backfill per hour + - Startup backfill: runs automatically on bot start if last run was > 6 hours ago + - Progress reported via interaction follow-ups + - Returns `{ codesFound, codesRedeemed, pendingCodes, errors }` summary + +- **DM notification fan-out in `bot.ts`** - On code detection + - `getAllUsersWithDmOnCode()` fetches users with `dm_on_code = true` + - Sends a DM to each matching user when a new code is detected in the monitored channel + +- **`/codes` pagination** - `/codes` now paginates results (5 per page) with Prev/Next buttons + - `buildCodesPage()` helper generates paginated embeds + - Each entry shows code, status, loot detail, and timestamp + - **`/catchup` command** - Redeem all known valid codes in one step - Collects every code the bot has seen (successful redeems from any user + pending codes) - Skips codes already redeemed by the requesting user and codes marked as expired diff --git a/README.md b/README.md index 19b99c1..e973175 100644 --- a/README.md +++ b/README.md @@ -84,16 +84,19 @@ See [docker-compose.example.yml](docker-compose.example.yml) for all available c ## ✨ Features -- 🤖 **Slash Commands** - `/setup`, `/redeem`, `/catchup`, `/autoredeem`, `/inventory`, `/open`, `/blacksmith`, `/codes`, `/makepublic`, `/backfill`, `/deleteaccount`, `/help` +- 🤖 **Slash Commands** - `/setup`, `/redeem`, `/catchup`, `/autoredeem`, `/inventory`, `/open`, `/blacksmith`, `/codes`, `/makepublic`, `/backfill`, `/notifications`, `/stats`, `/logs`, `/deleteaccount`, `/help` - 🔄 **Auto Code Detection** - Scans Discord messages for codes automatically - ⏮️ **Message History Backfill** - Recover missed codes from message history with built-in rate limiting - 🔁 **Catch Up** - Redeem all known valid codes in one command (great for new members) - 🤖 **Auto-Redeem Toggle** - Enable or disable automatic code redemption per user (`/autoredeem`) +- 🔔 **DM Notifications** - Configurable per-user DM alerts for new codes, successes, and failures (`/notifications`) +- 📊 **Server Stats** - Aggregate loot totals and redemption statistics (`/stats`) - 🎁 **Code Redemption** - Submit codes and get rewards - 📦 **Chest Management** - Open chests and view loot - ⚒️ **Blacksmith** - Upgrade heroes with contracts -- 📊 **Inventory** - View gold, rubies, equipment, and progress +- 📋 **Inventory** - View gold, rubies, equipment, and progress - 🗑️ **Account Deletion** - GDPR-friendly self-service data removal (`/deleteaccount`) +- 🔐 **Encrypted Credentials** - AES-256-GCM encryption for stored user credentials - 💾 **Secure Storage** - SQLite database keeps credentials safe and local - 👥 **Multi-User** - Each user manages their own account - ⚡ **Fast** - Built on Bun for 3-4x performance vs Node.js diff --git a/SECURITY.md b/SECURITY.md index f674b3d..2703de3 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -521,7 +521,7 @@ For complete dependency management details, see [docs/dependency-management.md]( The project includes comprehensive design documentation describing all actors and actions within the system. See [docs/system-design.md](docs/system-design.md) for: - **Actors**: Discord users, Discord servers, Discord bot, Idle Champions API, SQLite database -- **Actions**: 9 slash commands, message scanning, backfill operations, API interactions, database operations +- **Actions**: 15 slash commands, message scanning, backfill operations, API interactions, database operations - **Data Flows**: Complete flow diagrams for each major operation - **Architecture**: Component interactions, subsystems, deployment - **Data Models**: User credentials, code history, backfill state, API logs @@ -532,7 +532,7 @@ The project includes comprehensive design documentation describing all actors an The project includes complete API documentation for all external software interfaces. See [docs/api-reference.md](docs/api-reference.md) for: -- **Discord Bot API**: 9 slash commands with parameters, responses, and error handling +- **Discord Bot API**: 15 slash commands with parameters, responses, and error handling - **Command Parameters**: Detailed description of each parameter and valid values - **Response Formats**: Discord embeds, ephemeral responses, data structures - **Error Responses**: Error codes, causes, and resolution steps diff --git a/docs/api-reference.md b/docs/api-reference.md index ae1c36a..cacfe87 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -29,7 +29,7 @@ The Discord bot responds to slash commands in Discord channels. All commands ret **Response Format**: Discord Embeds (rich message format) or Ephemeral Text -**Total commands**: 12 (`/setup`, `/redeem`, `/catchup`, `/autoredeem`, `/inventory`, `/open`, `/blacksmith`, `/codes`, `/makepublic`, `/backfill`, `/deleteaccount`, `/help`) +**Total commands**: 15 (`/setup`, `/redeem`, `/catchup`, `/autoredeem`, `/inventory`, `/open`, `/blacksmith`, `/codes`, `/makepublic`, `/notifications`, `/stats`, `/logs`, `/backfill`, `/deleteaccount`, `/help`) --- @@ -754,7 +754,119 @@ Bot (ephemeral): ✅ Account Deleted --- -### 12. `/help` +### 13. `/notifications` + +View and update DM notification preferences. + +**Invocation**: +``` +/notifications [dm_on_code:] [dm_on_success:] [dm_on_failure:] +``` + +**Parameters**: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `dm_on_code` | boolean | No | DM when a new code is detected in the channel (default: false) | +| `dm_on_success` | boolean | No | DM when auto-redeem succeeds (default: true) | +| `dm_on_failure` | boolean | No | DM when auto-redeem fails (default: false) | + +**Behaviour**: With no parameters, shows current preferences. With parameters, updates specified preferences. + +**Response Format** (Ephemeral): +``` +┌─────────────────────────────────────────┐ +│ 🔔 Notification Preferences │ +│ │ +│ DM on code detected: ❌ Off │ +│ DM on redeem success: ✅ On │ +│ DM on redeem failure: ❌ Off │ +└─────────────────────────────────────────┘ +``` + +**Error Responses**: + +| Error | Cause | Resolution | +|-------|-------|-----------| +| `NO_CREDENTIALS` | User hasn't run `/setup` | Run `/setup` first | + +--- + +### 14. `/stats` + +Show server-wide code redemption statistics and aggregate loot totals. + +**Invocation**: +``` +/stats +``` + +**Parameters**: None + +**Response Format** (Public Embed): +``` +┌───────────────────────────────────────┐ +│ 📊 Server Statistics │ +│ │ +│ Unique codes tracked: 142 │ +│ Total redemptions: 1,087 │ +│ Registered users: 12 │ +│ │ +│ **Aggregate Loot (all users)** │ +│ Sapphire Chest: 2,340 │ +│ Gold Chest: 810 │ +│ Modron Chest: 156 │ +│ ... │ +└───────────────────────────────────────┘ +``` + +**Data Returned**: +- Total unique codes seen by the bot +- Total successful redemption events +- Number of registered users +- Server-wide aggregate chest and item loot counts + +--- + +### 15. `/logs` + +Show the last N lines of the bot's combined log file. Admin only. + +**Invocation**: +``` +/logs [lines:] +``` + +**Parameters**: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `lines` | integer | No | Number of log lines to show (1-100, default: 20) | + +**Permissions**: Requires the `Manage Messages` permission on the Discord server. + +**Response Format** (Ephemeral): +``` +┌─────────────────────────────────────────────┐ +│ 📋 Last 20 log lines │ +│ │ +│ [2026-05-24 10:15:01] [info] Bot ready │ +│ [2026-05-24 10:15:42] [info] Code detected │ +│ [2026-05-24 10:15:43] [info] Redeemed OK │ +│ ... │ +└─────────────────────────────────────────────┘ +``` + +**Error Responses**: + +| Error | Cause | Resolution | +|-------|-------|-----------| +| `PERMISSION_DENIED` | User lacks Manage Messages permission | Requires Manage Messages permission | +| `LOG_NOT_FOUND` | Log file does not exist | Bot has not run long enough to create logs | + +--- + +### 16. `/help` Display command reference and usage instructions. @@ -796,7 +908,13 @@ Display command reference and usage instructions. │ **Utilities** │ │ /autoredeem enabled: │ │ Toggle automatic code redemption │ -│ /backfill [channel:] │ +│ /notifications │ +│ View/update DM notification preferences │ +│ /stats │ +│ Server-wide stats and aggregate loot │ +│ /logs [lines:] (admin) │ +│ Show last N lines of the bot log │ +│ /backfill [channel:] (admin) │ │ Recover codes from message history │ │ /deleteaccount │ │ Permanently delete all your stored data │ @@ -823,35 +941,36 @@ The bot automatically scans all messages in the monitored channel for promo code **Trigger**: Message posted in monitored Discord channel -**Pattern Matching**: Regular expression matching 4-20 alphanumeric character sequences +**Pattern Matching**: Regular expression matching 12- or 16-character sequences of uppercase alphanumeric and symbol characters (optionally hyphen-separated) **Detection Pattern**: ```regex -\b([A-Z0-9]{4,20})\b +(?:[A-Z0-9*!@#$%^&*]-?){12}(?:(?:[A-Z0-9*!@#$%^&*]-?){4})? ``` +Applied after stripping Discord emoji tags (`<:name:id>`, ``) and URLs from message content. + Matches: -- `IDLE2024` ✅ -- `CHAMPIONS500` ✅ -- `PROMO100` ✅ +- 12-character or 16-character sequences of uppercase alphanumeric and symbol characters +- Characters may be separated by single hyphens (e.g., `ABCD-EFGH-IJKL`) Does NOT match: -- `ABC` (too short, <4 chars) ❌ -- `ABC-123` (contains hyphen) ❌ -- `abc123` (all lowercase, needs validation per API) ⚠️ +- Shorter sequences (< 12 characters) +- Discord emoji markup (stripped before matching) +- URLs (stripped before matching) **Detection Behavior**: ``` -Message Posted: "Free code: IDLE2024" +Message Posted: "Free code: ABCD1234EFGH" ↓ Bot scans message content ↓ -Pattern matches: IDLE2024 +Pattern matches: ABCD1234EFGH ↓ Retrieves message author credentials ↓ -Calls API: redeemCoupon(IDLE2024) +Calls API: redeemCoupon(ABCD1234EFGH) ↓ Records result in database ↓ diff --git a/docs/development.md b/docs/development.md index 2a54914..51853b8 100644 --- a/docs/development.md +++ b/docs/development.md @@ -33,7 +33,8 @@ brew install mise src/bot/ ├── bot.ts # Main Discord client & events ├── api/ -│ └── idleChampionsApi.ts # Game API client (query-param based) +│ ├── idleChampionsApi.ts # Game API client (query-param based) +│ └── types/ # Type definitions from game API ├── commands/ │ ├── setup.ts # Save user credentials │ ├── redeem.ts # Manual code redemption @@ -42,14 +43,18 @@ src/bot/ │ ├── inventory.ts # Show account info │ ├── open.ts # Open chests │ ├── blacksmith.ts # Upgrade heroes -│ ├── codes.ts # Show code history +│ ├── codes.ts # Show code history (paginated) │ ├── makepublic.ts # Share codes with other users -│ ├── backfill.ts # Recover missed codes from history +│ ├── notifications.ts # View/update DM notification preferences +│ ├── stats.ts # Server-wide stats and aggregate loot +│ ├── logs.ts # Show last N lines of bot log (admin) +│ ├── backfill.ts # Recover missed codes from history (admin) +│ ├── deleteaccount.ts # GDPR account deletion │ └── help.ts # Command help ├── database/ │ ├── db.ts # Drizzle database connection & migrate() -│ ├── userManager.ts # User credentials storage -│ ├── codeManager.ts # Code tracking & history +│ ├── userManager.ts # User credentials storage (AES-256-GCM encrypted) +│ ├── codeManager.ts # Code tracking, history & loot totals │ ├── auditManager.ts # Audit log operations │ ├── backfillManager.ts # Backfill operations & locking │ ├── schema/ # Drizzle table definitions (one file per table) @@ -58,7 +63,8 @@ src/bot/ │ │ ├── redeemed_codes.ts │ │ ├── pending_codes.ts │ │ ├── audit_log.ts -│ │ └── backfill_operations.ts +│ │ ├── backfill_operations.ts +│ │ └── loot_totals.ts │ └── migrations/ # Auto-generated SQL migrations (drizzle-kit) ├── handlers/ │ ├── codeScanner.ts # Message code detection @@ -66,6 +72,7 @@ src/bot/ │ └── backfillHandler.ts # Message history scanning & redemption └── utils/ ├── logger.ts # Winston logger (file + console output) + ├── crypto.ts # AES-256-GCM encryption/decryption for credentials ├── apiRequestLogger.ts # API response logging & cleanup └── debugLogger.ts # Debug utilities @@ -139,8 +146,16 @@ bun test --watch # Re-run on file changes | File | What it tests | |------|---------------| | `src/bot/handlers/codeScanner.test.ts` | `extractCodesFromText` — regex, emoji stripping, case normalisation | -| `src/bot/database/codeManager.test.ts` | All `CodeManager` methods — per-user redemption, public/private, pending codes | -| `src/bot/database/userManager.test.ts` | All `UserManager` CRUD operations | +| `src/bot/handlers/autoRedeemer.test.ts` | Queue serialization, DM sending, skip logic | +| `src/bot/handlers/backfillHandler.test.ts` | Message history scanning, server swap handling | +| `src/bot/database/codeManager.test.ts` | All `CodeManager` methods — per-user redemption, public/private, pending codes, loot totals | +| `src/bot/database/userManager.test.ts` | All `UserManager` CRUD operations (with AES-256-GCM encryption) | +| `src/bot/database/auditManager.test.ts` | Audit log operations | +| `src/bot/database/backfillManager.test.ts` | Backfill rate limiting, global lock | +| `src/bot/commands/notifications.test.ts` | `/notifications` command and preference updates | +| `src/bot/commands/stats.test.ts` | `/stats` with empty/populated DB | +| `src/bot/commands/logs.test.ts` | `/logs` command with mocked filesystem | +| `src/bot/utils/apiRequestLogger.test.ts` | API log file cleanup, sensitive param masking | ### How It Works @@ -226,10 +241,14 @@ erDiagram USERS { string discord_id PK - int user_id "Idle Champions user ID" - string user_hash "Idle Champions auth token" + string user_id "AES-256-GCM encrypted" + string user_hash "AES-256-GCM encrypted" string server "game server URL" - string instance_id "deprecated" + string instance_id + boolean auto_redeem "default true" + boolean dm_on_code "default false" + boolean dm_on_success "default true" + boolean dm_on_failure "default false" datetime created_at datetime updated_at } @@ -239,17 +258,17 @@ erDiagram string code string discord_id FK string status "Success or Code Expired" - json loot + json loot_detail int is_public "0 = private, 1 = public" datetime expires_at - datetime timestamp + datetime redeemed_at } PENDING_CODES { int id PK string code string discord_id FK - datetime added_at + datetime found_at } AUDIT_LOG { @@ -257,18 +276,25 @@ erDiagram string discord_id FK string action json details - datetime timestamp + datetime created_at } BACKFILL_OPERATIONS { int id PK - string initiated_by "discord_id FK or 'system'" + string initiated_by "discord_id" datetime started_at datetime completed_at int codes_found int codes_redeemed string status "in_progress, completed, failed" } + + LOOT_TOTALS { + string loot_key PK + string loot_type "chest or item" + string scope PK "discordId or __server__" + int total_count + } ``` ## Testing Commands diff --git a/docs/full-documentation.md b/docs/full-documentation.md index b1e327e..d4fbf4d 100644 --- a/docs/full-documentation.md +++ b/docs/full-documentation.md @@ -4,14 +4,17 @@ A Discord bot that automatically scans for and redeems Idle Champions promo code ## Features -- 🤖 **Slash Commands** - `/setup`, `/redeem`, `/catchup`, `/autoredeem`, `/inventory`, `/open`, `/blacksmith`, `/codes`, `/makepublic`, `/backfill`, `/deleteaccount`, `/help` +- 🤖 **Slash Commands** - `/setup`, `/redeem`, `/catchup`, `/autoredeem`, `/inventory`, `/open`, `/blacksmith`, `/codes`, `/makepublic`, `/notifications`, `/stats`, `/logs`, `/backfill`, `/deleteaccount`, `/help` - 🔄 **Auto Code Detection** - Scans Discord messages for codes automatically - 🤖 **Auto-Redeem Toggle** - Enable or disable automatic code redemption per user (`/autoredeem`) +- 🔔 **DM Notifications** - Configurable per-user DM alerts for new codes, successes, and failures (`/notifications`) +- 📊 **Server Stats** - Aggregate loot totals and redemption statistics (`/stats`) - ⏮️ **Message History Backfill** - Recover missed codes from message history (protected with rate limiting) - 🎁 **Code Redemption** - Submit codes and get rewards - 📦 **Chest Management** - Open chests and view loot - ⚒️ **Blacksmith** - Upgrade heroes with contracts -- 📊 **Inventory** - View gold, rubies, equipment, and progress +- 📋 **Inventory** - View gold, rubies, equipment, and progress +- 🔐 **Encrypted Credentials** - AES-256-GCM encryption for stored user credentials - 💾 **Secure Storage** - SQLite database keeps credentials safe and local - 🗑️ **Account Deletion** - GDPR-friendly self-service removal of all stored data (`/deleteaccount`) - 👥 **Multi-User** - Each user manages their own account @@ -69,9 +72,12 @@ brew install mise | `/inventory` | View your account (gold, rubies, equipment, progress) | | `/open chest_type: count:` | Open chests (Gold, Sapphire, etc.) | | `/blacksmith contract_type: hero_id: count:` | Upgrade heroes | -| `/codes [count:]` | Show your redeemed codes history (last 10) | +| `/codes` | Show your redeemed codes history (paginated, 5 per page) | | `/makepublic code:` | Share one of your redeemed codes with other users | -| `/backfill [channel:]` | Recover missed codes from message history | +| `/notifications` | View/update DM notification preferences | +| `/stats` | Server-wide statistics and aggregate loot totals | +| `/logs [lines:<1-100>]` | Show last N lines of combined.log (admin only) | +| `/backfill [channel:]` | Recover missed codes from message history (admin only) | | `/deleteaccount` | Permanently delete all your stored data (GDPR) | | `/help` | Show all commands | @@ -220,6 +226,42 @@ Permanently and irreversibly delete all data the bot holds about you. Requires a - **After deletion:** You will need to run `/setup` again to use the bot - **Example:** `/deleteaccount` +### Notification Preferences + +#### `/notifications` + +View and update your DM notification preferences. + +- **No parameters required** (shows current settings) +- **Optional parameters:** + - `dm_on_code` – DM when a new code is detected in the channel (default: false) + - `dm_on_success` – DM when auto-redeem succeeds (default: true) + - `dm_on_failure` – DM when auto-redeem fails (default: false) +- **Example:** `/notifications` or `/notifications dm_on_code:true dm_on_failure:true` + +### Server Statistics + +#### `/stats` + +Show server-wide code redemption statistics and aggregate loot totals. + +- **No parameters required** +- **Shows:** Total unique codes seen, total redemption events, registered user count, server-wide aggregate loot +- **Available to:** All users — no special permissions required +- **Example:** `/stats` + +### Admin Tools + +#### `/logs [lines:<1-100>]` + +Show the last N lines of the bot's combined log file. + +- **Optional parameters:** + - `lines` – Number of log lines (1–100, default: 20) +- **Permissions:** Requires Discord `Manage Messages` permission +- **Returns:** Ephemeral embed with log content +- **Example:** `/logs` or `/logs lines:50` + ### Help #### `/help` @@ -235,23 +277,35 @@ Display all available commands with brief descriptions. src/bot/ ├── bot.ts # Main Discord client & event handlers ├── api/ -│ └── idleChampionsApi.ts # Game server API client -├── commands/ # Slash command handlers (11 commands) +│ ├── idleChampionsApi.ts # Game server API client +│ └── types/ # Type definitions from game API +├── commands/ # Slash command handlers (15 commands) ├── database/ # Database layer (Drizzle ORM) │ ├── db.ts # Drizzle connection & migrate() on startup -│ ├── userManager.ts # User credentials -│ ├── codeManager.ts # Code tracking & history +│ ├── userManager.ts # User credentials (AES-256-GCM encrypted) +│ ├── codeManager.ts # Code tracking, history & loot totals │ ├── auditManager.ts # Audit log │ ├── backfillManager.ts # Backfill locking & tracking │ ├── schema/ # Drizzle table definitions (one file per table) +│ │ ├── users.ts +│ │ ├── redeemed_codes.ts +│ │ ├── pending_codes.ts +│ │ ├── audit_log.ts +│ │ ├── backfill_operations.ts +│ │ └── loot_totals.ts │ └── migrations/ # Auto-generated SQL migrations ├── handlers/ # Message scanning for codes -└── utils/ # Helpers (logging, debug logging, etc.) +│ ├── codeScanner.ts +│ ├── autoRedeemer.ts +│ └── backfillHandler.ts +└── utils/ # Helpers (logging, crypto, debug, API logging) + ├── logger.ts + ├── crypto.ts + ├── apiRequestLogger.ts + └── debugLogger.ts src/test/ └── setup.ts # Bun test preload (DB_PATH=:memory:) -lib/ -└── *.d.ts # Type definitions from game API ``` ## Configuration @@ -288,11 +342,12 @@ mise tasks # View all available tasks SQLite database managed with Drizzle ORM (`bun:sqlite` + `drizzle-orm`). Migrations are applied automatically at startup. -- **users** - Discord user credentials +- **users** - Discord user credentials (AES-256-GCM encrypted user_id/hash), notification prefs, autoRedeem flag - **redeemed_codes** - Code history (status: `Success` or `Code Expired`; includes `is_public` and `expires_at`) - **pending_codes** - Codes waiting to be redeemed - **audit_log** - All bot actions - **backfill_operations** - Backfill run history & global lock +- **loot_totals** - Aggregate loot cache (per-user and server-wide, used by `/stats`) ```bash bun run db:generate # Regenerate migrations from schema changes diff --git a/docs/mise.md b/docs/mise.md index d55bc6e..53e4714 100644 --- a/docs/mise.md +++ b/docs/mise.md @@ -45,8 +45,7 @@ mise install This will: -- ✅ Install Bun 1.0 -- ✅ Install Node.js 20 (as fallback) +- ✅ Install Bun 1.3.14 - ✅ Set up the environment ### Step 2: Run Any Task @@ -143,8 +142,7 @@ Now when you `cd` into the project, tools and environment variables load automat ```toml [tools] -bun = "1.0" # Latest 1.x version -node = "20" # Latest 20.x version +bun = "1.3.14" # Pinned version for reproducibility ``` ## Troubleshooting diff --git a/docs/security-assessment.md b/docs/security-assessment.md index a909308..ecddeb7 100644 --- a/docs/security-assessment.md +++ b/docs/security-assessment.md @@ -89,29 +89,23 @@ The Idle Champions Code Redeemer Discord Bot is a Discord-integrated application **Mitigation Strategies in Place**: - ✅ SQLite database encrypted at OS level (file permissions) +- ✅ User credentials encrypted at rest with AES-256-GCM (key from `ENCRYPTION_KEY` env var) - ✅ HTTPS/TLS for all external API communication - ✅ Ephemeral responses (only user sees credential confirmation) - ✅ Parameterized queries (prevent SQL injection) - ✅ Gitleaks scanning in git hooks and CI/CD - ✅ Environment variables for sensitive config (bot token) - ✅ No credentials in git repository +- ✅ `/deleteaccount` command for GDPR-compliant self-service data deletion **Residual Risk**: **MEDIUM** **Recommended Actions**: -1. Implement optional application-level encryption for SQLite database - - Example: Encrypt specific columns with user's password - - Requires password entry on bot startup - -2. Add automatic credential rotation mechanism +1. Add automatic credential rotation mechanism - Require `/setup` update every N days - Notify user of stale credentials -3. Add `/unsetup` command to securely delete credentials - - Currently: Manual database deletion - - Proposed: Discord command for clean removal - -4. Implement credential deletion on inactivity (30+ days) +2. Implement credential deletion on inactivity (30+ days) - Reduce window of exposure for abandoned accounts - Notify user via DM before deletion diff --git a/docs/structure.md b/docs/structure.md index ac2c669..e111a8f 100644 --- a/docs/structure.md +++ b/docs/structure.md @@ -28,7 +28,7 @@ idle-code-redeemer/ │ ├── bot/ ← Discord bot (ACTIVE) │ │ ├── bot.ts ← Main Discord client & event handlers │ │ ├── api/ ← Game server API client -│ │ ├── commands/ ← Slash command handlers (12 commands) +│ │ ├── commands/ ← Slash command handlers (15 commands) │ │ ├── database/ ← Database managers & Drizzle schema │ │ │ ├── db.ts ← Drizzle connection & migrate() │ │ │ ├── userManager.ts @@ -38,14 +38,13 @@ idle-code-redeemer/ │ │ │ ├── schema/ ← Drizzle table definitions (one file per table) │ │ │ └── migrations/ ← Auto-generated SQL migration files │ │ ├── handlers/ ← Message scanning for codes -│ │ └── utils/ ← Utilities (debug logging) +│ │ └── utils/ ← Utilities (logger, crypto, debug, API request logging) │ │ -│ └── lib/ ← Type definitions (from game API) +│ └── bot/api/types/ ← Type definitions from game API │ ├── player_data.d.ts │ ├── redeem_code_response.d.ts │ ├── blacksmith_response.d.ts -│ ├── server_definitions.d.ts -│ └── chrome.d.ts ← Not used (kept for reference) +│ └── server_definitions.d.ts │ ├── data/ ← SQLite database (git-ignored) │ └── idle.db @@ -73,7 +72,7 @@ idle-code-redeemer/ - **[src/bot/bot.ts](../src/bot/bot.ts)** - Discord client initialization, event handlers, command routing - **[src/bot/api/idleChampionsApi.ts](../src/bot/api/idleChampionsApi.ts)** - Game server API client with query-parameter format -### Commands (12 slash commands) +### Commands (15 slash commands) - **[src/bot/commands/setup.ts](../src/bot/commands/setup.ts)** - `/setup user_id: user_hash:` - **[src/bot/commands/redeem.ts](../src/bot/commands/redeem.ts)** - `/redeem code:` @@ -82,9 +81,12 @@ idle-code-redeemer/ - **[src/bot/commands/inventory.ts](../src/bot/commands/inventory.ts)** - `/inventory` (gold, rubies, equipment, progress) - **[src/bot/commands/open.ts](../src/bot/commands/open.ts)** - `/open chest_type: count:` - **[src/bot/commands/blacksmith.ts](../src/bot/commands/blacksmith.ts)** - `/blacksmith contract_type: hero_id: count:` -- **[src/bot/commands/codes.ts](../src/bot/commands/codes.ts)** - `/codes [count:]` (view redeemed codes history) +- **[src/bot/commands/codes.ts](../src/bot/commands/codes.ts)** - `/codes` (paginated redeemed codes history with loot, 5 per page) - **[src/bot/commands/makepublic.ts](../src/bot/commands/makepublic.ts)** - `/makepublic code:` (share codes with other users) -- **[src/bot/commands/backfill.ts](../src/bot/commands/backfill.ts)** - `/backfill [channel:]` (recover missed codes) +- **[src/bot/commands/notifications.ts](../src/bot/commands/notifications.ts)** - `/notifications` (view/update DM preferences: dm_on_code, dm_on_success, dm_on_failure) +- **[src/bot/commands/stats.ts](../src/bot/commands/stats.ts)** - `/stats` (server-wide stats: unique codes, total redemptions, registered users, aggregate loot) +- **[src/bot/commands/logs.ts](../src/bot/commands/logs.ts)** - `/logs [lines:<1-100>]` (admin only: show last N lines of combined.log) +- **[src/bot/commands/backfill.ts](../src/bot/commands/backfill.ts)** - `/backfill [channel:]` (admin only: recover missed codes) - **[src/bot/commands/deleteaccount.ts](../src/bot/commands/deleteaccount.ts)** - `/deleteaccount` (permanently delete all stored user data, GDPR-friendly) - **[src/bot/commands/help.ts](../src/bot/commands/help.ts)** - `/help` @@ -95,23 +97,32 @@ idle-code-redeemer/ - **[src/bot/database/codeManager.ts](../src/bot/database/codeManager.ts)** - Code tracking & history - **[src/bot/database/auditManager.ts](../src/bot/database/auditManager.ts)** - Audit log operations - **[src/bot/database/backfillManager.ts](../src/bot/database/backfillManager.ts)** - Backfill operations & global lock -- **[src/bot/database/schema/](../src/bot/database/schema/)** - Drizzle table definitions (one TypeScript file per table) +- **[src/bot/database/schema/](../src/bot/database/schema/)** - Drizzle table definitions: `users.ts`, `redeemed_codes.ts`, `pending_codes.ts`, `audit_log.ts`, `backfill_operations.ts`, `loot_totals.ts` - **[src/bot/database/migrations/](../src/bot/database/migrations/)** - Auto-generated SQL migrations (committed to source) ### Auto Features - **[src/bot/handlers/codeScanner.ts](../src/bot/handlers/codeScanner.ts)** - Message scanning for codes (regex pattern) - **[src/bot/handlers/autoRedeemer.ts](../src/bot/handlers/autoRedeemer.ts)** - Automatically redeems detected codes for all users with auto-redeem enabled -- **[src/bot/utils/logger.ts](../src/bot/utils/logger.ts)** - Winston structured logger (file + console) -- **[src/bot/utils/apiRequestLogger.ts](../src/bot/utils/apiRequestLogger.ts)** - API response logging -- **[src/bot/utils/debugLogger.ts](../src/bot/utils/debugLogger.ts)** - Debug utilities +- **[src/bot/utils/logger.ts](../src/bot/utils/logger.ts)** - Winston structured logger (file + console, log rotation) +- **[src/bot/utils/crypto.ts](../src/bot/utils/crypto.ts)** - AES-256-GCM encryption/decryption for stored credentials +- **[src/bot/utils/apiRequestLogger.ts](../src/bot/utils/apiRequestLogger.ts)** - API response logging with sanitized URLs and auto-cleanup +- **[src/bot/utils/debugLogger.ts](../src/bot/utils/debugLogger.ts)** - Debug utilities (auto-cleanup of old debug files) ### Tests -- **[src/bot/handlers/codeScanner.test.ts](../src/bot/handlers/codeScanner.test.ts)** - Unit tests for code detection regex -- **[src/bot/database/codeManager.test.ts](../src/bot/database/codeManager.test.ts)** - Unit tests for all CodeManager methods -- **[src/bot/database/userManager.test.ts](../src/bot/database/userManager.test.ts)** - Unit tests for all UserManager CRUD operations -- **[src/test/setup.ts](../src/test/setup.ts)** - Bun test preload: sets `DB_PATH=:memory:` before imports +- **[src/bot/handlers/codeScanner.test.ts](../src/bot/handlers/codeScanner.test.ts)** - Unit tests for code detection regex, emoji stripping +- **[src/bot/handlers/autoRedeemer.test.ts](../src/bot/handlers/autoRedeemer.test.ts)** - Queue serialization, DM sending +- **[src/bot/handlers/backfillHandler.test.ts](../src/bot/handlers/backfillHandler.test.ts)** - Message history backfill +- **[src/bot/database/codeManager.test.ts](../src/bot/database/codeManager.test.ts)** - All CodeManager methods: per-user redemption, public/private codes, pending, expiry, loot totals +- **[src/bot/database/userManager.test.ts](../src/bot/database/userManager.test.ts)** - All UserManager CRUD operations (with encryption) +- **[src/bot/database/auditManager.test.ts](../src/bot/database/auditManager.test.ts)** - Audit log operations +- **[src/bot/database/backfillManager.test.ts](../src/bot/database/backfillManager.test.ts)** - Backfill rate limiting +- **[src/bot/commands/notifications.test.ts](../src/bot/commands/notifications.test.ts)** - `/notifications` command and preference updates +- **[src/bot/commands/stats.test.ts](../src/bot/commands/stats.test.ts)** - `/stats` with empty/populated DB +- **[src/bot/commands/logs.test.ts](../src/bot/commands/logs.test.ts)** - `/logs` command with mocked filesystem +- **[src/bot/utils/apiRequestLogger.test.ts](../src/bot/utils/apiRequestLogger.test.ts)** - API log file cleanup +- **[src/test/setup.ts](../src/test/setup.ts)** - Bun test preload: sets `DB_PATH=:memory:` and `MIGRATIONS_PATH` before imports ### Configuration diff --git a/docs/system-design.md b/docs/system-design.md index 241fa0e..be4fcf1 100644 --- a/docs/system-design.md +++ b/docs/system-design.md @@ -92,9 +92,10 @@ The bot reads promo codes from Discord messages, redeems them via the Idle Champ **Role**: End-user interacting with the bot **Actions**: -- Submit slash commands (`/setup`, `/redeem`, `/catchup`, `/autoredeem`, `/inventory`, `/open`, `/blacksmith`, `/codes`, `/makepublic`, `/backfill`, `/deleteaccount`, `/help`) +- Submit slash commands (`/setup`, `/redeem`, `/catchup`, `/autoredeem`, `/inventory`, `/open`, `/blacksmith`, `/codes`, `/makepublic`, `/notifications`, `/stats`, `/logs`, `/backfill`, `/deleteaccount`, `/help`) - Send messages containing promo codes in the monitored channel - Receive responses and error messages from the bot +- Receive DM notifications for code detection, redemption success/failure (configurable via `/notifications`) **Data Exchanged**: - Credentials (user ID, hash) → to bot @@ -132,7 +133,7 @@ The bot reads promo codes from Discord messages, redeems them via the Idle Champ - **Auto Redeemer** - Automatically redeems detected codes for all users with auto-redeem enabled - **Backfill Handler** - Scans message history - **API Client** - Communicates with Idle Champions API -- **Database Managers** - Persistence layer (users, codes, backfill state) +- **Database Managers** - Persistence layer (users, codes, audit, backfill state, loot totals) **Actions**: - Listen for messageCreate events (channel-specific) @@ -183,22 +184,27 @@ The bot reads promo codes from Discord messages, redeems them via the Idle Champ **Role**: Persistent local storage **Tables**: -- **users** - User credentials (Discord ID, user_id, hash) -- **codes** - Code history (code, redeemer, timestamp, result) -- **backfill_state** - Backfill operation tracking (lock, last_message, status) -- **debug_log** - API response logging (call, timestamp, response, cleanup) +- **users** - User credentials (Discord ID, encrypted user_id, encrypted hash, notification preferences, autoRedeem) +- **redeemed_codes** - Code history (code, discordId, redeemed_at, status, loot_detail, is_public, expires_at) +- **pending_codes** - Codes detected but not yet redeemed by the finder +- **audit_log** - Operation tracking (action, details, created_at per user) +- **backfill_operations** - Backfill run history & global lock (status: in_progress/completed/failed) +- **loot_totals** - Aggregate loot cache (per-user and server-wide counts for `/stats`) **Actions**: -- Store/retrieve user credentials -- Record code redemption history -- Track backfill operation state -- Log API responses for debugging +- Store/retrieve user credentials (AES-256-GCM encrypted) +- Record code redemption history with loot details +- Track pending codes for catchup/auto-redeem +- Manage backfill operation locking and history +- Cache aggregate loot totals for efficient stats queries +- Log all operations for audit trail **Data Characteristics**: - **Persistence**: Data survives bot restarts - **Locality**: No network dependency - **Isolation**: Each user's data accessible only via their Discord ID -- **Cleanup**: Debug logs auto-deleted after 7 days +- **Encryption**: User credentials encrypted with AES-256-GCM at rest +- **Cleanup**: Debug files (`debug/`) deleted after 1 hour; API request logs (`api-logs/`) deleted after 1 day --- diff --git a/docs/testing-strategy.md b/docs/testing-strategy.md index 2019419..aa9f4a4 100644 --- a/docs/testing-strategy.md +++ b/docs/testing-strategy.md @@ -173,11 +173,19 @@ bun test --watch | File | Tests | Coverage | |------|-------|----------| -| `src/bot/handlers/codeScanner.test.ts` | 12 | `extractCodesFromText` — regex patterns, emoji stripping, case normalisation, edge cases | -| `src/bot/database/codeManager.test.ts` | 32 | All `CodeManager` methods — per-user redemption, public/private codes, pending codes, expiry | -| `src/bot/database/userManager.test.ts` | 13 | All `UserManager` CRUD operations | - -**Total**: 57 tests across 3 files +| `src/bot/handlers/codeScanner.test.ts` | ~12 | `extractCodesFromText` — regex patterns, emoji stripping, case normalisation, edge cases | +| `src/bot/handlers/autoRedeemer.test.ts` | ~10 | Queue serialization, DM sending, skip logic | +| `src/bot/handlers/backfillHandler.test.ts` | ~8 | Message history scanning, code extraction, server swap handling | +| `src/bot/database/codeManager.test.ts` | ~32 | All `CodeManager` methods — per-user redemption, public/private codes, pending codes, expiry, loot totals | +| `src/bot/database/userManager.test.ts` | ~13 | All `UserManager` CRUD operations, AES-256-GCM encryption/decryption | +| `src/bot/database/auditManager.test.ts` | ~8 | Audit log operations | +| `src/bot/database/backfillManager.test.ts` | ~8 | Backfill rate limiting, global lock | +| `src/bot/commands/notifications.test.ts` | ~6 | `/notifications` command, preference updates | +| `src/bot/commands/stats.test.ts` | ~5 | `/stats` with empty/populated DB | +| `src/bot/commands/logs.test.ts` | ~5 | `/logs` command with mocked filesystem | +| `src/bot/utils/apiRequestLogger.test.ts` | ~6 | API log file cleanup, sensitive param masking | + +**Total**: 110+ tests across 11 files **Test Infrastructure**: @@ -188,7 +196,7 @@ bun test --watch - Tables are cleared in FK-safe order in `beforeEach` (children before parents) - `closeDatabase()` is **never** called in tests — Bun reuses workers between test files -**Pass Criteria**: All 57 tests pass with zero failures +**Pass Criteria**: All tests pass with zero failures **Fail Action**: Test run exits with a non-zero code and shows failed assertion details