Initial import: vesti.nixg.ru — новостной апрув-проект (web, crawler, classifier, publisher, openspec)

This commit is contained in:
kpa39l
2026-09-13 15:58:32 +00:00
commit c3f59f7b7a
113 changed files with 7065 additions and 0 deletions
@@ -0,0 +1,79 @@
# Spec: tg-publisher-service
## Purpose
Изолированный FastAPI-сервис публикации карточек в Telegram-каналы через Bot API.
Единая точка вызова для всех компонентов (веб, cron, будущие боты). Один бот-контроллер
публикует во все каналы, в которые добавлен администратором. Telegram доступен только
через SOCKS5-прокси (127.0.0.1:1080).
## ADDED Requirements
### Requirement: Изолированный сервис публикации
Сервис MUST быть отдельным FastAPI-приложением (контейнер), НЕ импортируемым модулем веба.
Он MUST предоставлять `GET /healthz` (статус, бот, прокси, каналы) и
`POST /api/v1/publish` (публикация карточки).
#### Scenario: Healthcheck
- **GIVEN** сервис запущен
- **THEN** `GET /healthz` возвращает 200 с `{"status":"ok","bot":...,"channels":[...]}`
и healthcheck в docker-compose (`curl -f`) проходит
#### Scenario: Публикация карточки
- **GIVEN** POST /api/v1/publish c `card: {text, direction, lang}` и `channels: ["@dedinit_vesti"]`
- **THEN** сервис отправляет сообщение через Bot API в каждый канал и возвращает
`{"ok":true,"results":{"@dedinit_vesti":{"message_id":<int>,"views":0}}}`
### Requirement: Один бот, несколько каналов (конфиг)
Сервис MUST поддерживать список каналов из конфигурации (`VESTI_BOT_CHANNELS`, через запятую).
`channels` в запросе MAY переопределять список. Если бот добавлен в канал администратором —
публикация MUST работать; иначе сервис MUST вернуть понятную ошибку (403).
#### Scenario: Несколько каналов
- **GIVEN** `VESTI_BOT_CHANNELS=@dedinit_vesti,@other_channel` (оба добавлены боту)
- **WHEN** POST /api/v1/publish без поля channels
- **THEN** карточка публикуется в оба канала; results содержит оба message_id
#### Scenario: Бот не админ канала
- **GIVEN** канал, в котором бот не администратор
- **WHEN** публикация в него
- **THEN** сервис возвращает 403 с текстом ошибки Bot API (sendMessage → Forbidden)
### Requirement: Прокси SOCKS5 для Bot API
Все запросы к api.telegram.org MUST идти через прокси из `TG_PROXY=socks5://127.0.0.1:1080`.
Если прокси недоступен — сервис MUST вернуть 502 (не падать).
#### Scenario: Прокси недоступен
- **GIVEN** прокси 127.0.0.1:1080 выключен
- **WHEN** POST /api/v1/publish
- **THEN** сервис возвращает 502 с ошибкой подключения (не 500, не краш)
### Requirement: Сбор views
Сервис MUST предоставлять способ получения просмотров для опубликованных сообщений
(через Bot API getMessage), чтобы веб показывал метрики.
#### Scenario: Views после публикации
- **GIVEN** сообщение опубликовано (message_id получен)
- **WHEN** запрос views для этого message_id
- **THEN** возвращается число просмотров (0 если ещё нет)
### Requirement: Режим подтверждения
Сервис MUST НЕ публиковать автоматически: вызывается только по HTTP-запросу (из веба
после клика «Опубликовать»). Автопостинга по таймеру внутри сервиса НЕТ.
#### Scenario: Нет автопостинга
- **GIVEN** сервис запущен без входящих запросов
- **THEN** ничего не публикуется (процесс только слушает HTTP)
## Modified Requirements (из tg-publisher)
- `publish_multi` и `get_views` переезжают в сервис (HTTP-интерфейс вместо импорта).
- vesti-web: approve вызывает POST /api/v1/publish; ответ используется для
distributed_dirs + views (как раньше, только источник данных — HTTP).
## NOT Requirements
- Не реализуем чтение каналов (краулинг) — это остаётся в tg-crawler (Telethon).
- Не реализуем веб-интерфейс сервиса (только API + healthz).
- Не храним БД в сервисе (вся персистентность — в vesti.db через веб).
- Не делаем автопостинг (см. Requirement выше).