MythEclipse e00b23f9b8 fix(concurrency): eliminate all identified double-queue and race conditions
Implements full audit from double_queue_audit.md.

## Critical

fix(backend): reanalyze-batch — per-scope in-flight guard (messages.routes.ts)
  Two concurrent admin sessions clicking 'Retry All Errors' simultaneously now
  get a 409 REANALYZE_BATCH_IN_PROGRESS for the same guildId:channelId scope.
  Prevents the recovery worker from being triggered twice for the same set of
  error messages.

fix(discord-gateway): messageUpdate embed resolution skip (messageCapture.ts)
  Discord fires messageUpdate when link previews resolve 1-2s after send
  even though the message body is unchanged. Compare newContent vs
  existingContent before resetting ai_status to pending and re-queueing LLM.
  Eliminates a spurious duplicate analysis that could overwrite a valid result.

## High

fix(discord-gateway): scheduleAutoDelete idempotency (aiAnalyzer.ts)
  Add autoDeleteInFlight Set. Both processBatch and processIndividualFallback
  call scheduleAutoDelete; without the guard, a message that races through
  both paths launches two concurrent attemptAutoDeleteFlaggedMessage calls,
  producing a duplicate moderation-action log entry and a Discord 10008 error.
  The Set is cleaned up in a .finally() block after each attempt completes.

## Medium

fix(frontend): revert optimistic pending state on HTTP failure (useMessages.ts)
  Capture the prior MessageRecord inside the setMessages functional updater
  (no extra useCallback deps needed). If reanalyzeMessage() throws, restore
  the snapshot so the UI reflects the real DB state instead of lying.

fix(discord-gateway): exclude individual_analysis_exhausted from recovery queries
  Both getConversationKeysWithIncompleteAnalysis and
  getIncompleteMessagesByConversation now add a NOT LIKE guard for
  individual_analysis_exhausted. Prevents an infinite recovery loop if a bug
  ever writes both flags to the same row.

## Low

fix(discord-gateway): unified timer path in scheduleConversationAnalysis
  Remove the separate cooldown-path that created a secondary timer calling
  scheduleConversationAnalysis recursively. Replace with a single
  clear-and-reset pattern where delayMs = max(cooldownRemainder+500, debounce).
  Eliminates the edge case where both timers were live simultaneously.

## Misc

fix(backend): cast req.params.id to String() to satisfy Express typings
  Pre-existing tsc error (string | string[]) exposed by our edit.
  String() is correct; route params are always scalar strings at runtime.
2026-06-05 16:09:23 +07:00
2026-06-02 19:04:49 +07:00
2026-06-02 18:34:55 +07:00
2026-06-02 18:55:48 +07:00
2026-06-02 19:06:23 +07:00
a
2026-06-03 03:23:10 +07:00
2026-06-02 18:34:55 +07:00

Discord Moderation Watcher Bot

Bot monitoring Discord yang merekam voice channel, menangkap pesan teks, menyimpan attachment, menjalankan analisis opsional, dan menyediakan dashboard web real-time.

Stack utama: Node.js, pnpm, TypeScript, discord.js-selfbot-v13, @discordjs/voice, Express, WebSocket, Drizzle ORM, SQLite/PostgreSQL, React, Vite, Vitest, dan Biome.

Prasyarat

  • Node.js versi modern yang kompatibel dengan TypeScript dan Vite.
  • pnpm 10.x. Repo ini dipin ke pnpm@10.25.0.
  • FFmpeg tersedia di PATH untuk proses muxing audio dan playback media.
  • yt-dlp tersedia di PATH untuk resolve audio YouTube, search result YouTube, dan Spotify track.
  • Native audio dependencies dapat dibuild di mesin lokal (@discordjs/opus, better-sqlite3, sodium-native).

Install FFmpeg:

# Ubuntu/Debian
sudo apt install ffmpeg

# Arch
sudo pacman -S ffmpeg

Install yt-dlp:

pnpm run install:yt-dlp

Script installer akan memakai package manager yang tersedia (pacman, apt-get, dnf, brew) atau fallback ke pipx/pip.

Setup

pnpm install
cp .env.example .env

Edit .env sesuai server yang dimonitor:

DISCORD_TOKEN=your_token_here
MONITOR_GUILD_ID=your_guild_id_here
RECORDINGS_DIR=./recordings
WEBSERVER_PORT=3000
DATABASE_TYPE=sqlite

