Commit Graph
345 Commits
Author SHA1 Message Date
MythEclipseandClaude Opus 4.8 4fa7875b80 feat: add sticker context to LLM prompts + sticker image cache
- New sticker-specific vision prompt that tells LLM stickers are cartoon/meme art, not real photos
- New text-only warning for stickers that fail to download — prevents flagging based on name alone
- Updated system prompt with dedicated sticker guidance section (looser standards for cartoon content)
- Filesystem-backed sticker cache (keyed by name, 7-day TTL, 100MB max with LRU eviction)
- Config: STICKER_CACHE_DIR and STICKER_CACHE_MAX_SIZE_MB with defaults
- Updated .env.example with auto-delete + sticker config docs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 15:54:37 +07:00
MythEclipseandClaude Opus 4.8 202ea311a5 feat: lower auto-delete confidence threshold to 0.50, add allowed severities to defaults
- AUTO_DELETE_MIN_CONFIDENCE default 0.92 -> 0.50
- AUTO_DELETE_ALLOWED_SEVERITIES default 'critical' -> 'critical,high,medium'
- Add auto-delete config section to .env.example

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 15:35:07 +07:00
MythEclipse 8f6a35f591 feat: integrate NVIDIA Nemotron-3 Content Safety API for Indonesian badword detection
- Added configuration options for NVIDIA Nemotron API key, model, and base URL.
- Refactored badword detection to utilize NVIDIA API, with a fallback to a local badword list.
- Updated moderation functions to handle asynchronous operations for text evidence generation.
- Removed dependency on the `indonesian-badwords` package and implemented custom detection logic.
- Enhanced tests to accommodate asynchronous behavior and validate new detection methods.
2026-05-30 14:48:50 +07:00
MythEclipseandClaude Opus 4.6 3cc6b7a924 fix(auto-delete): derive severity/action from legacy fields, log at info level
- Derive severity from status/score when ai_severity missing
- Derive recommended_action from severity/status when field missing
- Upgrade skip logs from debug to info so skip reasons are visible
- Allow both 'flagged' and 'warn' status for eligibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-30 03:40:26 +07:00
MythEclipse 6eb83b864f feat(moderation): update auto-delete logic to include 'warn' status for message eligibility 2026-05-30 02:48:27 +07:00
MythEclipseandClaude Opus 4.6 29a186fea1 fix(db): add migration for AI moderation review schema
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-30 01:14:12 +07:00
MythEclipse d082cd850b Merge branch 'worktree-agent-acd4fe7096dc8d6e4' 2026-05-30 01:04:43 +07:00
MythEclipseandClaude Opus 4.6 c894e5cd75 feat: expand AI moderation with structured analysis, review workflow, and guardrails
- Add structured AI moderation fields (categories, severity, confidence,
  recommended_action, policy_version, evidence) to messages table
- Add moderation_reviews, moderation_actions, and retention_policies tables
- Upgrade LLM response parsing to support structured metadata with backwards
  compatibility for legacy responses
- Implement public AI evaluation review UI with decision controls
  (approve, false positive + reanalyze, escalate)
- Add auto-delete guardrails requiring high confidence, severity, and
  allowed categories; log all attempts to moderation_actions
