# PRD — VESTI: новостной агрегатор с веб-интерфейсом > **Правило проекта (пользователь, 2026-09-09): все задачи выполняются через OpenSpec** — > каждый элемент/изменение оформляется как change (proposal, design, specs, tasks), > проверяется `openspec validate`. Изменения фиксировать в STATUS.md / TODO.md / > WALKTHROUGH.md / PRD.md. - Статус: v0.2 (2026-09-10) — прототип работает, реальная публикация включена - Расположение: /opt/vesti - Эволюция: /opt/news (наследие — код, схема, sources.yaml, Telethon-сессия) - Репозитории: vesti-* (источник истины gitverse.ru, зеркало gitea bigbox:3000) ## 1. Цель Построить собственный новостной конвейер «СМИ»: собирать новости из многих источников (RSS, почтовые рассылки, сайты, Telegram, WeChat, X), дедуплицировать и объединять в одну статью со ссылками на все источники (юридическая защита), проверять факты, дополнять мультимедиа, категоризировать по направлениям, и формировать: 1. **Дайджесты по направлениям** — для еженедельных видео (учитывают популярность тем, реакции, мои оценки интересности). 2. **Ленты новостных ботов** — Telegram, VK, fediverse по направлениям. Всё управление — через веб-сайт с авторизацией: списки внешних новостей с метриками популярности, списки опубликованных со своими метриками, редактирование прямо в интерфейсе. Собственный контент (канал @dedinit «Дед в АйТи» ~1092 поста) — отдельный класс: is_own=1/is_own_canonical=1, приоритетная классификация (critical без LLM при словарном попадании), fan-out по направлениям. ## 2. Направления (категории) Технологии, Политика, Игры, Электроника, БЯМ (большие языковые модели), Линукс. ## 3. Языки ботов Отдельные боты под комбинации направление×язык: ru, en, zh, ko. Именование: @dedinit_vesti_<направление>_<язык>_bot. Прототип (текущий): **бот-контроллер @dedinit_controller_bot публикует в канал @dedinit_vesti** (один бот → много каналов через добавление админом; каналы из VESTI_BOT_CHANNELS). Отдельные боты направлений — фаза 2. ## 4. Принципы - **Каноническая БД — PostgreSQL** (решение D1; для прототипа допустим SQLite, миграция на Postgres на фазе 2). - **Банк статей — markdown-бандлы с frontmatter** + мультимедиа рядом: `bundles/<направление>//.md` + `media//`. - **Всё локально** на bigbox; массовая классификация — Ollama qwen3:8b-nothink; облачный LLM (deepseek, отдельный API/ключ — учёт затрат проекта) — факт-чек, слияние, дайджесты (решение E2). - **n8n — оркестратор** для RSS/email/сайтов (отдельный контейнер, общий для инфраструктуры), НЕ для Telegram (Bot API не читает чужие каналы — только MTProto/Telethon). - **Публикация ботов — режим «черновик на подтверждение»** (решение C2-b): сначала подтверждение человеком, потом смягчение. - **Переиспользуем существующий стек**: Qdrant (docker-qdrant-1, :6333), Redis (:6379), Ollama (:11434), telegram-tunnel (SOCKS5 :1080 → VPS01), Garage S3 (медиа, фаза 2), GoToSocial (fediverse-бот, фаза 2), gitea (:3000, зеркало) / gitverse.ru (истина). - Тихие watchdogs: молчат, пока всё живо (политика пользователя). ## 5. Архитектура (текущая, 2026-09-10) ``` sources.yaml (реестр, git) │ ▼ telegram_crawler.py (cron каждые 30 мин, Telethon MTProto, SOCKS5 :1080) │ инкрементально после last_post_id; метрики views/reactions; --all / --source ▼ backfill_dedinit.py (разовый бэкфилл своего канала ~1092 постов, is_own=1, медиа) │ ▼ SQLite vesti.db (raw-посты, tg_state, runs, классификации) ──► Postgres (фаза 2) │ ▼ classifier.py (Ollama qwen3:8b-nothink + словарный фильтр) │ направление, relevance, interest 1-5, summary; мультинаправления ▼ vesti-web (FastAPI + Bootstrap 5.3 + Jinja2 + HTMX, 127.0.0.1:8400) │ авторизация; кандидаты → подтвердить/отклонить; «Свои»; метрики ▼ (подтверждённые) publisher-service (FastAPI-микросервис, Docker vesti-publisher, :8410) │ POST /api/v1/publish → Bot API через SOCKS5 :1080 (host.docker.internal) │ медиа: маунт ../../media/media:/srv/publisher/media:ro (веб шлёт media/) ▼ Telegram @dedinit_vesti (бот-контроллер @dedinit_controller_bot; каналы из VESTI_BOT_CHANNELS) │ ▼ bundles/<направление>//.md (markdown + frontmatter) + media/ ``` Сервисы/порты (актуально): | Сервис | Порт | Способ запуска | |---|---|---| | vesti-web | 127.0.0.1:8400 | systemd-юнит vesti-web.service (установлен; daemon-reload на bigbox зависает — внешняя проблема, подхватится при перезагрузке). Пароль: VESTI_WEB_PASSWORD or ADMIN_PASSWORD из .env | | publisher-service | 127.0.0.1:8410 | Docker (docker-compose, restart policy), healthz | | telegram-tunnel | SOCKS5 127.0.0.1:1080 | systemd telegram-tunnel.service | | Ollama | 127.0.0.1:11434 | существующий (qwen3:8b-nothink) | Cron (Hermes cron, no_agent, тихие): - vesti-crawler-all-sources (`*/30 * * * *`) — краулер по всем включённым источникам, лог /opt/vesti/logs/crawler-cron.log; молчит при успехе. - vesti-watchdog (`*/15 * * * *`) — publisher :8410 healthz, веб :8400, SOCKS5 :1080, контейнер, БД; шумит в Telegram только при проблеме. Фаза 2+: n8n (RSS/email/сайты), Postgres, S3 (Garage), семантический dedup (Qdrant bge-m3), факт-чек облаком, дайджесты для видео, боты VK/fediverse, WeChat/X. ## 6. Метрики популярности - Внешние TG-посты: views + reactions (через Telethon при краулинге). - Свои посты: views через Bot API (GET /api/v1/views/{channel}/{mid}). - Сайты/RSS (фаза 2): только свои переходы (UTM-метки в ссылках). - Интерес темы: кол-во реакций/просмотров → показатель хайповости; залайканные комментарии — сигнал (фаза 2). ## 7. Дайджесты (G1, G3) Отдельная сущность по направлению: по каждой теме — суть, источники, оценка популярности. Для еженедельного видео: 5–10 новостных поводов в выпуске, выходные. Оценки интересности — в вебе (1–5 + флаг «в дайджест»). ## 8. Технологический стек | Слой | Технология | Статус | |---|---|---| | Язык | Python 3.12, venv /opt/vesti/.venv | новый | | Telegram-краулер | Telethon (MTProto), SOCKS5 :1080, сессия /opt/vesti/telegram/ | новый (переисп. код /opt/news) | | Бот-публикатор | publisher-service: httpx[socks] → Bot API (не python-telegram-bot) | новый | | Каноническая БД | SQLite (прототип) → PostgreSQL (фаза 2) | новый | | LLM-классификация | Ollama qwen3:8b-nothink (:11434) | существующий | | Облачный LLM | deepseek (отдельный API/ключ) | фаза 2 | | Векторный поиск | Qdrant :6333 (коллекция news), bge-m3 | существующий | | Веб | FastAPI + Bootstrap 5.3 + Jinja2 + HTMX | новый, :8400 | | Оркестратор | n8n (отдельный контейнер, :5678), общий | фаза 2 | | Медиа | локально /opt/vesti/media/media/ → Garage S3 | фаза 2 | | Git | gitverse.ru (истина), gitea bigbox :3000 (зеркало) | существующий | | Доставка | Telegram-боты, потом VK/fediverse | новый | ## 9. OpenSpec Проект ведётся по OpenSpec: /opt/vesti/openspec/. Active changes: - `tg-crawler-publisher-prototype` (прототип конвейера; реализован), - `own-content-hub` (свой канал @dedinit, fan-out; код готов, бэкфилл идёт, интеграционная проверка после него), - `publisher-service` (микросервис публикации; создан 2026-09-09, validate чист, реальная публикация работает). Файлы: proposal.md, design.md, specs/*/spec.md, tasks.md. ## 10. Прототип — текущее состояние Вертикаль Линукс (ru) + свой контент: 1. TG-краулер 8 публичных каналов (Telethon, SOCKS5, инкрементальный, метрики; форварды из своих каналов отфильтровываются, чужие — с fwd-полями без медиа). 2. Классификатор (словари + локальный qwen3:8b-nothink; мультинаправления; свои посты → critical без LLM при словарном попадании). 3. Реальная публикация: бот-контроллер @dedinit_controller_bot → @dedinit_vesti (проверено: message_id 2–4, approve через веб работает). 4. Веб (127.0.0.1:8400): кандидаты → подтвердить/отклонить, «Свои», метрики, опубликованные. 5. Банк статей markdown с frontmatter (bundles/, origin:own для своих). 6. Бэкфилл канала @dedinit (~1092 постов is_own=1, медиа) — скрипт crawler/backfill_dedinit.py, запущен 2026-09-10. Источники: linuxklub, linuxos_tg, dotfiles_linux, linux_education, LinuxMastery, linuxcamp_tg, gitgate, krxnotes + канал dedinit (own). ## 11. Критерии готовности прототипа (DoD) 1. Краулер собирает посты из линукс-каналов с метриками, без дублей (проверка: count по sha256 = count id). — ДОСТИГНУТО 2. Классификатор корректно относит ≥90% контрольных постов к направлению. — В ПРОВЕРКЕ (после бэкфилла — прогон по 543+ неклассифицированным) 3. Бот публикует карточку-пост только после подтверждения в вебе. — ДОСТИГНУТО 4. Веб доступен локально, с авторизацией; кандидаты/опубликованные/метрики видны. — ДОСТИГНУТО 5. Бандл создан в bundles/linux/YYYY-MM/.md с frontmatter и источниками. — ДОСТИГНУТО 6. Watchdog молчит 3 дня подряд при здоровой системе. — В ПРОВЕРКЕ (cron создан 2026-09-10) 7. Смягчение публикации (C2: b → a/в) — отдельное решение после прототипа. ## 12. Открытые вопросы / следующие фазы - Миграция SQLite → Postgres (когда поток статей вырастет). - Облачный LLM API для факт-чека/слияния/дайджестов — отдельный ключ/учёт затрат. - n8n-воркфлоу для RSS/email/сайтов (общий контейнер). - Боты VK, fediverse (GoToSocial) — фаза 2; отдельные боты направлений (@dedinit_vesti___bot) — фаза 2. - WeChat (закрытый, сложный) и X (платный API) — исследование отдельно. - Автопубликация/смягчение условий (после прототипа). - Дайджесты: сущность, связь с видео-сценарием. - git-репозиторий /opt/vesti (gitverse истина, gitea зеркало) — инициализировать. - openspec archive publisher-service / tg-crawler-publisher-prototype после подтверждения и интеграционных проверок.