Catatan: project ini memakai selfbot library, bukan bot token Discord standar. Pastikan penggunaan sesuai risiko dan aturan platform yang berlaku.

Menjalankan

# Bot/server utama dengan auto-restart
pnpm run dev

# Production-style start
pnpm run start

# Dashboard frontend dev server
pnpm run dev:web

Dashboard build production disajikan dari public/app setelah menjalankan:

pnpm run build:web

Command Development

# Type checking
pnpm run typecheck

# Lint
pnpm run lint

# Format
pnpm run format

# Test
pnpm run test

# Build frontend + TypeScript
pnpm run build

# Install external yt-dlp CLI for YouTube/search/Spotify track playback
pnpm run install:yt-dlp

Database

Default database adalah SQLite di .muxer-queue.db. PostgreSQL dapat dipakai dengan DATABASE_TYPE=postgres dan konfigurasi DATABASE_URL atau variabel POSTGRES_*.

Jika aplikasi dijalankan di PostgreSQL production, migrasi dapat dijalankan otomatis saat startup dengan AUTO_MIGRATE_ON_STARTUP=true. Jalur ini memakai advisory lock supaya hanya satu instance yang memigrasi schema pada satu waktu.

# Generate migration Drizzle
pnpm run db:generate

# Jalankan migration via drizzle-kit
pnpm run db:migrate

# Jalankan migration programmatic
pnpm run db:migrate:programmatic

# Buka Drizzle Studio
pnpm run db:studio

Fitur

  • Voice recording ke segment .ogg per user.
  • Metadata JSON per segment audio.
  • Text message capture untuk pesan baru, edit, dan delete.
  • Attachment capture dan upload ke endpoint Picser.
  • SQLite/PostgreSQL via Drizzle ORM.
  • REST API dan WebSocket untuk dashboard.
  • Dashboard React untuk pesan, gambar, voice, media playback, dan moderation review.
  • Media playback dari direct URL, file lokal, YouTube URL, search terms, dan Spotify track URL.
  • Metrics Prometheus di endpoint server.
  • Retry dengan backoff untuk operasi eksternal.
  • AI moderation analysis opsional via konfigurasi AI_*.

Struktur Rekaman

recordings/
  <user-id>/
    <user-id>-<session-start>-0.ogg
    <user-id>-<session-start>-0.json
    <user-id>-<session-start>-1.ogg
    <user-id>-<session-start>-1.json

Segment duration dikontrol oleh RECORDING_SEGMENT_MS.

Struktur Proyek

src/
  index.ts                    Entry point Discord client dan server
  recorder.ts                 Voice recording pipeline
  recorder/                   Audio stream, decoder, segment metadata
  moderation/                 Message capture, storage, uploads, AI review
  database/                   Drizzle setup, schema, migrations
  routes/                     Express route modules
  webserver.ts                Express + WebSocket server
  retry.ts                    Retry helper berbasis p-retry
  audio/ffmpegProcess.ts      Direct ffmpeg process wrapper
frontend/                     React dashboard source
public/app/                   Dashboard build output
tests/                        Vitest tests
drizzle/migrations/           Database migrations

Konfigurasi Penting

Lihat .env.example untuk daftar lengkap. Variabel utama:

  • DISCORD_TOKEN — token akun/client yang dipakai selfbot.
  • MONITOR_GUILD_ID — guild yang dimonitor untuk moderation capture.
  • RECORDINGS_DIR — direktori output audio.
  • WEBSERVER_PORT — port HTTP/WebSocket.
  • DATABASE_TYPEsqlite atau postgres.
  • PICSER_UPLOAD_URL — endpoint upload attachment.
  • AI_ANALYSIS_ENABLED — aktifkan/nonaktifkan analisis AI.
  • AI_LLM_API_KEY, AI_LLM_BASE_URL, AI_LLM_MODEL — konfigurasi provider LLM.

Verifikasi Setelah Perubahan

Sebelum menjalankan lama atau deploy, jalankan:

pnpm install
pnpm run typecheck
pnpm run lint
pnpm run test
pnpm run build

Catatan Library Modernization

Project memakai Zod untuk validasi runtime, Drizzle untuk database, dan wrapper node:child_process langsung untuk FFmpeg. Library lama class-transformer, class-validator, dan fluent-ffmpeg sudah tidak dipakai.

S
Description
Bete Discord moderation watcher
Readme
26 MiB
Languages
TypeScript 97.1%
Nix 1%
Shell 0.8%
CSS 0.7%
PLpgSQL 0.4%