Telegram bridge lets me drive the local Claude Code tmux session from my phone — inbound messages are typed into the 'claude' session, pane diffs are streamed back as plain Telegram messages (TUI noise, tool-call blocks, echoed user input and already-sent lines are filtered so only the assistant's actual reply reaches the chat). Deployed as food-market-telegram-bridge.service, reads creds from /etc/food-market/telegram.env (not committed). Also committing the local docker-registry unit for reproducibility — registry:2 on 127.0.0.1:5001, data persisted in /opt/food-market-data/docker-registry. Setup docs in docs/telegram-bridge.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.5 KiB
Telegram ↔ tmux bridge
Управление локальной сессией Claude Code с телефона через Telegram-бота. Входящее сообщение от whitelisted chat_id набирается в tmux-сессию claude как будто вы сами печатаете; ответный вывод пайнa каждые ~2.5 с отправляется обратно в чат.
Как это выглядит в работе
- Вы пишете боту:
запусти тесты - Бот делает
tmux send-keys -t claude -l "запусти тесты" && tmux send-keys -t claude Enter— текст попадает в поле ввода Claude - Фоновый поллер раз в 2.5 с снимает
tmux capture-pane, сравнивает с предыдущим снапшотом, присылает новые строки как<pre>…</pre>-блок
Команды бота:
/ping— живой ли, какая сессия и интервал/snapshot— выслать полный текущий пайн (полезно после длинного молчания или после рестарта)
Один раз — настройка
1. Креды
Положите в /etc/food-market/telegram.env:
TELEGRAM_BOT_TOKEN=<токен от @BotFather>
TELEGRAM_CHAT_ID=<ваш личный chat_id, целое число>
Узнать chat_id — напишите @userinfobot в Telegram, он ответит с вашим id. Файл доступен только владельцу (chmod 600).
Только сообщения от этого одного chat_id будут обработаны — всё остальное молча игнорируется.
2. tmux-сессия claude
Бот ожидает существующую сессию с именем claude. Создайте её как обычно:
tmux new-session -d -s claude
tmux attach -t claude # и запустите внутри `claude` (или что там у вас)
Сервис стартует даже без сессии — в лог упадёт warning, но send-keys / capture-pane начнут работать как только сессия появится. Имя сессии можно переопределить через env TMUX_SESSION=other в юните.
3. Старт сервиса
sudo systemctl enable --now food-market-telegram-bridge.service
В ответ бот пришлёт ✅ bridge up … — это индикатор успеха.
Эксплуатация
Логи
sudo journalctl -u food-market-telegram-bridge.service -f
sudo journalctl -u food-market-telegram-bridge.service --since '10 min ago'
Перезапуск
sudo systemctl restart food-market-telegram-bridge.service
Остановить
sudo systemctl stop food-market-telegram-bridge.service # до ребута
sudo systemctl disable food-market-telegram-bridge.service # и после ребута
Поменять интервал/сессию
Отредактируйте /etc/systemd/system/food-market-telegram-bridge.service, добавьте в секцию [Service]:
Environment=POLL_INTERVAL_SEC=1.5
Environment=TMUX_SESSION=other-session
Environment=CAPTURE_HISTORY_LINES=400
Затем sudo systemctl daemon-reload && sudo systemctl restart food-market-telegram-bridge.
Раскладка
- Скрипт:
/opt/food-market-data/telegram-bridge/bridge.py - venv (Python 3.12,
python-telegram-bot 21.x):/opt/food-market-data/telegram-bridge/venv/ - Креды:
/etc/food-market/telegram.env(ownernns, mode0600) - systemd unit:
/etc/systemd/system/food-market-telegram-bridge.service
Что хорошо знать
disable_notification=Trueстоит на фоновых сообщениях пайна — не будет жужжать при каждом diff'e.- Telegram-лимит 4096 символов; длинные пайн-блоки режутся на куски по ~3800 символов.
- Если после долгого молчания в чате слишком много истории, шлите
/snapshot— бот обнуляет baseline и присылает текущий экран целиком. - Бот заходит в Telegram long-polling (исходящее к api.telegram.org, без входящих портов) — никакого проброса портов не нужно.