Wrap the forced yt-dlp download (and its node/ffmpeg children) and the rsync
upload with 'nice -n 19 ionice -c 3' so the known-big, slow /force job yields the
CPU and disk to everything else under load while still finishing fast when idle.
Normal short downloads stay at full priority. Best-effort: prefix is empty if the
tools are absent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When a posted video is too long (>1h, probed up front) or a normal download
fails as too-big/timeout, the bot offers /force. Replying /force downloads the
full video off-loop (separate slot, no size cap, --max-filesize 10G), uploads it
via rsync/SSH to a web docroot, and posts a tokenized link auto-deleted after 24h
by an hourly sweep (also evicts oldest past 10 GB; remote-clock TTL; only ever
removes its own token dirs).
Hosting target is env-configured (FORCE_REMOTE/FORCE_REMOTE_DIR/FORCE_BASE_URL/
FORCE_SSH_PORT/FORCE_SSH_KEY) so published code carries no infra details; the
feature self-disables when unset (probe is skipped too, so normal posting is
unchanged). Staged on disk (not tmpfs), self-heals orphaned staging dirs, and
re-arms the offer on transient failure. See signal-bot.service.example.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Switch storage backend from in-memory to SQLite (silences the library warning).
recent_urls (the edit-redelivery dedup map) now survives restarts: it's tiny and
TTL-bounded, stored via the library KV store, with wall-clock timestamps and
prune-on-load. last_video is deliberately NOT persisted — each value is a base64
video up to ~133 MB, so persisting it would bloat the DB; it stays in memory.
DB lives at bot-state.db (gitignored).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bot.py:
- Run all yt-dlp/ffmpeg/ffprobe/file-IO/base64 work off the event loop via
asyncio.to_thread, bounded by a Semaphore(2); the loop no longer freezes
bot-wide during downloads/encodes.
- download_attachments=False; VideoTracker fetches only video attachments lazily
instead of the library base64-ing every attachment of every message.
- last_video keyed per (group, sender) with group-latest fallback so /speed and
/rev stop silently targeting a stranger's video; proactive TTL sweeps free the
big base64 blobs and bound recent_urls growth.
- _reencode guards 0/NaN ffprobe duration and adds -maxrate/-bufsize; /rev now
size-checks + re-encodes + faststart like the other paths.
- Twitter URL regex no longer merges space-separated links (keeps i/web/status);
dedupe repeated URLs within a message; mark-handled only on success/no-media so
a corrective edit can retry; 'unsupported url' surfaced instead of silently dropped.
- All sends wrapped (catch SendMessageError); base64 decode guarded; edit/sync
attachment envelopes handled; /cookies temp file created 0600.
deploy:
- Pin signalbot==1.1.0, yt-dlp floor, add missing yt-dlp-ejs.
- Pin signal-cli-rest-api by digest + add /v1/health healthcheck.
- Restart=always (the library never exits non-zero when wedged).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
/clip <seconds> in a link's message overrides the default 60s clip
window (capped at 600s); with a ?t= it sets the window length, without
one it clips from the start. /help lists every command with examples.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When a YouTube link carries a t=/start= offset, download only a 60s
window around it via yt-dlp --download-sections instead of the whole
video. Hour-long uploads shared at a timestamp previously failed the
100 MB Signal limit even after re-encoding.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Group-chat bot that downloads videos from X/Instagram/YouTube/TikTok links
via yt-dlp and posts them back, plus /speed and /rev video toys.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>