Files
GMW/.hermes/plans/2026-08-18_gmw-public-features-2-6.md
T
asepharyana 9b3134d767 feat(gmw): public features #2-#6 — live moderation feed, toxic topic trends, channel timeline, CSV export, activity heatmap
- 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.
2026-08-18 17:43:02 +07:00

5.2 KiB
Raw Blame History

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.ts broadcasts JSON {type,data,timestamp} to frontendClients. Backend ws/redis-bridge.ts subscribes Redis channels listed in DISCORD_CHANNEL_TO_WS_EVENT (backend shared/redis-channels.ts) and re-emits as WS events. FE src/lib/ws auto-reconnect typed client.
  • Gateway → Redis: EventBroadcaster + RedisEventPublisher ( discord-gateway/src/modules/event-broadcaster). Publish via eventBroadcaster.publish(EventChannels.X, payload).
  • Moderation data: moderation_actions table (now has explainability cols). moderation.repository.listActions returns rows. ModerationAction FE type at frontend/src/lib/types/moderation.ts.
  • Messages: messages.list / getMessagesByChannel (backend oRPC + repository). FE messagesApi + 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.

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 (023) 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 build green for gateway, backend, frontend
  • Backend /trpc/moderation/trends returns categories/severities/actions
  • Backend /trpc/messages/activity returns byHour grids
  • WS moderation_action received 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 under components/