- Add retention manager scaffolding for messages/attachments/voice
- Add action executor for moderation actions (mute, warn, kick, ban)
- Add review routes: GET/POST/PATCH /api/reviews, GET/POST/PATCH /api/actions
- Preserve auth separation: voice/media/recordings gated, review public

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-30 01:02:51 +07:00
MythEclipse 394bd5a471 feat(moderation): skip message capture for specific channels in shouldCaptureMessageLocation 2026-05-30 00:37:20 +07:00
MythEclipse b938420eb3 feat(moderation): enhance Indonesian slang lexicon with additional profanity normalization and expand badword detection logic 2026-05-30 00:14:28 +07:00
MythEclipse d03244a0ae feat(moderation): update lexicon to include sexual deviation topics and clarify analysis guidelines 2026-05-30 00:01:09 +07:00
MythEclipse 81606f49a5 feat(moderation): enhance Indonesian slang lexicon with profanity normalization 2026-05-29 23:59:38 +07:00
MythEclipse 9ed50bc8ed feat(ui): add "analytics" tab to activeTabs and defaultSharedUIState 2026-05-29 21:10:00 +07:00
MythEclipse 687fcec62c chore: add comment to clarify app initialization process 2026-05-29 20:54:57 +07:00
MythEclipse 8c3bb77984 feat: add analytics hooks and routes for moderation statistics
- Implemented `useAnalytics` hook for fetching and managing analytics data.
- Created `analyticsStore.ts` to handle database queries for hourly stats, topic trends, user leaderboard, and moderation stats.
- Added Express routes for analytics endpoints including overview, hourly stats, topic trends, user leaderboard, moderation stats, and top violators.
- Introduced a utility function `filterHits` for filtering specific terms in text.
2026-05-29 19:37:08 +07:00
MythEclipseandClaude Opus 4.8 fb09ac81c5 feat(moderation): Indonesian slang normalizer and false-positive prevention
- Add indonesian-badwords dependency for local lexical signal
- Add Indonesian slang lexicon with woy/woi/hadeh as safe casual terms
- Normalize Discord custom emoji <:name:id> to [emoji:name] in prompts
- Wire normalization evidence into both conversationContext and llmModerationClient prompts
- Harden system prompt: woy/woi are casual greetings, not SARA/hate
- Add tests for emoji normalization, slang mapping, badword detection

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 18:39:10 +07:00
Asep Haryana SaputraandGitHub 87a4afec26 Merge pull request #7 from MythEclipse/feature/auto-delete-flagged-messages
feat(config): change default value of boolean to false in config schema
2026-05-29 18:13:16 +07:00
MythEclipse b96fb619c9 feat(config): change default value of boolean to false in config schema 2026-05-29 18:12:28 +07:00
Asep Haryana SaputraandGitHub 83e077a80e Merge pull request #6 from MythEclipse/feature/auto-delete-flagged-messages
feat(moderation): auto-delete flagged messages
2026-05-29 18:05:25 +07:00
MythEclipseandClaude Opus 4.8 7da83387ee feat(moderation): auto-delete flagged messages
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 18:04:42 +07:00
MythEclipse 649283a3bc feat(moderation): enhance media analysis handling and integrate image evidence 2026-05-29 17:43:34 +07:00
MythEclipse 2b1c436b3e feat(moderation): enhance media evidence handling and deferral analysis rejection 2026-05-29 17:32:04 +07:00
MythEclipse 0e9890db79 chore: remove node-datachannel submodule in favor of npm package 2026-05-29 15:37:43 +07:00
Asep Haryana Saputra 4938aa3a2f ci: remove obsolete deploy.yml, replaced by notify-parent.yml 2026-05-29 15:00:01 +07:00
Asep Haryana Saputra 3ff51fd72e ci: remove VPS deploy step; build/push/deploy now handled by monorepo 2026-05-29 14:09:02 +07:00
Asep Haryana Saputra bdf0f302a7 ci: add notify-parent workflow for monorepo integration 2026-05-29 14:08:23 +07:00
MythEclipse 82e77d9f12 fix(moderation): update Traefik router rule to use correct host 2026-05-29 13:15:56 +07:00
MythEclipse f624eea94b fix(moderation): update Traefik router rule to use correct hosts 2026-05-29 03:46:38 +07:00
MythEclipse 130f13e8a7 fix(moderation): update Traefik router rule to use correct host 2026-05-29 02:52:41 +07:00
MythEclipse 441ff5a0ed feat(moderation): fetch and analyze URLs (images and web text) from messages
- Added  to safely extract and fetch up to 3 URLs per message (with SSRF protection, 5MB limit, and 8s timeout).
- Implemented recursive  extraction to resolve Tenor/Giphy links from their HTML viewers to raw GIF binaries.
- In , fetched images are automatically injected as  into the vision LLM context, and truncated webpage text is appended to the message string.
2026-05-28 01:29:31 +07:00
MythEclipse c6af313c33 fix(moderation): immediately abort retries on 429 Too Many Requests
- In llmModerationClient.ts (inner retry), if OpenAI throws a 429 (or 401/403), throw p-retry's AbortError to immediately exit the 3-attempt inner retry loop.
- In aiAnalyzer.ts (outer retry), propagate the AbortError from runModerationAnalysis so the 2-attempt outer retry loop also aborts immediately.
- This ensures that a burst of 20 concurrent tasks hitting rate limits immediately returns the messages to the DB queue (as 'analysis_incomplete') and rapidly increments the individual circuit breaker, pausing processing and preventing a thundering herd instead of making 12 API calls per stuck message.
2026-05-28 01:09:57 +07:00
MythEclipse 9976e66ca5 fix(moderation): patch 3 additional aiAnalyzer vulnerabilities
Infinite recovery loop (#new):
  - Inside retryWithBackoff callback in processIndividualFallback, detect
    'analysis_incomplete' in the LLM result and throw to trigger backoff.
  - Track exhaustedOnIncomplete flag across retries.
  - On final exhaustion: write terminal flag 'individual_analysis_exhausted'
    to DB so the recovery query (which only looks for 'analysis_incomplete')
    never picks this message up again.
  - Transient failures (network/parse) are NOT written as exhausted; they
    remain as 'analysis_incomplete' and are retried via the CB-throttled
    recovery cycle.

Token budget zero-result deadlock (#10):
  - If pickBatchWithinBudget returns [] because every candidate message
    individually exceeds AI_ANALYSIS_MAX_TARGET_TOKENS, fall back to
    messages.slice(0,1) so at least the first message is processed.
  - Without this, messages would be permanently stuck as 'pending' because
    every recovery tick would fetch them, trim to 0, and exit silently.
  - Uses messages.slice(0,1) instead of messages[0]! to avoid the
    forbidden noNonNullAssertion lint rule.

Stale state map memory leak (#9):
  - startPendingAIAnalysisWorker now prunes conversationErrorCooldown and
    conversationProcessing on every recovery interval tick.
  - Cooldown entries past their expiry timestamp are deleted.
  - Processing entries older than AI_ANALYSIS_PROCESSING_TIMEOUT_MS are
    deleted (these represent stale locks from crashed processing runs).
  - Prevents unbounded Map growth for long-running bots with many channels.

Batch/individual scheduling collision (#8):
  - Build incompleteKeySet (Set<string>) from incompleteKeys before the
    batch recovery loop.
  - Batch recovery loop skips any key present in incompleteKeySet so a
    conversation that has both 'pending' and 'analysis_incomplete' messages
    is only targeted by the individual pipeline, not both simultaneously.
  - Avoids the DB last-write-wins race where batch and individual pipelines
    both update the same message rows concurrently.
2026-05-28 00:07:55 +07:00
MythEclipse 61045aabc8 fix(moderation): patch 6 aiAnalyzer audit vulnerabilities
#1+#5 - Individual fallback circuit breaker
  - Add individualConsecutiveErrors + individualCooldownUntil (30s)
  - On success: reset counter; on failure: increment + trip at
    AI_ANALYSIS_INDIVIDUAL_CB_THRESHOLD (default 10) consecutive errors
  - enqueueIndividualFallbacks checks CB before admitting any work

#1 - Individual fallback concurrency cap
  - enqueueIndividualFallbacks enforces AI_ANALYSIS_INDIVIDUAL_MAX_CONCURRENT
    (default 20); overflow stays as error/analysis_incomplete in DB and is
    recovered by the recovery worker on the next interval

#3 - Unhandled rejection in async setTimeout
  - scheduleConversationAnalysis no longer uses async arrow in setTimeout;
    all async work is chained with .then()/.catch() explicitly

#4 - Recovery worker ignores individualInFlight
  - Add individualInFlightByConversation Map<conversationKey, count>
  - processIndividualFallback increments/decrements it in try/finally
  - startPendingAIAnalysisWorker skips conversations present in the map
  - Recovery worker also processes error/analysis_incomplete messages via
    two new messageStore queries: getConversationKeysWithIncompleteAnalysis
    and getIncompleteMessagesByConversation

#6 - pickBatchWithinBudget never called
  - scheduleConversationAnalysis now calls pickBatchWithinBudget with
    AI_ANALYSIS_MAX_TARGET_TOKENS (default 4000) + 50-token per-msg overhead
    after fetching messages, before passing to processBatch

#7 - AI_PROCESSING_OVERLAP_MS 30s shorter than max LLM retry window
  - Replace hardcoded 30 000 ms constant with configurable
    AI_ANALYSIS_PROCESSING_TIMEOUT_MS (default 120 000 ms)
  - LLM client: 30s timeout × 3 retries + backoff ≈ 90-100s; 120s is safe

New config keys:
  AI_ANALYSIS_PROCESSING_TIMEOUT_MS   (default: 120000)
  AI_ANALYSIS_INDIVIDUAL_MAX_CONCURRENT (default: 20)
  AI_ANALYSIS_INDIVIDUAL_CB_THRESHOLD  (default: 10)
  AI_ANALYSIS_MAX_TARGET_TOKENS        (default: 4000)

New AnalysisQueueStatus fields:
  activeIndividualRequests, individualInFlightCount,
  individualCircuitBreakerActive
2026-05-27 23:32:38 +07:00
MythEclipse 5925c11c54 feat(moderation): two-tier batch+individual fallback pipeline
- After a batch LLM call, any result flagged analysis_incomplete is
  immediately fanned out to an individual per-message fallback queue
- Batch hard-fail (result.ok=false) and unhandled exceptions now also
  route all affected messages to the individual queue instead of waiting
  behind the conversation error cooldown
- Individual queue runs fully parallel (fire-and-forget per message),
  de-duplicated by a Set<messageId> so no double-processing
- processIndividualFallback runs in the main process (no worker pool IPC
  overhead for a single-item call), with retryWithBackoff 2x/2-15s
- AnalysisQueueStatus gains activeIndividualRequests +
  individualInFlightCount fields for dashboard observability
2026-05-27 23:25:37 +07:00
MythEclipse 2156c52c35 feat: add OpenAI integration with custom header logging and request aborting 2026-05-26 01:34:37 +07:00
MythEclipse c1c149855a feat: enforce strict AI env validation and force close 2026-05-26 00:59:32 +07:00
MythEclipse 7126959548 fix: bypass Cloudflare WAF 403 blocks by spoofing User-Agent and removing X-Stainless headers 2026-05-26 00:03:01 +07:00
MythEclipse cc61e2576b refactor: optimize AI moderation pipeline, fix OOM risks, token duplication, and add Zod validation 2026-05-25 23:23:12 +07:00
MythEclipse cc2ee84c3b refactor(ai-analyzer): fix resource leaks, OOM risk, and strict structured outputs 2026-05-25 22:14:05 +07:00
MythEclipse c32b5274e9 chore: update dependencies and configuration
- Added @discordjs/opus and opusscript to package.json and pnpm-lock.yaml.
- Updated pnpm-workspace.yaml to allow builds for @discordjs/opus.
- Imported dotenv in config.ts for environment variable management.
- Created .npmrc to manage npm configurations.
2026-05-24 20:06:36 +07:00
MythEclipse 34b15e20dc chore(vendor): update discord-video-stream to latest commit from fork 2026-05-23 17:38:48 +07:00
MythEclipseandClaude Opus 4.7 2183955133 chore(submodules): update all submodule URLs to point to MythEclipse forks
Update all vendor submodules to use forks under MythEclipse account:
- vendor/discord-video-stream
- vendor/drizzle-orm
- vendor/better-sqlite3
- vendor/node-datachannel

This ensures all submodules are under your control and can be updated
independently without relying on upstream repositories.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 17:38:34 +07:00
MythEclipse 246919cfa0 chore(vendor): update discord-video-stream subproject to latest commit 2026-05-23 17:36:09 +07:00
MythEclipseandClaude Opus 4.7 9b49c05f32 fix(streaming): quote User-Agent header to prevent ffmpeg argument splitting
The ffmpeg -headers option was receiving the User-Agent value split across
multiple arguments due to spaces not being properly quoted. This caused ffmpeg
to interpret "Mozilla/5.0" as an output format, resulting in:
  [NULL @ ...] Unable to find a suitable output format for 'Mozilla/5.0'

Fixed by wrapping the entire headers string in quotes so parseArgsStringToArgv
treats it as a single argument. The headers string is now properly passed to
ffmpeg as: -headers "User-Agent: ... \r\nConnection: ..."

The fix has been applied to vendor/discord-video-stream/src/media/newApi.ts
and compiled into dist/media/newApi.js. A patch file and documentation have
been added to the patches/ directory for reference.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 17:35:42 +07:00
MythEclipseandClaude Opus 4.7 f2b476e1f0 fix(streaming): quote User-Agent header to prevent ffmpeg argument splitting
The ffmpeg -headers option was receiving the User-Agent value split across
multiple arguments due to spaces not being properly quoted. This caused ffmpeg
to interpret "Mozilla/5.0" as an output format, resulting in:
  [NULL @ ...] Unable to find a suitable output format for 'Mozilla/5.0'

Fixed by wrapping the entire headers string in quotes so parseArgsStringToArgv
treats it as a single argument. The headers string is now properly passed to
ffmpeg as: -headers "User-Agent: ... \r\nConnection: ..."

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 17:31:21 +07:00
MythEclipse 4ec9b50f33 fix(moderation): interleave images with owning messages, Indonesian-first prompt
- Replace flat imageParts prologue with per-message image map (messageImageMap)
  keyed by message_id. Images are now inserted immediately after their owning
  message's text part in the multimodal content array, giving the vision model
  proper text+image co-context instead of a disconnected image dump before the
  entire prompt.

- Rewrite moderationPrompt as Indonesian-first bilingual system prompt:
  * Primary language: Bahasa Indonesia; English secondary
  * Explicit Discord community context with Indonesian slang awareness
    (anjay, wkwk, santuy, gw/lo abbreviations, etc.)
  * SARA, hoaks, ujaran kebencian cultural context
  * Charitable intent for ambiguous Indonesian phrasing
  * Expanded flag taxonomy: sara, hoaks, nsfw_image, gore_image, doxxing, scam
  * analysis field instructed in Bahasa Indonesia (maks 2 kalimat)
  * Retry/correction messages also in Bahasa Indonesia

- Image instruction block conditionally injected into prompt only when
  hasImages=true, explicitly telling model to treat image + preceding text
  as one semantic unit and to OCR meme/screenshot text as message content.
2026-05-22 01:04:00 +07:00
MythEclipse d0e906763e fix(moderation): fix image attachment pipeline causing PIL BadRequestError on NVIDIA inference
Three-layer defect chain causing 'cannot identify image file <_io.BytesIO object>':

1. attachmentUploader: hardcoded 'application/octet-stream' on Tele CDN upload
   regardless of actual file MIME type — CDN stored images under wrong type.

2. messageCapture: processAttachmentUpload call site never forwarded
   attachment.contentType into the options bag, so the fix in (1) would
   have received undefined and fallen back to octet-stream anyway.

3. llmModerationClient: blindly trusted att.type from the DB record
   (Discord-provided MIME) when constructing data: URLs, but validated
   neither the HTTP status of the CDN re-fetch nor the actual byte content.
   Stale/expired CDN URLs returning HTML error pages were base64-encoded
   and sent to the model as 'image/jpeg', causing PIL to reject the stream.

Fixes:
- uploadAttachmentToTele now accepts contentType param (defaults to
  application/octet-stream for non-image files)
- processAttachmentUpload options bag gains optional contentType field
- messageCapture forwards attachment.contentType at the call site
- Added sniffImageMimeType() using magic-byte probes for JPEG, PNG, GIF,
  WebP, AVIF/HEIF — runs on every downloaded attachment buffer before
  base64 encoding; skips the attachment (logs headerHex for diagnosis)
  if bytes don't match a known image format
- data: URL now uses the sniffed MIME type, not the DB record
2026-05-21 23:44:18 +07:00
Asep Haryana Saputra d76549f94a refactor(app): simplify path handling for static files and index.html 2026-05-21 12:35:28 +00:00
Asep Haryana Saputra 610bcf2b8e feat(config): add build options for rolldown checks in Vite configuration 2026-05-21 12:30:04 +00:00
Asep Haryana Saputra 41197fd2c2 feat(build): optimize Dockerfile to build vendor packages during image build and streamline package.json scripts 2026-05-21 12:27:07 +00:00