Ratmas Bot Development
This skill provides development guidelines for the Ratmas Secret Santa Discord bot, a TypeScript-based Discord bot using Prisma, discord.js, and Node ESM.
Architecture Overview
The bot follows a service-oriented architecture with clear separation of concerns:
**Entry Point**: `src/index.ts` bootstraps the Discord client and instantiates scoped services (`UserService`, `MessageService`, `ChannelService`, `RoleService`)**Core Orchestration**: `src/services/rat.service.ts` handles Ratmas events**Validation**: `rat.service.helpers.ts` contains validation logic**Operations**: `rat.service.operations.ts` handles pairing/DM flows**Persistence**: `repositories/ratmas.repository.ts` manages data access**Commands**: `src/commands/` contains slash command implementations**Documentation**: `docs/` contains architecture notes (`ARCHITECTURE.md`, `PERSISTENCE.md`)Step-by-Step Instructions
1. Setting Up Development Environment
When setting up or onboarding to this project:
Ensure Node 20+ is installedRun `npm install` to install dependenciesRun `npm run prisma:generate` to generate the Prisma clientUse `npm run build` to compile TypeScriptUse `npm run lint` and `npm run format:check` for code quality2. Working with Persistence
When adding or modifying database operations:
**Never** construct `PrismaClient` directly; import from `src/persistence/prisma-client.ts`All Prisma access **must** go through `RatmasRepository` in `repositories/ratmas.repository.ts`Keep `src/types/ratmas.types.ts` in sync with `prisma/schema.prisma`Update enum guards (`RatmasEventStatus`, `ACTIVE_STATUSES`) when adding statesUse `replacePairings` for transactional pairing updates—never mutate pairing rows directlyAfter schema changes, run `npm run prisma:generate` and `npm run prisma:migrate:dev`Ensure migrations land in `prisma/migrations/`Key schema notes:
`RatmasEvent` includes: `eventStartDate`, `eventEndDate`, `purchaseDeadline`, `revealDate`, `timezone`Reuse repository mapping helpers and existing validation (`VALID_STATUS_VALUES`)3. Adding Discord Slash Commands
When implementing new slash commands:
Place command files in `src/commands/` (follow pattern from `ratmas-start.command.ts`)Register commands per guild using `ensureRatmasStartCommand` patternGate access via `RATMAS_ROLE_ID` and admin permissionsPlace modal/channel orchestration in separate helper files (e.g., `ratmas-start.helpers.ts`)Use `ChannelService` for channel creation and permission managementHandle interactions (modals, buttons) with proper custom IDs (e.g., `RATMAS_OPT_OUT_BUTTON_ID`)4. Working with Services
When adding functionality to services:
Services are **thin wrappers** around the Discord clientPrefer extending existing services over creating new onesNever revive the legacy `DiscordService`Use dependency injection for testabilityExamples: - Use `RoleService.removeRoleFromMember` for role removal
- Use `ChannelService` for channel operations
- Compose services rather than creating monolithic implementations
5. Date and Timezone Handling
When working with dates and timezones:
Use `src/utils/date.utils.ts` (Luxon-based) for all date operationsAlways pass schedules through `parseRatmasSchedule` for validation and UTC normalizationStore dates in UTC in the databaseFormat dates for user display using Luxon helpers6. Writing Tests
When adding or modifying tests:
Run tests with `npm test` (sets `NODE_OPTIONS=--experimental-vm-modules`)Use Jest ESM + ts-jest configurationMock `discord.js` behavior directly—see `__tests__/commands/` for patternsTest command flows: `__tests__/commands/` shows modal flow testingTest utilities: `__tests__/utils/date.utils.test.ts` shows Luxon assertionsTest bootstrapping: `__tests__/index.test.ts` shows app boot testingAssert on formatted strings rather than raw Date objects for user-facing copyUse dependency injection to keep tests deterministic7. Code Organization and Hygiene
When organizing code and documentation:
Keep temporary planning files in `/temp/`Avoid adding new helpers at repo rootSeparate infrastructure from gameplay logicPlace documentation and operational notes in `docs/`Follow existing file structure and naming conventionsAlways import local files with `.js` suffix (ESM requirement)Use utilities like `shuffleArray`, `generateId` with injectable dependencies8. Adding New Features
When implementing new Ratmas features:
1. Update schema in `prisma/schema.prisma` if needed
2. Run `npm run prisma:generate` and `npm run prisma:migrate:dev`
3. Update types in `src/types/ratmas.types.ts`
4. Add validation in `rat.service.helpers.ts`
5. Add operations in `rat.service.operations.ts`
6. Add persistence logic in `repositories/ratmas.repository.ts`
7. Update `RatmasService` orchestration in `src/services/rat.service.ts`
8. Add slash commands in `src/commands/` if needed
9. Write tests following existing patterns
10. Update documentation in `docs/`
Important Constraints
**Node 20+ with native ESM is required****Always import local files with `.js` extension****Never construct PrismaClient directly**—use shared client from `src/persistence/prisma-client.ts`**Never mutate pairing rows directly**—use `replacePairings` transaction**All Prisma access must go through RatmasRepository****Services must remain thin wrappers**—no business logic in services**Do not revive legacy `DiscordService`**—use scoped services insteadKey Files Reference
`src/index.ts` - Application bootstrap`src/services/rat.service.ts` - Main Ratmas orchestration`src/services/rat.service.helpers.ts` - Validation logic`src/services/rat.service.operations.ts` - Pairing/DM flows`repositories/ratmas.repository.ts` - Data access layer`src/commands/ratmas-start.command.ts` - Slash command example`src/utils/date.utils.ts` - Timezone/date utilities`prisma/schema.prisma` - Database schema`docs/ARCHITECTURE.md` - Architecture documentation`docs/PERSISTENCE.md` - Persistence patternsTesting Commands
```bash
npm test # Run all tests
npm run build # Compile TypeScript
npm run lint # Lint code
npm run format:check # Check formatting
npm run prisma:generate # Generate Prisma client
npm run prisma:migrate:dev # Run migrations
```