7.5 KiB
Batch 4 QA Report
Date: 2026-05-24
Scope: Workspace build validation, Telegram API constraint analysis, regression checklist
Blockers: None
1. Build Results
| Check | Result | Details |
|---|---|---|
cargo check --workspace |
✅ PASS | Finished in 5.38s, all crates compile cleanly |
cargo test --workspace |
✅ PASS | All test suites pass (0 unit tests present across crates, all doc-tests pass) |
cd frontend && npm run build |
✅ PASS | Built in 1.44s. Warning: lib-BKGKj-wr.js (497 kB) and index-5C1xoqEL.js (1,038 kB) exceed 500 kB after minification. This is a Vite chunk-size warning, not a build failure. |
2. Telegram API Constraint Analysis
2.1 Caption Length — ⚠️ FLAGGED
Location: crates/cgcx-bot/src/main.rs, handle_forward_callback, "approve" action (~line 1810)
Code:
let caption = format!(
"{}\n\nSubmitted by: {}\nDirect link: <code>{}</code>\nForward link: <code>{}</code>",
escape_html(&forward_def.forward_message),
author_line,
link,
forward_link
);
Analysis:
forward_def.forward_messageis stored asTEXT NOT NULL DEFAULT ''in SQLite (migrations/003_forward_system.sql). SQLiteTEXThas no practical length limit.- The fixed parts of the caption (
Submitted by: …,Direct link: …,Forward link: …) add ~60–120 characters. author_linecan be up to ~60 characters (escaped username + ID).linkis ~80–120 characters (base URL + 12-char CXID + 12-char password).forward_linkis ~70–100 characters (t.me/{bot}?start=submitfwdid{code}).- Telegram API limit for captions: 1,024 characters.
Risk: If an admin sets a forward_message longer than ~800 characters, the total caption will exceed 1,024 characters. Telegram will reject the send_media_group or send_message call with a Bad Request: MEDIA_CAPTION_TOO_LONG error. The code does not truncate or validate caption length before sending.
Recommendation: Truncate forward_def.forward_message to a safe limit (e.g., 700 chars) before interpolating into the caption, or split into a separate text message if the message is long.
2.2 Media Type Handling — 📋 NOTED
Locations:
finalize_upload, review-group media batch (~line 1340)handle_forward_callback, destination media batch (~line 1850)
Code:
let media = if mime_type.starts_with("image/") {
InputMedia::Photo(InputMediaPhoto::new(input_file))
} else {
InputMedia::Document(InputMediaDocument::new(input_file))
};
Analysis:
- Images are correctly sent as
InputMediaPhoto. - All non-image files are sent as
InputMediaDocument, regardless of MIME type.
Impact:
- Video files (
video/mp4, etc.) lose native Telegram playback UI (no inline player, no duration badge, no thumbnail generation). - Audio files (
audio/mpeg, etc.) lose native audio player UI. - Telegram treats them as generic documents, which degrades UX in review and destination groups.
Recommendation: Map MIME types more precisely:
| MIME prefix | Current | Better |
|---|---|---|
image/* |
InputMediaPhoto |
✅ Keep |
video/* |
InputMediaDocument |
InputMediaVideo |
audio/* |
InputMediaDocument |
InputMediaAudio |
| other | InputMediaDocument |
✅ Keep |
2.3 Memory Usage — 📋 NOTED
Locations:
finalize_upload, review-group decryption (~line 1320)handle_forward_callback, destination decryption (~line 1830)
Code pattern:
match cgcx_crypto::decrypt_bytes(&ciphertext, &file.encrypted_key_wrapped, &ctx.master_key) {
Ok(bytes) => decrypted.push((file.mime_type.clone(), bytes)),
...
}
...
let input_file = InputFile::memory(bytes.clone());
Analysis:
tokio::fs::read(&file.stored_path)loads the entire encrypted file into memory asciphertext.decrypt_bytesdecrypts in-memory and returns a newVec<u8>(bytes). At this point, two copies of the file exist in RAM (ciphertext + plaintext).InputFile::memory(bytes.clone())clones the plaintext bytes again for theInputMediastruct. Now three copies may exist transiently.- Files are batched in chunks of 10 (
decrypted.chunks(10)), so up to 10 files are held in memory simultaneously.
Risk: For large uploads (e.g., a 100 MB video), this can easily exhaust RAM, especially on constrained hosts or when multiple submissions are processed concurrently. The bot does not stream or chunk-decrypt files.
Recommendation: Consider streaming decryption to temporary files and using InputFile::file(path) instead of InputFile::memory(bytes). This keeps only one copy on disk instead of multiple copies in RAM.
3. Regression Checklist — Batch 4
Use this checklist before merging or deploying Batch 4 changes:
Build & Compile
cargo check --workspacepasses with zero errorscargo test --workspacepasses (all suites green)cd frontend && npm run buildproducesdist/without errors- No new compiler warnings introduced in
cgcx-bot
Bot Runtime
- Bot starts successfully and connects to Telegram (
get_mesucceeds) - InMemStorage dialogue state machine transitions correctly (Start → TermsPending → MainMenu → UploadStaging → UploadOptions → UploadFinalizing)
- Service message cleanup works in groups/channels and is silently skipped in private chats
- Punishment expiration timer revokes bans/mutes after duration elapses
- Global ban propagation (
propagate_punishment) only runs whenconfig.groups.global_ban == true
Upload & Submission Flow
- Staging accepts media, documents, and text up to
max_batch_size - Upload options (destroy, download, password, show_author) toggle correctly
finalize_uploadrespectsmax_total_batch_byteslimit- Disk-space check (
fs2::available_space) blocks uploads when temp space < 2× batch size - Blocked-hash detection (
CgcxError::BlockedHash) aborts upload and cleans up
Forward & Review System
/create_submit_forwardvalidates bot admin status in both destination and review groups- Submission links (
?start=submitfwdid{code}) work and enforce allow-lists - Review message is sent to review group with correct inline keyboard
- Approve action sends media batch to destination without
MEDIA_CAPTION_TOO_LONGerror - Approve action sends DM confirmation to submitter with posted link
- Ignore/Ban/Blacklist/Ban+Blacklist callbacks update review message and submitter correctly
- Media batching handles >10 files by splitting into multiple
send_media_groupcalls
Admin Commands
/reloadrefreshes moderation lists/blacklist_uid,/whitelist_uidupdate DB and moderation engine/sban,/smute,/mute,/pban,/kickresolve target user and apply restrictions/rmute,/rbanrevoke active punishments/get_idreturns chat ID or searches admins by username/display name
Security & Stability
- Panic hook logs location and message
CatchPanicLayer-swallowed panics are traceable via logs- 8MB thread stack prevents stack overflow during dptree dispatch
4. Blockers
No critical blockers. All builds pass.
Non-blocking issues identified:
- Caption length risk — can cause Telegram API rejection on approval; should be mitigated before relying on forward system in production.
- Media type mapping — video/audio UX is degraded; nice-to-have improvement.
- Memory usage — large files may cause OOM during forward review/approval; should be monitored or mitigated for production load.