6.6 KiB
Why
У VESTI уже работает публикация в Telegram через изолированный publisher-service
(POST /api/v1/publish, Docker-контейнер :8410, каналы @dedinit_vesti). Но у проекта есть
вторая цель — «ленты ботов (Telegram, VK, fediverse)» (openspec/config.yaml, домен:
медиа-конвейер → банк статей → дайджесты + ленты ботов). Fediverse-нода — собственный
GoToSocial на social.dedinit.ru (bigbox, Docker-контейнер, :8082), аккаунт @vesti
(@vesti@dedinit.ru) — обычный пользователь (admin=0), создан и подтверждён.
Сейчас при approve статьи веб зовёт publisher-service, и тот умеет публиковать только в Telegram. Чтобы публиковать одобренные карточки ещё и в свой GoToSocial-аккаунт (от имени vesti), publisher-service нужно расширить fediverse-каналом.
GtS 0.22.x реализует Mastodon-совместимый REST API (проверено: POST /api/v1/apps
работает, возвращает client_id/client_secret; у ноды есть действующие OAuth-приложения
Phanpy/Tusky/GoToSocial Settings). Значит, публикация в GtS идентична Mastodon:
POST /api/v1/statuses с Bearer-токеном пользователя и опциональными медиа
(POST /api/v2/media). Это стандарт, библиотек не требует — хватит httpx (уже есть в
publisher).
What Changes
Расширяем publisher-service (FastAPI, :8410) мульти-канальной отправкой:
помимо Telegram-каналов (@dedinit_vesti) появляется fediverse-канал
@vesti@dedinit.ru — публикация в GoToSocial через Mastodon-совместимый API.
- Новый модуль
services/publisher/app/gotosocial.py— клиент GtS:post_status(text, media_paths, visibility="public")→POST /api/v1/statuses(текст ≤5000, лимит GtS; разметка: markdown или plaintext);upload_media(path)→POST /api/v2/media(multipart) до публикации, вернётmedia_id; до 6 вложений (лимит GtS);get_me()→ GET /api/v1/accounts/verify_credentials (healthz);delete_status(id)→ DELETE /api/v1/statuses/{id} (для отмены тестовых);- все GET запросы к GtS напрямую (без прокси — social.dedinit.ru доступен из контейнера, проверено HTTP 200).
- Конфиг (config.py + .env):
GT_SOCIAL_URL=https://social.dedinit.ru,GT_SOCIAL_ACCESS_TOKEN=<токен @vesti>(Bearer, scope write),GT_SOCIAL_VISIBILITY=public(MAY: private/unlisted). - main.py:
channels-список может содержать помимо@...(Telegram) ещё и fediverse-канал (например@vesti@dedinit.ruили префиксgt:); publisher умеет отправлять карточку в оба типа каналов, результаты — вresultsкак обычно. dry_run также эмулирует fediverse-публикацию (не уходит наружу). - healthz: добавить
gotosocialблок (url, аккаунт, токен задан?). - Документация: .env.example, README publisher, STATUS.md.
Не меняется
- Telegram-путь (telegram.py, каналы @dedinit_vesti) — работает как раньше.
- Веб :8400 (approve) — публикация по-прежнему через HTTP POST /api/v1/publish; веб-клиент не меняется (только конфиг каналов publisher).
- Формат карточки (текст+медиа) — один и тот же для всех каналов.
Capabilities
New Capabilities
gotosocial-publisher: публикация карточки VESTI в собственный GoToSocial (Mastodon API, Bearer-токен @vesti), медиа-аплоад, healthz, dry_run, delete.
Modified Capabilities
tg-publisher-service: publisher-service теперь мульти-канальный (Telegram + fediverse); конфиг каналов расширяется.vesti-web: без изменений кода (approve шлёт карточку publisher как раньше, но та уходит и в GtS если канал в конфиге).
Impact
- Затронутые сервисы/порты: publisher-service :8410 (Docker-контейнер vesti-publisher) — только пересборка образа; GtS :8082/8443 (social.dedinit.ru) — читается по HTTPS.
- Файлы:
- новый: services/publisher/app/gotosocial.py
- изменён: services/publisher/app/main.py, config.py, channels.py, services/publisher/Dockerfile (НЕ нужно — httpx уже есть), .env.example
- Данные: без миграций БД.
- Секреты: GT_SOCIAL_ACCESS_TOKEN (новый, в .env), URL GtS не секретен.
- Прокси: Telegram — как раньше через SOCKS5; GoToSocial — НАПРЯМУЮ (внутренняя сеть WG → vps02 → bigbox; из контейнера publisher social.dedinit.ru отвечает 200).
- Rollback: убрать канал
@vesti@dedinit.ruиз VESTI_BOT_CHANNELS (или вернуть конфиг) — Telegram-публикация не затронута; модуль gotosocial.py можно оставить невостребованным.
Risks
- Токен @vesti (scope write) — если протухнет, publisher вернёт 401/403 в results с
понятной ошибкой; healthz покажет
token_set=false. - GtS недоступен → HTTP 502 в результатах канала; остальные каналы продолжают работать (fan-out не падает целиком — уже реализовано в main.py).
- Лимиты GtS: 5000 символов, 6 вложений, MIME ограничен — текст обрезается до 5000, медиа до 6; неподдерживаемый тип → ошибка в результатах канала.
- Спам-защита GtS (rate limit) при частой публикации — допустимо (публикует человек, не автопостинг).