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
- Introduced `loggerSerialization.ts` to handle error serialization and log metadata formatting.
- Added `serializeError` function to convert Error objects into a structured format.
- Implemented `serializeLogValue` to handle various data types including Errors, Dates, RegExps, and plain objects.
- Created `formatLogMetadata` to format log metadata using the serialization functions.
feat(pagination): implement cursor encoding and decoding
- Added `pagination.ts` to manage cursor-based pagination.
- Implemented `encodeCursor` to convert cursor data into a base64 string.
- Developed `decodeCursor` to parse base64 strings back into cursor data, with error handling for invalid inputs.
The Dockerfile was only copying discord.js-selfbot-v13/package.json but not
discord-video-stream/package.json before running pnpm install. This caused
pnpm to fail with ERR_PNPM_WORKSPACE_PKG_NOT_FOUND since @dank074/discord-video-stream
is declared as a workspace dependency but its package.json wasn't available.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Added allowBuilds for specific packages in pnpm-workspace.yaml
- Excluded certain minimum release ages for packages
- Updated onlyBuiltDependencies list
- Marked discord.js-selfbot-v13 subproject as dirty
- 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.
Design for migrating from Pino to Winston with hybrid console/file logging,
npm-standard log levels, and dependency cleanup.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Implemented PostgreSQL and SQLite schemas for voice recordings.
- Created repository functions for inserting, updating, and listing voice recordings.
- Developed uploader logic to handle file uploads and database updates.
- Added routes for accessing voice recordings via API.
- Enhanced UI state to include recordings tab.
- Introduced moderation WebSocket event for uploaded recordings.
- Add ffmpeg stderr monitoring to catch audio decoding errors
- Add HTTP timeout parameters (-connect_timeout, -read_timeout) for VPS reliability
- Add URL validation in yt-dlp to ensure audio URLs are resolved
- Add comprehensive logging to mediaController for queue operations
- Add logging to musicPlayer for ffmpeg command execution and errors
- Update musicPlayer test to expect new timeout parameters
Fixes:
1. Silent audio issue: ffmpeg errors are now visible in logs
2. Queue bypass: Added logging to track queue → playback flow
3. VPS network issues: Added timeouts for HTTP stream connections
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Call unpause() when starting a stream in DiscordPlayer to prevent getting stuck in a paused state.
- Call stop() instead of pause() in VoiceController on disconnect to fully reset the player.
- Add user-agent header to ffmpeg calls in MusicPlayer and Transcoder to prevent YouTube 403 Forbidden on VPS.
- Add screenshare streams to the media queue when successfully started.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Full rewrite of legacy vanilla JS UI into React SPA
- Implement modern design system using Tailwind CSS and shadcn/ui primitives
- Create typed API modules and hooks for voice, media, and moderation
- Add new features: separated Music and Screen Share panels, Image Grid
- Implement unified WebSocket hook for real-time state and PCM audio
- Improve visualizer with smooth CSS transitions and live state sync
- Add __dirname polyfill for ES module compatibility
- Ensure responsive layout for mobile and desktop
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add mode selector buttons (Music / Screen Share)
- Create separate input panels for music and screen share
- Add screenSourceInput and screen share queue/skip/stop buttons
- Update queueMedia to include mode parameter (music/screen)
- Add queueScreen function for screen share mode
- Toggle between panels based on selected mode
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Import fileURLToPath from node:url
- Define __dirname using import.meta.url for ES module compatibility
- Fixes ReferenceError when serving static files in production
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>