95 lines
3.5 KiB
Markdown
95 lines
3.5 KiB
Markdown
---
|
|
tags: [project, hermes, zulip, go, infra]
|
|
status: deployed
|
|
created: 2026-06-16
|
|
---
|
|
|
|
# Zulip Router
|
|
|
|
## Цель
|
|
|
|
Event-роутер между Zulip и несколькими Hermes-инстансами.
|
|
Решает проблему конкурентного получения событий (Валера, Клавдий и другие боты видели все сообщения одновременно, `reset` срабатывал у всех).
|
|
|
|
## Проблема (root cause)
|
|
|
|
- Каждый Hermes-инстанс в polling-режиме регистрирует собственную event queue в Zulip
|
|
- `reset` — системная команда Hermes, обрабатывается до хука `whale-thread-guard`
|
|
- Нет механизма ownership топика между инстансами
|
|
|
|
## Архитектура
|
|
|
|
```
|
|
Zulip Event Queue
|
|
↓ poll (valera-bot credentials)
|
|
[zulip-router]
|
|
├── @Валера → http://hermes-valera:8765/zulip-webhook
|
|
└── @Клавдий → http://hermes-klavdiy:8765/zulip-webhook
|
|
↕ ownership: stream+topic → bot (TTL 24h, persisted to /data/ownership.json)
|
|
```
|
|
|
|
Hermes/Орёл — не трогаем, остаётся в polling-режиме.
|
|
|
|
## Правила маршрутизации
|
|
|
|
1. `@Валера` или `@Valera` в тексте → Валера (+ ownership обновляется)
|
|
2. `@Клавдий` или `@Klavdiy` в тексте → Клавдий (+ ownership обновляется)
|
|
3. Топик уже у кого-то → туда же (TTL refreshed)
|
|
4. Новый топик без @mention → Валера (default)
|
|
5. `reset` без @mention → уходит только к владельцу топика ✓
|
|
|
|
## Код
|
|
|
|
`~/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 к Hermes webhook endpoints |
|
|
|
|
## Deployment
|
|
|
|
**Compose:** `~/docker/hermes/docker-compose.yml` — сервис `zulip-router`
|
|
|
|
**Env vars** (`~/docker/hermes/.env`):
|
|
```
|
|
ZULIP_ROUTER_API_KEY=<valera-bot api key из ~/.hermes/profiles/valera/config.yaml>
|
|
ZULIP_WEBHOOK_TOKEN=zr-secret-2026
|
|
```
|
|
|
|
**Запуск после заполнения .env:**
|
|
```bash
|
|
cd ~/docker/hermes
|
|
docker compose up -d --force-recreate
|
|
```
|
|
|
|
**Перезапуск после изменений в профилях:**
|
|
```bash
|
|
docker compose restart hermes-valera hermes-klavdiy
|
|
```
|
|
|
|
## Hermes профили
|
|
|
|
Валера и Клавдий переведены в webhook-режим:
|
|
```yaml
|
|
platforms:
|
|
zulip:
|
|
webhook_port: 8765
|
|
webhook_path: /zulip-webhook
|
|
webhook_token: 'zr-secret-2026'
|
|
```
|
|
|
|
Токен должен совпадать с `ZULIP_WEBHOOK_TOKEN` в `.env`.
|
|
|
|
## TODO
|
|
|
|
- [ ] Заполнить ZULIP_ROUTER_API_KEY в ~/docker/hermes/.env
|
|
- [ ] `docker compose up -d --force-recreate`
|
|
- [ ] Проверить логи: `docker compose logs -f zulip-router`
|
|
- [ ] Протестировать: написать @Клавдий и @Валера в отдельных топиках
|
|
- [ ] Проверить ownership: обратиться в топик без @mention — должен отвечать последний бот
|
|
- [ ] При необходимости: добавить Hermes/Орёл в роутер (отдельная задача)
|