Files
obsidian-vault/personal/projects/zulip-router.md
T

99 lines
4.3 KiB
Markdown
Raw 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.
# Zulip Router
_Последнее обновление: 2026-06-16_
## Цель
Event-роутер между Zulip и balda-ботами (Валера, Клавдий).
Решает проблему конкурентного получения событий — сейчас оба бота через Events API видят все сообщения одновременно.
## Проблема (root cause)
- Каждый balda-бот регистрирует собственную event queue в Zulip Events API
- Без роутера оба получают все сообщения, и каждый может ответить на @mention другого
- `extractOtherMention()` патч в коде — костыль, не решает проблему полностью
## Как сейчас (до роутера)
```
Zulip
├── Events API ← Валера (valera-bot, poll на :8091)
└── Events API ← Клавдий (claudio-bot, poll на :8092)
└── Webhook → оба (outgoing webhook bot_type=3 через @mention)
```
## Архитектура (с роутером)
```
Zulip Event Queue
↓ poll (отдельный бот-роутер или reuse одного из существующих)
[zulip-router] (~/Docker/zulip-router/)
├── @Валера / @Valera → POST http://balda-agent-valera-1:8091/zulip/webhook
├── @Клавдий / @Klavdiy → POST http://claudio-agent-claudio-1:8092/zulip/webhook
└── (default / unknown) → POST http://balda-agent-valera-1:8091/zulip/webhook
↕ ownership: stream+topic → bot (TTL 24h, persisted to /data/ownership.json)
```
**Важно:** таргеты — не Hermes инстансы, а balda-боты (Go бинарники). У каждого уже есть webhook endpoint `/zulip/webhook`.
## Правила маршрутизации
1. `@Валера` или `@Valera` в тексте → Валера (+ ownership обновляется)
2. `@Клавдий` или `@Klavdiy` в тексте → Клавдий (+ ownership обновляется)
3. Топик уже у кого-то → туда же (TTL refreshed)
4. Новый топик без @mention → Валера (default)
5. `reset` без @mention → только владельцу топика
## Что меняется в balda
После деплоя роутера у обоих ботов нужно **отключить Events API polling** в config.yaml:
```yaml
zulip:
events_polling:
enabled: false # ← было true
```
После этого сообщения приходят **только через webhook** (от роутера).
Перезапуск: `docker-compose up -d` (в каждом compose).
## Код
`~/Docker/zulip-router/` — Go, ~260 LOC (план)
| Файл | Назначение |
|------|------------|
| `main.go` | Poll loop, routing, dispatch |
| `config.go` | Config struct + YAML loading + env var expansion |
| `zulip.go` | Zulip API client (register_queue, get_events, long-poll) |
| `ownership.go` | Topic ownership store (RW mutex + JSON persistence) |
| `forwarder.go` | HTTP POST к balda webhook endpoints |
## Deployment
**Compose:** `~/Docker/zulip-router/docker-compose.yaml`
**Env vars:**
```env
ZULIP_ROUTER_API_KEY=<api-key бота-роутера>
```
**Запуск:**
```bash
cd ~/Docker/zulip-router
docker-compose up -d
```
## TODO
- [ ] Написать код роутера (~260 LOC)
- [ ] Создать compose `~/Docker/zulip-router/docker-compose.yaml`
- [ ] Создать бота-роутера в Zulip (или реюзнуть valera-bot/claudio-bot), получить API key
- [ ] `docker-compose up -d`
- [ ] Проверить логи: `docker logs zulip-router --tail 50`
- [ ] Отключить `events_polling.enabled` у Валеры → `docker-compose up -d`
- [ ] Отключить `events_polling.enabled` у Клавдия → `docker-compose up -d`
- [ ] Протестировать: @Клавдий и @Валера в разных топиках
- [ ] Проверить ownership: сообщение в топик без @mention → последний бот
- [ ] Удалить `extractOtherMention()` патч из кода (больше не нужен)