Phase 7 of TEAMS.md. `api.registerSlashCommands` stops throwing: a module
registers a command's DEFINITION and its HANDLER together, the bot pulls the
definitions over the internal listener and runs none of our code, and the
handler executes here — forced by the bot container having no `modules` volume,
and the right boundary anyway.
Registration validates what Discord would reject as a batch (names, description
lengths, the four option types, required-before-optional), because the bot
registers the whole set in one PUT and a single bad entry costs every command
including the bot's own. Commands are not namespaced under their owner — there
is no dot in Discord's name grammar — so collisions are first-come with the
holder named.
The dispatcher is the access boundary: `linked` has no Discord equivalent, so
the platform-side permission default can only ever be advertising. It resolves
the actor by `auth_providers.kind` rather than the id slug, treats a banned
account as unlinked, bounds a handler under the bot's own timeout, and keeps
`ok` outside the envelope so a handler cannot forge it.
Liveness is asked at both the pull and the dispatch. The registries have no
removal path, so a module an operator disables at runtime would otherwise keep
a live handler behind a command Discord still advertises.
Co-Authored-By: Claude <noreply@anthropic.com>
Standalone bot/ service (its own package.json/Dockerfile) managed entirely
through a new admin-only Discord Bot panel — token stored encrypted in the
DB and pushed to the bot process in-memory, never an env var. Built in
phases, each independently verified against a live Discord guild:
- Bot skeleton: gateway connection, internal shared-secret API, self-heals
on its own restart by pulling config from the site
- Moderation core: /ban /kick /mute /warn /warnings + mod-log channel
- Word/invite/spam filtering with leetspeak-resistant normalization and a
staff role/channel allowlist
- Scheduled messages: recurring (cron) and one-off channel posts
- Role assignment: button role menus, auto-role on join, temp roles,
bulk role ops
- Auto-rotating primary invite with an audit log
- Site integration: news-publish -> Discord announce webhook, manual
/announce, read-only /wiki search
Also fixes a pre-existing bug in both DB pools (server + bot): the mariadb
driver defaulted to timezone 'local', silently mis-serializing bound Date
params by the host's local offset instead of the DB's UTC session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>