From 4af2690babdbf822776bd58ff9fafcd7a1449059 Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Wed, 14 Jan 2026 16:10:23 +0100 Subject: [PATCH] feat: implement branded discord embeds and versioning --- .agent/skills/create-ticket/SKILL.md | 51 ++++++++++++++++++ .agent/skills/review/SKILL.md | 54 +++++++++++++++++++ .../001-refactor-command-validation.md | 30 +++++++++++ .../tickets/002-centralized-error-logging.md | 31 +++++++++++ .../work/tickets/003-modularize-web-server.md | 32 +++++++++++ .../tickets/004-branded-discord-embeds.md | 28 ++++++++++ .../work/tickets/005-refactor-exam-logic.md | 29 ++++++++++ .gitignore | 1 - bot/lib/embeds.ts | 34 +++++++++--- bun.lock | 2 +- package.json | 1 + shared/lib/constants.ts | 6 +++ tsconfig.json | 1 + 13 files changed, 291 insertions(+), 9 deletions(-) create mode 100644 .agent/skills/create-ticket/SKILL.md create mode 100644 .agent/skills/review/SKILL.md create mode 100644 .agent/work/tickets/001-refactor-command-validation.md create mode 100644 .agent/work/tickets/002-centralized-error-logging.md create mode 100644 .agent/work/tickets/003-modularize-web-server.md create mode 100644 .agent/work/tickets/004-branded-discord-embeds.md create mode 100644 .agent/work/tickets/005-refactor-exam-logic.md diff --git a/.agent/skills/create-ticket/SKILL.md b/.agent/skills/create-ticket/SKILL.md new file mode 100644 index 0000000..f854aea --- /dev/null +++ b/.agent/skills/create-ticket/SKILL.md @@ -0,0 +1,51 @@ +--- +name: create-ticket +description: Create a ticket for a task that needs to be worked on. +--- + +# Skill: create-ticket + +## Purpose + +Decompose high-level objectives into "Atomic Tickets" to maximize development velocity and minimize cognitive overhead. + +## Execution Rules + +1. **Directory Check:** Scan `.agent/work/tickets/`. Determine sequence number `NNN`. +2. **Naming:** `.agent/work/tickets/NNN-brief-description.md`. +3. **The Atomic/Velocity Test:** - Max 3 files modified. + - Max 80 lines of logic. + - Must be verifiable via a single command or test suite. + - If it exceeds these, **split it.** +4. **Context Injection:** Include relevant code snippets or interface definitions directly in the ticket to prevent "context hunting." +5. **No Breaking Changes:** If a ticket changes a shared interface, it must include the refactor for consumers or be split into a "Transition" ticket. + +## Ticket Template + +### Context & Goal + +[Why this matters + the specific problem it solves.] + +### Dependencies + +- [e.g., Ticket #NNN] + +### Affected Files + +- `path/to/file_A.ext`: [Specific change description] +- `path/to/file_B.ext`: [Specific change description] + +### Technical Constraints & Strategy + +- [e.g., Implementation: Use the existing X wrapper instead of a new fetch call.] +- [e.g., Constraint: Maintain backward compatibility with Y.] + +### Definition of Done (Binary) + +- [ ] Criterion 1 (e.g., `npm test` passes for `X.test.ts`) +- [ ] Criterion 2 (e.g., UI component renders without hydration errors) +- [ ] Criterion 3 (e.g., API response matches the schema in `types.ts`) + +### New Test Files + +- `path/to/test_A.ext`: [What is being tested] diff --git a/.agent/skills/review/SKILL.md b/.agent/skills/review/SKILL.md new file mode 100644 index 0000000..8481d15 --- /dev/null +++ b/.agent/skills/review/SKILL.md @@ -0,0 +1,54 @@ +--- +name: code-review +description: A "Default-to-Fail" audit of codebase changes. Observation only; no file modifications. +--- + +# Skill: code-review + +## Purpose + +Protect the codebase from "feature creep," technical debt, and weak validation. This skill assumes the latest changes are flawed until they pass a rigorous audit. + +## Execution Rules + +1. **Read-Only Protocol:** This is a diagnostic skill. **Under no circumstances should any files be modified.** Provide feedback only. +2. **Default-to-Fail:** Assume the code is broken or insufficient. The burden of proof lies on the code and its tests. +3. **The Atomic Veto:** - Check the diff. If it exceeds 3 files or 80 lines of logic, **Veto immediately.** + - Reason: "Change exceeds atomic threshold; high risk of cognitive load." +4. **Strictness Audit (Tests):** + - **Veto** if assertions are fuzzy (e.g., `toBeTruthy()`). + - **Veto** if there is no "Red Path" (failure case) test. + - **Veto** if the test is "loose" (e.g., doesn't check specific property values). +5. **Direct Feedback:** No sycophancy. Use "Blockers" for issues and "Verdict: APPROVE" only when the code is bulletproof. + +## Review Template + +### Verdict: [FAIL / APPROVE] + +**Primary Blocker:** [One sentence identifying the biggest reason for rejection.] + +--- + +### 1. Atomic Constraint Check + +- **Files Changed:** [Count] / 3 +- **Logic Lines:** [Count] / 80 +- **Status:** [PASS / FAIL (Veto if FAIL)] + +### 2. Test Strictness Audit + +- **Assertion Quality:** [List specific lines with fuzzy matchers. Demand strict equality.] +- **Failure Coverage:** [Does a test exist for the 'Error/Empty' state? If no, FAIL.] +- **Logic Sync:** [Does the test actually exercise the logic added, or just side effects?] + +### 3. Logic & Resilience + +- **Unchecked States:** [Identify unhandled nulls, undefineds, or missing error catches.] +- **Efficiency:** [Is there a faster path or a redundant operation?] + +### 4. Direct Actionables + +_Note: The reviewer does not apply these. The user/agent must create a ticket or apply fixes manually._ + +1. [Specific fix for Blocker 1] +2. [Specific fix for Blocker 2] diff --git a/.agent/work/tickets/001-refactor-command-validation.md b/.agent/work/tickets/001-refactor-command-validation.md new file mode 100644 index 0000000..9c0c946 --- /dev/null +++ b/.agent/work/tickets/001-refactor-command-validation.md @@ -0,0 +1,30 @@ +### Context & Goal + +Currently, every command manually performs checks like user existence or maintenance mode, or these are hardcoded into the `CommandHandler`. Standardizing these requirements in the command definition itself makes the code cleaner and more declarative. + +### Dependencies + +- None + +### Affected Files + +- `shared/lib/types.ts`: Update `Command` interface to include a optional `requirements` object. +- `bot/lib/handlers/CommandHandler.ts`: Update to read and enforce these requirements. +- `bot/commands/economy/balance.ts`: Refactor to use the new requirements (example). + +### Technical Constraints & Strategy + +- Implementation: Use a standardized `requirements` object in the `Command` interface. +- Requirements could include: `userExists: boolean`, `permissions: string[]`, `devOnly: boolean`. +- Ensure `CommandHandler` provides clear error messages to the user when a requirement fails. + +### Definition of Done (Binary) + +- [ ] `Command` interface updated in `types.ts`. +- [ ] `CommandHandler.ts` enforces requirements before executing command. +- [ ] At least one command (e.g., `balance`) is refactored to use the new system. +- [ ] Clear error embeds are shown to the user when requirements aren't met. + +### New Test Files + +- None (Verification via manual testing of command execution). diff --git a/.agent/work/tickets/002-centralized-error-logging.md b/.agent/work/tickets/002-centralized-error-logging.md new file mode 100644 index 0000000..607c759 --- /dev/null +++ b/.agent/work/tickets/002-centralized-error-logging.md @@ -0,0 +1,31 @@ +### Context & Goal + +The bot currently relies on `console.error` which is hard to track and lacks context. A centralized error logging service will allow for better debugging, persistent error logs, and future integration with services like Sentry or Discord webhooks for alerts. + +### Dependencies + +- None + +### Affected Files + +- `shared/lib/logger.ts`: New file for the unified logger service. +- `bot/lib/handlers/CommandHandler.ts`: Update to use the new logger for command errors. +- `web/src/server.ts`: Update to use the new logger for API and WebSocket errors. + +### Technical Constraints & Strategy + +- Implementation: Create a `Logger` class/object in `shared/lib`. +- Support log levels: `info`, `warn`, `error`, `debug`. +- Errors should capture: timestamp, source (bot/web), error message, and stack trace if available. +- For now, logging to console and a local log file (e.g., `logs/error.log`) is sufficient. + +### Definition of Done (Binary) + +- [ ] `Logger` service implemented in `shared/lib/logger.ts`. +- [ ] Command errors are logged via the new service. +- [ ] Web server errors are logged via the new service. +- [ ] Log output is formatted consistently. + +### New Test Files + +- `shared/lib/logger.test.ts`: Verify logger output and file writing. diff --git a/.agent/work/tickets/003-modularize-web-server.md b/.agent/work/tickets/003-modularize-web-server.md new file mode 100644 index 0000000..45b7db2 --- /dev/null +++ b/.agent/work/tickets/003-modularize-web-server.md @@ -0,0 +1,32 @@ +### Context & Goal + +The current `web/src/server.ts` is a monolithic file with a very long `fetch` handler. This makes it difficult to read and maintain. Modularizing the logic into separate route handlers and middleware-like functions will improve code quality and scalability. + +### Dependencies + +- None + +### Affected Files + +- `web/src/server.ts`: Refactor to use modular handlers. +- `web/src/routes/api.ts`: New file for API route definitions. +- `web/src/routes/static.ts`: New file for static file serving logic. +- `web/src/routes/websocket.ts`: New file for WebSocket event handling. + +### Technical Constraints & Strategy + +- Implementation: Move different responsibilities (API, Static, WS) into separate files. +- The main `serve` configuration should just call these modules. +- Ensure the SPA fallback logic remains intact. + +### Definition of Done (Binary) + +- [ ] `web/src/server.ts` length reduced by at least 50%. +- [ ] API routes moved to dedicated module. +- [ ] Static file serving moved to dedicated module. +- [ ] WebSocket logic moved to dedicated module. +- [ ] Dashboard still loads and functions correctly. + +### New Test Files + +- None (Verification via manual testing of the dashboard). diff --git a/.agent/work/tickets/004-branded-discord-embeds.md b/.agent/work/tickets/004-branded-discord-embeds.md new file mode 100644 index 0000000..951163c --- /dev/null +++ b/.agent/work/tickets/004-branded-discord-embeds.md @@ -0,0 +1,28 @@ +### Context & Goal + +Enhance the user experience by standardizing the look and feel of Discord embeds. Adding consistent branding like a custom footer (with version info) and using the bot's accent color will make the bot feel more professional. + +### Dependencies + +- None + +### Affected Files + +- `bot/lib/embeds.ts`: Update standard embed creators. +- `shared/lib/constants.ts`: Add branding-related constants (colors, footer text). + +### Technical Constraints & Strategy + +- Implementation: Update `createBaseEmbed` and other helpers to automatically include footers and standard colors. +- Use info from `package.json` for versioning in the footer. +- Ensure the changes don't break existing layouts where custom colors might be needed. + +### Definition of Done (Binary) + +- [x] All standard embeds now include a consistent footer. +- [x] Embeds use a predefined brand color by default. +- [x] Version number is automatically pulled for the footer. + +### New Test Files + +- None. diff --git a/.agent/work/tickets/005-refactor-exam-logic.md b/.agent/work/tickets/005-refactor-exam-logic.md new file mode 100644 index 0000000..3c65355 --- /dev/null +++ b/.agent/work/tickets/005-refactor-exam-logic.md @@ -0,0 +1,29 @@ +### Context & Goal + +The `exam` command currently contains a lot of business logic, including reward calculations, timer management, and complex database transactions. Moving this logic to a dedicated `ExamService` will improve testability, maintainability, and keep the command file focused on user interaction. + +### Dependencies + +- None + +### Affected Files + +- `shared/modules/economy/exam.service.ts`: New file for the exam logic. +- `bot/commands/economy/exam.ts`: Refactor to use the new service. + +### Technical Constraints & Strategy + +- Implementation: Create an `ExamService` that handles `getExamStatus`, `takeExam`, and `registerForExam`. +- The command should only handle user input and formatting the response embeds based on the service's result. +- Ensure the Drizzle transactions are correctly handled within the service. + +### Definition of Done (Binary) + +- [ ] `ExamService` implemented with methods for all exam-related operations. +- [ ] `bot/commands/economy/exam.ts` refactored to use the service. +- [ ] Logic is covered by unit tests in a new test file. +- [ ] Manual verification shows the exam command still works as expected. + +### New Test Files + +- `shared/modules/economy/exam.service.test.ts`: Unit tests for reward calculations and state transitions. diff --git a/.gitignore b/.gitignore index cfa4f05..cab42e4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ .env node_modules docker-compose.override.yml -.agent shared/db-logs shared/db/data shared/db/loga diff --git a/bot/lib/embeds.ts b/bot/lib/embeds.ts index 6e618af..e8645d0 100644 --- a/bot/lib/embeds.ts +++ b/bot/lib/embeds.ts @@ -1,4 +1,15 @@ import { Colors, type ColorResolvable, EmbedBuilder } from "discord.js"; +import { BRANDING } from "@shared/lib/constants"; +import pkg from "../../package.json"; + +/** + * Applies standard branding to an embed. + */ +function applyBranding(embed: EmbedBuilder): EmbedBuilder { + return embed.setFooter({ + text: `${BRANDING.FOOTER_TEXT} v${pkg.version}` + }); +} /** * Creates a standardized error embed. @@ -7,11 +18,13 @@ import { Colors, type ColorResolvable, EmbedBuilder } from "discord.js"; * @returns An EmbedBuilder instance configured as an error. */ export function createErrorEmbed(message: string, title: string = "Error"): EmbedBuilder { - return new EmbedBuilder() + const embed = new EmbedBuilder() .setTitle(`❌ ${title}`) .setDescription(message) .setColor(Colors.Red) .setTimestamp(); + + return applyBranding(embed); } /** @@ -21,11 +34,13 @@ export function createErrorEmbed(message: string, title: string = "Error"): Embe * @returns An EmbedBuilder instance configured as a warning. */ export function createWarningEmbed(message: string, title: string = "Warning"): EmbedBuilder { - return new EmbedBuilder() + const embed = new EmbedBuilder() .setTitle(`⚠️ ${title}`) .setDescription(message) .setColor(Colors.Yellow) .setTimestamp(); + + return applyBranding(embed); } /** @@ -35,11 +50,13 @@ export function createWarningEmbed(message: string, title: string = "Warning"): * @returns An EmbedBuilder instance configured as a success. */ export function createSuccessEmbed(message: string, title: string = "Success"): EmbedBuilder { - return new EmbedBuilder() + const embed = new EmbedBuilder() .setTitle(`✅ ${title}`) .setDescription(message) .setColor(Colors.Green) .setTimestamp(); + + return applyBranding(embed); } /** @@ -49,11 +66,13 @@ export function createSuccessEmbed(message: string, title: string = "Success"): * @returns An EmbedBuilder instance configured as info. */ export function createInfoEmbed(message: string, title: string = "Info"): EmbedBuilder { - return new EmbedBuilder() + const embed = new EmbedBuilder() .setTitle(`ℹ️ ${title}`) .setDescription(message) .setColor(Colors.Blue) .setTimestamp(); + + return applyBranding(embed); } /** @@ -65,11 +84,12 @@ export function createInfoEmbed(message: string, title: string = "Info"): EmbedB */ export function createBaseEmbed(title?: string, description?: string, color?: ColorResolvable): EmbedBuilder { const embed = new EmbedBuilder() - .setTimestamp(); + .setTimestamp() + .setColor(color ?? BRANDING.COLOR); if (title) embed.setTitle(title); if (description) embed.setDescription(description); - if (color) embed.setColor(color); - return embed; + return applyBranding(embed); } + diff --git a/bun.lock b/bun.lock index f46e892..2fba0dd 100644 --- a/bun.lock +++ b/bun.lock @@ -9,12 +9,12 @@ "discord.js": "^14.25.1", "dotenv": "^17.2.3", "drizzle-orm": "^0.44.7", + "postgres": "^3.4.7", "zod": "^4.1.13", }, "devDependencies": { "@types/bun": "latest", "drizzle-kit": "^0.31.7", - "postgres": "^3.4.7", }, "peerDependencies": { "typescript": "^5", diff --git a/package.json b/package.json index 95cceac..8ac85e5 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "app", + "version": "1.0.0", "module": "bot/index.ts", "type": "module", "private": true, diff --git a/shared/lib/constants.ts b/shared/lib/constants.ts index 3d85bad..0df339f 100644 --- a/shared/lib/constants.ts +++ b/shared/lib/constants.ts @@ -85,3 +85,9 @@ export enum TriviaCategory { ANIMALS = 27, ANIME_MANGA = 31, } + +export const BRANDING = { + COLOR: 0x00d4ff as const, + FOOTER_TEXT: 'AuroraBot' as const, +}; + diff --git a/tsconfig.json b/tsconfig.json index 6e79a5d..e4570b6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,7 @@ "moduleResolution": "bundler", "allowImportingTsExtensions": true, "verbatimModuleSyntax": true, + "resolveJsonModule": true, "noEmit": true, // Best practices "strict": true,