diff --git a/personal/projects/zulip-router.md b/personal/projects/zulip-router.md index e34fada3..e482cfaf 100644 --- a/personal/projects/zulip-router.md +++ b/personal/projects/zulip-router.md @@ -27,22 +27,77 @@ Zulip ``` Zulip Event Queue ↓ poll (credentials Eagle из ~/.hermes/config.yaml) -[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) +[zulip-router] + │ + ├── @mention бота от человека → POST owner-боту + ownership + ├── @mention любого бота (включая другого) → ownership, НЕ форвардить + ├── трейд с ownership → POST owner-боту + ├── новый тред без @mention → default-боту (из конфига) + └── reset / system → только владельцу треда ``` **Ключевое:** credentials — Eagle/Орла (не новый бот). Роутер регистрирует свою event queue (отдельную от очереди Eagle). -## Правила маршрутизации +## Правила маршрутизации (пошагово) -1. `@Валера` или `@Valera` в тексте → Валера (+ ownership обновляется) -2. `@Клавдий` или `@Klavdiy` в тексте → Клавдий (+ ownership обновляется) -3. Топик уже у кого-то → туда же (TTL refreshed) -4. Новый топик без @mention → Валера (default) -5. `reset` без @mention → только владельцу топика +Для каждого сообщения из event queue: + +1. **Извлечь @mention** — найти все `@**Name**` в тексте (Zulip-формат). Игнорировать `@**all**` и `@**everyone**`. + +2. **Проверить источник @mention:** + - Если сообщение от **человека** (sender_id не из списка ботов) и содержит `@**<бот>**`: + - Записать этого бота как владельца треда (stream+topic) + - FORWARD сообщение этому боту + - Если сообщение от **человека** и содержит @mention **другого бота** (из конфига, но не того, что стал бы овнером): + - Всё равно записать этого бота как владельца треда + - НЕ форвардить (Zulip сам отправит webhook целевому боту по @mention) + - Если сообщение от **бота** (из конфига) и содержит @mention другого бота: + - Обновить ownership, но НЕ форвардить (Zulip сам доставит) + +3. **Нет @mention, но тред уже закреплён за ботом:** + - FORWARD владельцу треда + +4. **Новый тред без @mention:** + - FORWARD default-боту (из конфига) + +5. **reset / system-команды без @mention:** + - FORWARD только владельцу треда + +### Важный нюанс: @mention от другого бота + +Когда один бот пишет `@Валера` (например, Eagle), Zulip отправляет outgoing webhook Валере напрямую — роутер не должен дублировать это сообщение. Поэтому: + +- Роутер **ставит ownership**, но **не форвардит** если в сообщении есть @mention любого бота из его конфига +- Целевой бот получит сообщение через свой собственный outgoing webhook от Zulip + +## Конфиг роутера (`~/Docker/zulip-router/config.yaml`) + +```yaml +zulip: + bot_email: "eagle-bot@zulip.qentra.top" + api_key: "${ZULIP_API_KEY}" + server_url: "https://zulip.qentra.top" + +bots: + - name: "Валера" + aliases: ["Valera"] + webhook: "http://balda-agent-valera-1:8091/zulip/webhook" + - name: "Клавдий" + aliases: ["Klavdiy"] + webhook: "http://claudio-agent-claudio-1:8092/zulip/webhook" + +# bot_ids — sender_id ботов в Zulip (чтобы отличать сообщения человека от бота) +bot_ids: + - 8 # Eagle / Орёл (Hermes) + - 9 # Валера + - 10 # Клавдий + +default_bot: "Валера" + +ownership: + ttl: 24h + persist_path: "/data/ownership.json" +``` ## Что меняется в balda @@ -54,7 +109,7 @@ zulip: enabled: false ``` -Сообщения приходят **только через webhook** от роутера. +Сообщения приходят **только через webhook** от роутера (плюс outgoing webhook от Zulip при @mention напрямую). ## Код @@ -74,6 +129,8 @@ zulip: **Compose:** `~/Docker/zulip-router/docker-compose.yaml` +**Сеть:** `balda_default` (external) — чтобы видеть balda-agent-valera-1 и claudio-agent-claudio-1 по Docker DNS. + ### Credentials: Eagle, не новый бот Роутер использует credentials Eagle/Орла из `~/.hermes/config.yaml` → `platforms.zulip`. @@ -86,12 +143,7 @@ zulip: ```env # Credentials Eagle/Орла — из ~/.hermes/config.yaml -ZULIP_BOT_EMAIL=eagle-bot@zulip.qentra.top ZULIP_API_KEY=<скопировать из конфига Eagle> - -# Куда форвардить (Docker DNS, сеть balda_default) -VALERA_WEBHOOK=http://balda-agent-valera-1:8091/zulip/webhook -CLAUDIO_WEBHOOK=http://claudio-agent-claudio-1:8092/zulip/webhook ``` ### Запуск @@ -109,6 +161,7 @@ docker logs zulip-router --tail 20 # проверить poll loop - [ ] Набить `.env` с credentials Eagle (из `~/.hermes/config.yaml`) - [ ] `docker-compose up -d` - [ ] Проверить логи: `docker logs zulip-router --tail 50` -- [ ] Протестировать: @Клавдий и @Валера в разных топиках +- [ ] Протестировать: @Клавдий и @Валера в разных топиках (человеком) - [ ] Проверить ownership: сообщение в топик без @mention → последний бот +- [ ] Проверить @mention бота от другого бота → ownership ставится, дубляж нет - [ ] Удалить `extractOtherMention()` патч из кода (больше не нужен)