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>
signal-bot
A Signal group-chat bot that grabs videos from links people post (X/Twitter, Instagram, YouTube, TikTok) and posts them back into the chat, plus a couple of toys for messing with the last video.
What it does
- Auto-download — watches every group message for a supported link and replies with the video. Powered by yt-dlp. Videos over 100 MB are auto re-encoded with ffmpeg to fit.
/speed [n]— re-posts the last video sped upn× (default 2×), pitch-shifted like tape. e.g./speed 4,/speed 0.5./rev— re-posts the last video reversed (audio too)./cookies(DM, admins only) — paste exported.instagram.comcookie lines to refresh Instagram auth without touching the server.
Supported links: x.com / twitter.com (and fxtwitter/vxtwitter/fixupx
wrappers), instagram.com/reel|p, youtube.com / youtu.be / Shorts, and TikTok.
How it's wired
Signal ──► signal-cli-rest-api (json-rpc, :8080) ──► bot.py (signalbot lib)
[docker-compose.yml] [systemd service]
│
yt-dlp + ffmpeg + cookies.txt
bot.pyconnects to a signal-cli-rest-api daemon over a websocket injson-rpcmode.docker-compose.ymlruns that daemon; the linked-account data lives in a./signal-cli-datavolume (gitignored — it holds private keys).signal-bot.service.exampleis the systemd unit that runsbot.py.
Setup
-
Run signal-cli-rest-api and link it as a device to your Signal account:
docker compose up -dThen link via the QR at
http://127.0.0.1:8080/v1/qrcodelink?device_name=bot(scan from Signal → Settings → Linked devices). Keep signal-cli up to date — an out-of-date signal-cli silently stops receiving messages when Signal changes its server protocol (getServerGuid must not be nullin the logs);docker compose pull && docker compose up -dfixes it. -
Python env:
python3 -m venv venv venv/bin/pip install -r requirements.txtYouTube also needs a system
nodeon PATH (foryt-dlp-ejs). -
Cookies: copy
cookies.txt.exampletocookies.txtand fill in real exported cookies for the sites you want auth'd. (gitignored) -
Service: copy
signal-bot.service.exampletosignal-bot.service, set your user / phone number / paths, install it:sudo cp signal-bot.service /etc/systemd/system/ sudo systemctl enable --now signal-bot
Notes
- The bot only acts in group chats (except
/cookies, which is DM-only). - Reply-targeting (e.g. "speed up this video") was intentionally dropped:
Signal Desktop strips quote metadata from synced transcripts, so
/speedand/revalways act on the last video seen in the group.