Files
amari/CLAUDE.md
T
hikari 5e149a29e4
Node.js CI / CI (push) Successful in 36s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 1m27s
feat: mentorship improvements and name mention notifications (#14)
## Summary

- **Name/title mention notifications**: Amari now notifies Naomi when a message contains her name, common nicknames (`nomi`, `nao`, `nae`, `naonao`), or honorifics (`goddess`, `queen`, `mistress`, `your/her majesty`, `your/her highness`). Uses the same cooldown logic as mention forwarding.
- **Simplified mentee onboarding**: Replaced the lengthy welcome message with a concise prompt asking the new mentee to ping Naomi with their GitHub username and name.
- **Removed offboard notification**: `logMenteeLeave` now only logs a metric silently — no more "user must be offboarded" messages in the channel.
- **Deduplicated welcome messages**: Welcomed mentee IDs are persisted to `data/welcomed.txt` so the onboarding message is only ever sent once, even if the role is re-assigned.

## Test plan

- [ ] Assign mentorship role to a user and confirm the new onboarding message appears
- [ ] Re-assign the role to the same user and confirm no duplicate message is sent
- [ ] Remove a mentee from the server and confirm no offboard message is posted
- [ ] Send a message containing a matched name/honorific and confirm Naomi receives a DM forwarding it

 This PR was created with help from Hikari~ 🌸

Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Reviewed-on: #14
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
2026-03-02 16:03:14 -08:00

6.4 KiB

Amari — Claude Development Guide

Amari is Naomi's personal Discord bot assistant, built with TypeScript and Discord.js. It manages mentorship programmes, processes form submissions, posts RSS news feeds, handles GitHub webhooks, tracks RetroAchievements, and provides various automation features for the NHCarrigan Discord server.

Stack

  • Language: TypeScript (ESM)
  • Runtime: Node.js v24
  • Package Manager: pnpm 10.15.0
  • Discord Library: discord.js 14.x (Components V2)
  • HTTP Server: Fastify 5.x (port 7044)
  • Secrets: 1Password CLI (op run --env-file=prod.env -- ...)
  • Linting: @nhcarrigan/eslint-config (ESLint 9 flat config)
  • TypeScript Config: @nhcarrigan/typescript-config
  • Logging/Metrics: @nhcarrigan/logger (sends to alert server via LOG_TOKEN)
  • CI: Gitea Actions (.gitea/workflows/)

Project Structure

src/
├── config/           # Hardcoded IDs and static config
│   ├── forms.ts      # Baserow form ID → name mapping
│   ├── ids.ts        # Discord channel/guild/role/user/tag IDs
│   ├── progressReminders.ts  # Daily reminder channel configs
│   └── responses.ts  # DM response template
├── events/           # Discord event handlers
│   └── handleMessageCreate.ts
├── interfaces/       # TypeScript type definitions
│   ├── amari.ts      # Core Amari interface
│   ├── baserow.ts    # Mentorship form row type
│   ├── formSubmission.ts
│   ├── github.ts     # GitHub webhook payload types
│   ├── reminder.ts   # ProgressReminder type
│   └── rss.ts        # RSS feed types
├── modules/          # Feature implementations (one feature per file)
│   ├── cacheData.ts
│   ├── checkAchievements.ts
│   ├── getForumTagId.ts
│   ├── logMenteeJoin.ts
│   ├── logMenteeLeave.ts
│   ├── postNews.ts
│   ├── postProgressReminders.ts
│   ├── processFormSubmission.ts
│   ├── processGitHubEvent.ts
│   ├── processMentorshipRole.ts
│   ├── processUserGuildTag.ts
│   ├── respondToDm.ts
│   ├── respondToMention.ts
│   └── updateMentorshipThread.ts
├── server/
│   └── serve.ts      # Fastify HTTP server (port 7044)
├── utils/
│   ├── getComponentsForNaomi.ts  # Discord Components V2 helpers
│   └── logger.ts                 # Logger singleton
└── index.ts          # Entry point: bot init, scheduled jobs, timers

Key External Integrations

Integration Purpose Secret(s)
Discord Bot core BOT_TOKEN
Discord Analytics Guild analytics (cron + gateway event logging)
GitHub App Webhook processing, auto-assign GH_CLIENT_ID, GH_CLIENT_SECRET, GH_PRIVATE_KEY, GH_WEBHOOK_SECRET
Baserow Mentorship form submissions (deprecated — to be removed) BASEROW_SECRET, BASEROW_TOKEN
RetroAchievements Achievement tracking RA_KEY
Alert Server Logging/metrics LOG_TOKEN

Scheduled Tasks (node-schedule)

Cron Task
0 * * * * Post RSS news (freeCodeCamp + HackerNews)
0 0 * * * Audit guild tags across all members
0 9 * * 1-5 Post progress reminders (weekdays 9am)
Every 10 min Clear active channels set + check RetroAchievements

Features

Mentorship Management

  • Onboarding message when mentorship role is assigned
  • Forum thread tag management ("waiting on member" / "waiting on naomi")
  • Progress reminder posts in freeCodeCamp sprint channels (optionally creates threads)
  • Mentee join/leave logging

News Feed

  • Hourly RSS fetch from freeCodeCamp News and HackerNews
  • Comprehensive content filter (violence, harassment, drugs, piracy, fraud, etc.)
  • Posts up to 5 new items per feed, crossposts to announcement channels

GitHub Integration

  • Webhook receiver at POST /github
  • Auto-assigns new issues to naomi-lgbt
  • Auto-requests review from naomi-lgbt on new PRs

Form Processing

  • Webhook receiver at POST /form (Baserow)
  • Validates BASEROW_SECRET
  • Posts submission notification with "Resolve" button to forms channel

RetroAchievements

  • Polls every 10 minutes for achievements earned in last 10 minutes
  • Posts formatted achievement announcements using Discord Components V2

DM / Mention Forwarding

  • Non-Naomi DMs are forwarded to Naomi with Components V2 action buttons
  • Mentions of Naomi or NHCarrigan are forwarded (with cooldown based on recent channel activity)

Guild Identity

  • Monitors user updates for Discord guild identity (profile decoration)
  • Grants/revokes "representing" role accordingly

Important Discord IDs

All hardcoded in src/config/ids.ts. The primary guild is NHCarrigan (1354624415861833870).

Key user IDs:

  • Naomi: defined in ids.ts
  • Amari (bot): defined in ids.ts

Scripts

source ~/.nvm/nvm.sh && pnpm build   # Compile TypeScript → prod/
source ~/.nvm/nvm.sh && pnpm lint    # ESLint (0 warnings allowed)
op run --env-file=prod.env -- node prod/index.js  # Run with secrets

Tests

Currently no test suite exists (test script is a placeholder). When adding tests:

  • Use Vitest per Naomi's Node.js standards
  • Mock Discord.js — do not make real API calls in tests
  • Test files go in test/ directory, not src/

Notes & Conventions

  • Components V2: The bot uses Discord's newer Components V2 API for message formatting. Keep this consistent.
  • Module pattern: Each discrete feature lives in its own file under src/modules/. Add new features here, not in index.ts.
  • src/config/ids.ts: All Discord snowflakes are hardcoded here. Add new IDs to this file rather than scattering them.
  • Metrics logging: All significant user-facing actions should log a metric via the logger. Follow existing patterns.
  • Webhook security: Both GitHub and Baserow webhooks validate secrets before processing. Never skip this validation.
  • prod.env: Contains only 1Password references (safe to commit). Never put real secrets here.
  • GitHub App Installation ID (83119105) is intentionally hardcoded in index.ts.
  • Server port (7044) is intentionally hardcoded in src/server/serve.ts.
  • Baserow integration (src/modules/logMenteeJoin.ts, processFormSubmission.ts, src/interfaces/baserow.ts, src/config/forms.ts) is deprecated and should be removed when the time comes.