feat: implement branded discord embeds and versioning
This commit is contained in:
51
.agent/skills/create-ticket/SKILL.md
Normal file
51
.agent/skills/create-ticket/SKILL.md
Normal file
@@ -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]
|
||||||
54
.agent/skills/review/SKILL.md
Normal file
54
.agent/skills/review/SKILL.md
Normal file
@@ -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]
|
||||||
30
.agent/work/tickets/001-refactor-command-validation.md
Normal file
30
.agent/work/tickets/001-refactor-command-validation.md
Normal file
@@ -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).
|
||||||
31
.agent/work/tickets/002-centralized-error-logging.md
Normal file
31
.agent/work/tickets/002-centralized-error-logging.md
Normal file
@@ -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.
|
||||||
32
.agent/work/tickets/003-modularize-web-server.md
Normal file
32
.agent/work/tickets/003-modularize-web-server.md
Normal file
@@ -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).
|
||||||
28
.agent/work/tickets/004-branded-discord-embeds.md
Normal file
28
.agent/work/tickets/004-branded-discord-embeds.md
Normal file
@@ -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.
|
||||||
29
.agent/work/tickets/005-refactor-exam-logic.md
Normal file
29
.agent/work/tickets/005-refactor-exam-logic.md
Normal file
@@ -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.
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,7 +1,6 @@
|
|||||||
.env
|
.env
|
||||||
node_modules
|
node_modules
|
||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
.agent
|
|
||||||
shared/db-logs
|
shared/db-logs
|
||||||
shared/db/data
|
shared/db/data
|
||||||
shared/db/loga
|
shared/db/loga
|
||||||
|
|||||||
@@ -1,4 +1,15 @@
|
|||||||
import { Colors, type ColorResolvable, EmbedBuilder } from "discord.js";
|
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.
|
* 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.
|
* @returns An EmbedBuilder instance configured as an error.
|
||||||
*/
|
*/
|
||||||
export function createErrorEmbed(message: string, title: string = "Error"): EmbedBuilder {
|
export function createErrorEmbed(message: string, title: string = "Error"): EmbedBuilder {
|
||||||
return new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle(`❌ ${title}`)
|
.setTitle(`❌ ${title}`)
|
||||||
.setDescription(message)
|
.setDescription(message)
|
||||||
.setColor(Colors.Red)
|
.setColor(Colors.Red)
|
||||||
.setTimestamp();
|
.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.
|
* @returns An EmbedBuilder instance configured as a warning.
|
||||||
*/
|
*/
|
||||||
export function createWarningEmbed(message: string, title: string = "Warning"): EmbedBuilder {
|
export function createWarningEmbed(message: string, title: string = "Warning"): EmbedBuilder {
|
||||||
return new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle(`⚠️ ${title}`)
|
.setTitle(`⚠️ ${title}`)
|
||||||
.setDescription(message)
|
.setDescription(message)
|
||||||
.setColor(Colors.Yellow)
|
.setColor(Colors.Yellow)
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
|
|
||||||
|
return applyBranding(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,11 +50,13 @@ export function createWarningEmbed(message: string, title: string = "Warning"):
|
|||||||
* @returns An EmbedBuilder instance configured as a success.
|
* @returns An EmbedBuilder instance configured as a success.
|
||||||
*/
|
*/
|
||||||
export function createSuccessEmbed(message: string, title: string = "Success"): EmbedBuilder {
|
export function createSuccessEmbed(message: string, title: string = "Success"): EmbedBuilder {
|
||||||
return new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle(`✅ ${title}`)
|
.setTitle(`✅ ${title}`)
|
||||||
.setDescription(message)
|
.setDescription(message)
|
||||||
.setColor(Colors.Green)
|
.setColor(Colors.Green)
|
||||||
.setTimestamp();
|
.setTimestamp();
|
||||||
|
|
||||||
|
return applyBranding(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,11 +66,13 @@ export function createSuccessEmbed(message: string, title: string = "Success"):
|
|||||||
* @returns An EmbedBuilder instance configured as info.
|
* @returns An EmbedBuilder instance configured as info.
|
||||||
*/
|
*/
|
||||||
export function createInfoEmbed(message: string, title: string = "Info"): EmbedBuilder {
|
export function createInfoEmbed(message: string, title: string = "Info"): EmbedBuilder {
|
||||||
return new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTitle(`ℹ️ ${title}`)
|
.setTitle(`ℹ️ ${title}`)
|
||||||
.setDescription(message)
|
.setDescription(message)
|
||||||
.setColor(Colors.Blue)
|
.setColor(Colors.Blue)
|
||||||
.setTimestamp();
|
.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 {
|
export function createBaseEmbed(title?: string, description?: string, color?: ColorResolvable): EmbedBuilder {
|
||||||
const embed = new EmbedBuilder()
|
const embed = new EmbedBuilder()
|
||||||
.setTimestamp();
|
.setTimestamp()
|
||||||
|
.setColor(color ?? BRANDING.COLOR);
|
||||||
|
|
||||||
if (title) embed.setTitle(title);
|
if (title) embed.setTitle(title);
|
||||||
if (description) embed.setDescription(description);
|
if (description) embed.setDescription(description);
|
||||||
if (color) embed.setColor(color);
|
|
||||||
|
|
||||||
return embed;
|
return applyBranding(embed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
bun.lock
2
bun.lock
@@ -9,12 +9,12 @@
|
|||||||
"discord.js": "^14.25.1",
|
"discord.js": "^14.25.1",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-orm": "^0.44.7",
|
"drizzle-orm": "^0.44.7",
|
||||||
|
"postgres": "^3.4.7",
|
||||||
"zod": "^4.1.13",
|
"zod": "^4.1.13",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"drizzle-kit": "^0.31.7",
|
"drizzle-kit": "^0.31.7",
|
||||||
"postgres": "^3.4.7",
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "app",
|
||||||
|
"version": "1.0.0",
|
||||||
"module": "bot/index.ts",
|
"module": "bot/index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -85,3 +85,9 @@ export enum TriviaCategory {
|
|||||||
ANIMALS = 27,
|
ANIMALS = 27,
|
||||||
ANIME_MANGA = 31,
|
ANIME_MANGA = 31,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const BRANDING = {
|
||||||
|
COLOR: 0x00d4ff as const,
|
||||||
|
FOOTER_TEXT: 'AuroraBot' as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
// Best practices
|
// Best practices
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
|||||||
Reference in New Issue
Block a user