food-market/docs/telegram-bridge.md
nurdotnet d455087bc8 feat(ops): Telegram <-> tmux bridge + local docker-registry unit
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>
2026-04-23 10:53:45 +05:00

87 lines
4.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`. Создайте её как обычно:
```bash
tmux new-session -d -s claude
tmux attach -t claude # и запустите внутри `claude` (или что там у вас)
```
Сервис стартует даже без сессии — в лог упадёт warning, но `send-keys` / `capture-pane` начнут работать как только сессия появится. Имя сессии можно переопределить через env `TMUX_SESSION=other` в юните.
### 3. Старт сервиса
```bash
sudo systemctl enable --now food-market-telegram-bridge.service
```
В ответ бот пришлёт `✅ bridge up …` — это индикатор успеха.
## Эксплуатация
### Логи
```bash
sudo journalctl -u food-market-telegram-bridge.service -f
sudo journalctl -u food-market-telegram-bridge.service --since '10 min ago'
```
### Перезапуск
```bash
sudo systemctl restart food-market-telegram-bridge.service
```
### Остановить
```bash
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` (owner `nns`, mode `0600`)
- 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, без входящих портов) — никакого проброса портов не нужно.