- Live Moderation Feed: gateway publishes discord:moderation:action (Redis) → backend WS emits moderation_action → public web shows realtime stream. - Toxic Topic Trends: backend moderation.trends aggregates categories/severity/action_type (read-only) → SVG bar + donut. - Channel Timeline: messages view gets Feed/Timeline toggle with date-grouped separators. - CSV Export: client-side downloadCsv for moderation actions (no backend write scope). - Activity Heatmap: backend messages.activity (per-hour volume by channel) → pure-SVG grid. User reputation deliberately excluded — no such feature exists in the codebase. All read-only / public-facing / fully automatic per project rules.
5.2 KiB
GMW — Fitur Publik Lanjutan (#2–#6) Implementation Plan
For Hermes: Implement task-by-task. Build + lint + typecheck each service after its changes. Deploy via push to main (CI handles Nix build + systemd). Hard constraint (user 2026-08-18): public read-only web, fully automatic, rules in code, NO admin endpoints, NO shadow mode, NO per-channel web config. EXPLICITLY EXCLUDED: User Reputation / Strike History (user: "hapus sepenuhnya fitur user reputation" — it was never built; do not add it).
Existing infra to reuse (verified)
- WS: backend
ws/server.tsbroadcasts JSON{type,data,timestamp}to frontendClients. Backendws/redis-bridge.tssubscribes Redis channels listed inDISCORD_CHANNEL_TO_WS_EVENT(backendshared/redis-channels.ts) and re-emits as WS events. FEsrc/lib/wsauto-reconnect typed client. - Gateway → Redis:
EventBroadcaster+RedisEventPublisher(discord-gateway/src/modules/event-broadcaster). Publish viaeventBroadcaster.publish(EventChannels.X, payload). - Moderation data:
moderation_actionstable (now has explainability cols).moderation.repository.listActionsreturns rows.ModerationActionFE type atfrontend/src/lib/types/moderation.ts. - Messages:
messages.list/getMessagesByChannel(backend oRPC + repository). FEmessagesApi+useMessages. - Charts: NO chart lib installed. Use pure SVG/CSS (consistent with repo; avoid new deps).
- CSV: client-side Blob download, no backend.
Task 1 — Live Moderation Feed (#2)
Gateway: add MODERATION_ACTION: "discord:moderation:action" to
redis-channels.ts (shared) + EventChannels.MODERATION_ACTION in
eventTypes.ts. In moderationActionsDb.createModerationAction, after insert,
publish eventBroadcaster.publish(EventChannels.MODERATION_ACTION, actionRow).
Backend: add DISCORD_MODERATION_ACTION constant + map
[DISCORD_MODERATION_ACTION]: "moderation_action" in DISCORD_CHANNEL_TO_WS_EVENT.
FE: in src/lib/ws, subscribe to moderation_action; add useLiveModeration
hook (SWR-style with WS push, capped buffer ~50). Add <LiveModerationFeed>
client component on /moderation page (top of list, animated new-row).
Risk: gateway publish at every action (already async insert) — fire-and-forget,
wrap in try/catch. Verify WS event reaches FE via wscat/curl or log.
Task 2 — Toxic Topic Trends (#3)
Backend: add moderation.trends oRPC. Query moderation_actions grouped
by categories (jsonb text[]) over last 30 days, count per category + severity
breakdown. Also action_type distribution. Return
{ categories: {name,count}[], severities: {level,count}[], actions: {type,count}[] }.
Map jsonb array in SQL (use unnest or parse in JS). Reuse getDatabase.
FE: useModerationTrends hook + <TopicTrends> SVG bar chart (top 10
categories) + severity donut (SVG arcs). Place on /moderation as a panel.
Task 3 — Channel Timeline / Replay (#4)
Reuse existing messages.list (guildId) + getMessagesByChannel. Add a
Timeline tab to /messages that groups messages by date (client-side
bucket from created_at). Load-more via cursor. No new backend (existing
messagesRouter.list already supports guildId+limit+cursor). If needed, add
messages.timeline aggregation (count per day) — but keep simple: client
groups fetched rows. Verify existing endpoint returns enough history.
Task 4 — Export CSV (#5)
FE only. lib/csv.ts toCsv(rows, columns) + downloadCsv(filename, csv).
Add "Export CSV" button on /moderation (exports current actions) and
/messages (exports current list). Pure client-side, read-only. No backend.
Task 5 — Activity Heatmap (#6)
Backend: add messages.activity oRPC: per-channel message count grouped by
hour-of-day (0–23) over last 14 days. Return
{ channels: {channelId, name, byHour: number[24]}[], max }. Use SQL
EXTRACT(hour from ...) + group by channel. Channel name from
message.metadata->'channel'->>'channelName'.
FE: useMessageActivity hook + <ActivityHeatmap> SVG grid (channels ×
24h, color intensity = count/max). Place on /messages or /dashboard.
Verification checklist
pnpm typecheck && pnpm lint && pnpm buildgreen for gateway, backend, frontend- Backend
/trpc/moderation/trendsreturns categories/severities/actions - Backend
/trpc/messages/activityreturns byHour grids - WS
moderation_actionreceived by FE (log or visible live row) - No admin/write endpoint added; all public read-only
- No User Reputation code anywhere (grep "reputation|strike|reputasi")
- Deploy via push; all 3 services
running; moderation + messages pages load
Files touched (summary)
- gateway:
shared/redis-channels.ts,event-broadcaster/eventTypes.ts,event-broadcaster/eventBroadcaster.ts,message-capture/moderationActionsDb.ts - backend:
shared/redis-channels.ts,orpc/router.ts,modules/moderation/moderation.service.ts(+repository),modules/messages/messages.service.ts(+repository, +schema) - frontend:
lib/ws/*,hooks/use-moderation.ts,hooks/use-messages.ts,lib/csv.ts,lib/types/*,app/(dashboard)/moderation/view.tsx,app/(dashboard)/messages/view.tsx, new components undercomponents/