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

4.3 KiB
Raw Blame History

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:

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:

ZULIP_ROUTER_API_KEY=<api-key бота-роутера>

Запуск:

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() патч из кода (больше не нужен)