Add Discord bot: moderation, filters, scheduling, roles, invites, site integration #29
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/discord-bot"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adds a standalone Discord bot for the UOMysticMoon community server, built and verified in phases against a live Discord guild. The bot runs as its own service (
bot/, ownpackage.json/Dockerfile/DB tables) so a bot crash or restart never affects the website.Admin control, not env vars. The bot token is entered in a new admin-only Discord Bot panel, encrypted at rest (same pattern as the existing Google/Discord login-SSO secrets), and pushed to the bot process in-memory over a shared-secret internal API — never written to disk on the bot side, never an environment variable. The bot self-reconnects on its own restart by pulling current config from the site (verified: killing the bot container and restarting it reconnects with zero admin-panel interaction).
Phase 1 — Bot skeleton
/internal/config,/internal/status,/internal/bot-config)requireRole('admin'))Phase 2 — Moderation core
/ban,/kick,/mute,/warn,/warnings— each permission-gated via Discord's own permission system/modlog— configurable mod-log channel; every action posts an embed there and logs tomod_actions/warningsPhase 3 — Word / message filtering
/filter add|remove|list— banned-word list with severity tiers (delete / delete+warn / delete+mute), leetspeak + repeated-character normalization, word-boundary matching to avoid false positives/filterallow role|channel|list— staff/channel bypass listPhase 4 — Scheduled messages
/schedule recurring|once|remove|list— recurring messages vianode-cron, one-off messages via a once-a-minute due-message sweepPhase 5 — Role assignment
/rolemenu create— button-based self-assignable role menus (not reaction-based, avoids an extra privileged intent)/autorole— role auto-assigned on join/role add|remove— single-target assign/remove, optionally temporary (swept on expiry)/roles bulk-assign|bulk-remove— bulk ops targeted "by existing role" (the spec's "explicit member list" variant is deferred — no good Discord slash UX for a multi-user picker)Phase 6 — Invite rotation
/invite channel|rotate|log— auto-rotating primary invite (weekly cron + on-demand), full audit trail, previous invite is revoked on each rotationPhase 7 — Site integration
news-category post now fires a fire-and-forget announce to Discord (only on a genuine unpublished→published transition, not every save)/announce <post>— manually re-post/boost an existing news item/wiki <query>— public, read-only full-text search over the wiki, never writesAlso fixed
A real, pre-existing bug in both MariaDB pools (
server/src/utils/db.jsand the newbot/src/db.js): themariadbdriver defaults totimezone: 'local', silently serializing boundDatequery parameters using the host machine's local offset instead of the DB's actual UTC session — e.g. a temp role scheduled "1 second from now" showed up as already expired. Fixed by settingtimezone: 'auto'on both pools; verified with a direct round-trip showing 0-second drift.Explicitly deferred (flagged during design, not silently dropped)
warnings.expires_atcolumn exists and is ready for this/wiki spells fireball) — the site's search endpoint currently ignores category filters whenever a text query is givenTest plan
Every phase was verified against a real, live Discord guild and the real dev database (not mocks), including:
/ban/kick/mute/warn/warnings/modlogexercised live; mod-log embeds post correctly/schedule once— inserted a due one-off message, confirmed the cron sweep posted it to a real channel and marked it sent/rolemenubutton toggle validated against stored mapping; auto-role on join wired with Server Members intent/invite rotate— two consecutive rotations confirmed the first invite gets revoked and logged/wikisearch and/announcelookup confirmed against real wiki/post dataNot yet tested: production Docker Compose deployment (
docker-compose.ymlchanges are structurally verified viadocker compose configand a successfuldocker buildof the bot image, but not run end-to-end in compose).