Files
keiko/REWRITE.md
T
hikari 1c31a49bc4
Node.js CI / CI (push) Successful in 29s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 55s
feat: rewrite as moderation bot (#11)
## Summary

- Replaces the old AI companion bot with a full Discord moderation system
- Adds 8 slash commands: `warn`, `mute`, `unmute`, `kick`, `softban`, `ban`, `unban`, `prune`
- Adds logging for member join/leave, activity (messages, threads, voice), and moderation actions
- Audit log integration captures manual bans, kicks, timeouts, and unbans
- All applicable actions post sanctions to the Hikari sanction API
- All commands are ephemeral, use Components v2, and enforce permission + role hierarchy checks

## Test plan

- [ ] Run `pnpm register` to register all 8 commands to the guild
- [ ] Verify each command appears in Discord and is only visible to members with the appropriate permissions
- [ ] Test each command against a valid target and confirm mod log entry, DM notification, and sanction record
- [ ] Test each command against an invalid target (equal/higher role, self, bot) and confirm correct error response
- [ ] Perform a manual ban, kick, and timeout in the Discord UI and confirm audit log handler picks them up
- [ ] Perform a manual unban and confirm it logs correctly without creating a sanction
- [ ] Verify join/leave messages appear in the welcome log channel
- [ ] Verify message edits, deletes, thread events, and voice state changes appear in the activity log channel

 This issue was created with help from Hikari~ 🌸

Reviewed-on: #11
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
2026-03-24 20:35:26 -07:00

128 lines
4.3 KiB
Markdown

# Keiko — Moderation Bot Rewrite
## Overview
Complete rewrite of Keiko from an AI companion bot into a full-featured community moderation bot for Naomi's Discord server.
---
## Tech Stack
- **Language**: TypeScript
- **Runtime**: Node.js
- **Discord Library**: discord.js v14
- **Config**: `@nhcarrigan/typescript-config`
- **Linting**: `@nhcarrigan/eslint-config`
- **Logging**: `@nhcarrigan/logger`
- **Package Manager**: pnpm
---
## Planned Commands
All moderation commands respond **ephemerally** using **Discord Components v2**.
| Command | Description | Sanction Type |
|---------|-------------|---------------|
| `/warn <user> <reason>` | Issues a formal warning | `warning` |
| `/mute <user> <duration> <reason>` | Applies Discord native timeout | `mute` |
| `/unmute <user> <reason>` | Removes Discord native timeout | *(none)* |
| `/kick <user> <reason>` | Kicks user from server | `kick` |
| `/softban <user> <reason>` | Bans + immediately unbans (message purge) | `kick` |
| `/ban <user> <reason> [days]` | Permanently bans user | `ban` |
| `/prune <amount max:100>` | Deletes last N messages in current channel | *(none — logged to mod channel only)* |
---
## Logging Channels
Three configurable logging channels (set via environment/config):
| Channel | Events |
|---------|--------|
| **Welcome/Goodbye** | `guildMemberAdd`, `guildMemberRemove` |
| **Activity Log** | Message create/edit/delete, thread create/delete/update, voice channel join/leave/move |
| **Moderation Log** | All command-issued sanctions + parsed audit log actions (manual bans, kicks, timeouts) |
---
## Sanction API Integration
**Endpoint**: `POST https://hikari.nhcarrigan.com/sanction`
**Auth**: `Authorization: {SANCTION_TOKEN}`
**Payload**:
```json
{
"platform": "discord",
"uuid": "<target user ID>",
"username": "<target username>",
"type": "warning | kick | mute | ban",
"reason": "<reason>"
}
```
**Triggers** (sanction created for each):
- `/warn``warning`
- `/mute``mute`
- `/kick``kick`
- `/softban``kick`
- `/ban``ban`
- Audit log: manual ban detected → `ban`
- Audit log: manual kick detected → `kick`
- Audit log: manual timeout detected → `mute`
**No sanction**: `/unmute`, `/prune`
---
## Confirmed Decisions
| Question | Decision |
|----------|----------|
| Softban sanction type | Maps to `kick` |
| Mute mechanism | Discord native timeout |
| Prune scope | Last N messages (up to 100) in current channel; no sanction, log to mod channel |
| Channel config | Config object in `src/config/` (single-server) |
| Deployment scope | Single guild only |
| Repo strategy | Replace in-place on `feat/modbot` branch |
---
## Project Structure (Planned)
```
keiko/
├── src/
│ ├── commands/ # Slash command definitions
│ ├── events/ # Discord event handlers
│ ├── modules/ # Business logic (sanction sending, audit log parsing, etc.)
│ ├── utils/ # Shared utilities
│ └── index.ts # Entry point
├── prod.env # 1Password secret references (safe to commit)
├── package.json
├── tsconfig.json
├── eslint.config.js
└── vitest.config.ts
```
---
## Implementation Phases
- [x] **Phase 1 — Project scaffold** (package.json, tsconfig, eslint, entry point)
- [x] **Phase 2 — Commands** (all 7 slash commands with ephemeral Components v2 responses)
- [x] **Phase 3 — Sanction API integration** (send sanctions on command, handle errors)
- [x] **Phase 4 — Logging events** (welcome/goodbye, activity, moderation channel)
- [x] **Phase 5 — Audit log parsing** (detect manual moderation actions, send to sanction API)
- [ ] **Phase 6 — Deploy** (fill in channel IDs in `src/config/channels.ts`, fill in `clientId` in `src/config/guild.ts`, run `pnpm register` to register commands, then `pnpm start`)
## Next Steps Before Deploying
1. Fill in actual channel IDs in `src/config/channels.ts`
2. Fill in Keiko's bot client ID in `src/config/guild.ts`
3. Ensure `SANCTION_TOKEN` is in 1Password at the vault path in `prod.env`
4. Run `pnpm register` once to register slash commands with Discord
5. Enable **privileged intents** in Discord Developer Portal: `Server Members Intent` and `Message Content Intent`
6. Start the bot with `pnpm start`