- 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>
- 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.
- 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.
- 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>
- 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.
- 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.
- 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.
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
- Changed all import statements across the project to include the .js extension for consistency and to comply with ES module standards.
- Updated imports in various files including bootstrap.ts, shutdown.ts, config.ts, and many others.
- Ensured that all related modules and types are correctly imported with the new extension.
- Added `vendor/discord-video-stream` to pnpm workspace.
- Refactored `llmModerationClient.ts` for better readability and consistency.
- Adjusted imports in `recordingsRoutes.ts` for clarity.
- Updated `webserver.ts` to correctly import `createRecordingsRoutes`.
- Enhanced test cases in `llmModerationClient.test.ts` for improved readability.
- Updated submodule references for `better-sqlite3`, `discord-video-stream`, `discord.js-selfbot-v13`, `drizzle-orm`, and `node-datachannel`.
- Created documentation for deprecated dependency removal plan and design.