# re-publisher

Рабочий проект · Telegram → MAX

Беру новые сообщения из Telegram-папки MAX. На сервере пост проходит классификацию, фильтры и добавление источника. Подходящие материалы уходят в каналы MAX с исходным текстом и вложениями. В рабочем интерфейсе видны оценки моделей, OCR, лейблы, ошибки и история отправки.

Версия исходников: 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75

История репозитория: с 16 июня 2026. Текущий сценарий и снимки: 3 октября 2026.

## Задача

Хотел разбирать поток из нескольких каналов без ручного переноса каждого поста. Собрал конвейер, где можно посмотреть, почему материал отобран, что прочитал OCR и куда он отправился.

Моя рабочая область для тематических подборок. Публичный результат — каналы MAX; административный интерфейс закрыт паролем.

## Выполнено

- Сбор новых сообщений через Telethon, сохранение оригиналов, вложений и состояний обработки.
- Разметка корпуса вместе с Codex, локальное обучение TF-IDF и MiniLM, сохранение версий и истории прогонов.
- Конструктор фильтров, словарь лейблов, раздельные оценки текста и OCR.
- Отправитель MAX, проверка доставки, очередь, повторы, миграции и развёртывание.

## Что отличает реализацию

Оригинал не переписывается. Текст поста и надписи на медиа классифицируются отдельно. Фильтр проверяет оценки модели, а назначает обычный лейбл из моего словаря. Один пост может уйти в несколько каналов. Сохранённые ID сообщений и история попыток позволяют разбирать ошибки отправки.

## Состав прикладного кода

- Python content pipeline: 46.7%
- Python processing / CLI: 23.2%
- FastAPI / Jinja: 17.6%
- SQLAlchemy / PostgreSQL: 12.5%

Доли по размеру прикладных файлов Git в закреплённой версии. Каждый файл отнесён к одной подсистеме по правилам scripts/generate-stack-share.mjs; тесты, ассеты, зависимости и generated-файлы исключены. Округление — до 0,1%. Это состав кода, а не уровень владения технологиями.

## Стек

| Технология | Роль |
| --- | --- |
| Python / Telethon | Сбор из пользовательской Telegram-папки MAX |
| FastAPI / Jinja / JavaScript | Рабочий интерфейс, защищённые API и обновление доски |
| PostgreSQL / SQLAlchemy / Alembic | Оригиналы, задания, версии фильтров, лейблы, OCR и доставки |
| TF-IDF / scikit-learn | Основная автоматическая классификация |
| MiniLM / PyTorch | Обучение локально и ручное сравнение на сервере |
| RapidOCR / ONNX Runtime | Чтение надписей на медиа отдельным worker |
| MAX Bot API / httpx | Загрузка вложений, отправка и сверка сообщений |

## Архитектура

- Новые сообщения принимает collector в режиме new_only. Старый необработанный буфер не запускается автоматически.
- Отдельные процессы: web, collector, coordinator, TF-IDF, MiniLM, OCR и отправитель MAX. Очереди и результаты сохраняются в PostgreSQL.
- RapidOCR читает изображения и статические превью видео. Распознанный текст не дописывается в публикацию.
- Отсутствующая оценка остаётся неизвестной. Фильтры поддерживают категории, подкатегории, обязательные признаки, длину и вложенные И / ИЛИ / НЕ.
- Отправляется оригинальный пост с медиа и ссылкой «Источник». Отдельного блока ссылок из текста нет.

### Текущий конвейер: процессы, хранилище и MAX

![Текущий конвейер: процессы, хранилище и MAX](https://komaroff-dev.ru/evidence/publisher/20261003/architecture.svg)

[Mermaid](https://komaroff-dev.ru/evidence/publisher/20261003/architecture.mmd)

## Code-map

| Функция | Экран / API | Модуль | Данные / проверка |
| --- | --- | --- | --- |
| Сбор | Telegram MTProto | app/live_collector.py; app/collector.py | Граница new_only, курсор чата, оригиналы и медиа |
| Переходы стадий | /pipeline | app/pipeline_coordinator.py; app/content/source_marking.py | PipelineEntry, актуальность текста, лейблы и готовность |
| Модели | /api/pipeline/{id}/taxonomy | app/taxonomy/jobs.py; app/taxonomy/inference.py | Задания, полные оценки, версия, input_source, история |
| OCR | /api/pipeline/{id}/ocr | app/ocr/worker.py; app/ocr/engine.py; app/ocr/media.py | OCR run, строки, уверенность, кеш, хеши медиа |
| Фильтры и лейблы | /pipeline/filters; /marks | app/content/selection_rules.py; app/content/selection_filters.py | Дерево условий, версии правил, назначения и основания |
| MAX | /publication | app/publication/worker.py; app/publication/service.py | Маршруты, задания отправки, попытки, ID и сверка доставки |

Перед изменением пройти связи экран → API → состояние → хранилище. Проверить входы, ошибки и повторные действия. Обновить карту и схемы вместе с кодом, выполнить связанные проверки и привязать материалы к версии.

## Сценарии

- Посмотреть публичные каналы MAX и пример перепубликованного поста.
- С доступом владельца открыть доску: Не готовы → Отсортирован → Отфильтрован → Маркировка → На публикацию.
- Открыть карточку и выбрать источник результата: текст поста или OCR. Сравнить сохранённые прогоны.
- Открыть фильтр: проверить модель, условия, лейбл и совпавшие основания в предпросмотре.
- В истории MAX проверить канал, статус доставки, сохранённую ссылку и ошибки.

## Проверки

- GitHub CI закреплённой версии завершён успешно; ссылка ниже относится к 5f50077.
- При подготовке этой страницы открыты реальные обзор, доска, фильтр, карточки текста/OCR и история доставки.
- В рабочем интерфейсе первая партия 9 × 20 отмечена как подтверждённая. Доступны сохранённые ссылки на публикации.

[Tests and runtime build](https://github.com/komaroffsergei/re-publisher/actions/runs/37141555529) — 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75, 2026-10-03; версия демо.

## Результаты

- Девять каналов MAX подключены. Первая партия содержит 180 публикаций — по 20 на канал. Отправитель новых подходящих сообщений включён.
- В снимке истории есть последующие доставки. Число публикаций меняется по мере поступления сообщений.

## Ограничения

- Метки корпуса поставлены Codex. Показатели моделей измеряют согласие с этой разметкой, а не независимую человеческую точность.
- Маршруты для новичков и юмора экспериментальные. Для новичков test: 57/65 совпадений; для OCR-юмора validation: 92/104. Согласованные пороги качества не пройдены.
- OCR не понимает картинку без текста, не слушает звук и не просматривает всё видео. Ошибки чтения требуют разбора.
- Медиа больше 100 МБ и недоступные части альбома блокируют автоматическую готовность. В интерфейсе есть остановленные карточки.
- Счётчики и время на скриншотах — снимок состояния, а не обещание производительности.

## Запуск и эксплуатация

Тренировочные корпуса и секреты хранятся вне Git. На VPS передаются модельные артефакты и закреплённые Docker-образы; обучение выполняется на компьютере.

Настройки читаются в app/config.py. Telegram-сессия доступна collector, токен MAX — отправителю. В публичных материалах нет ключей и приватной переписки.

MAX_PUBLISHER_ENABLED управляет отдельным отправителем. Старые LLM, рерайт и публикация в Telegram не участвуют в текущем маршруте.

История попыток хранится в БД. Неопределённую доставку сначала сверяют, чтобы не отправить повтор.

## Скриншоты

[Обзор: стадии, живые циклы служб, очереди моделей и ошибки медиа. Снимок рабочего сервиса, 3 октября 2026.](https://komaroff-dev.ru/evidence/publisher/20261003/full/08-dashboard.webp) — 2026-10-03T18:32:34Z, 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75.

[Отправка в MAX включена. Здесь видны счётчики доставок и маршруты; значения относятся к моменту съёмки.](https://komaroff-dev.ru/evidence/publisher/20261003/full/01-publication.webp) — 2026-10-03T18:27:27Z, 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75.

[Пост из публичного Telegram-канала: оригинальное вложение, отдельная обработка и сохранённая доставка в MAX.](https://komaroff-dev.ru/evidence/publisher/20261003/full/02-card.webp) — 2026-10-03T18:28:52Z, 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75.

[Пустая подпись и текст на картинке: раскрытый OCR и отдельные оценки TF-IDF. В этом примере прогона MiniLM нет.](https://komaroff-dev.ru/evidence/publisher/20261003/full/03-ocr.webp) — 2026-10-03T18:28:53Z, 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75.

[Действующий фильтр «Смешное · OCR»: длина подписи, источник оценки OCR и пользовательский лейбл.](https://komaroff-dev.ru/evidence/publisher/20261003/full/05-filter-editor.webp) — 2026-10-03T18:48:06Z, 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75.

[Пять стадий доски. Выборка по NVIDIA и лейблу «Инструменты»; в кадре виден публичный материал.](https://komaroff-dev.ru/evidence/publisher/20261003/full/06-board.webp) — 2026-10-03T18:31:52Z, 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75.

[Карточка публичного поста: сохранённые прогоны TF-IDF по исходному тексту и доставки в несколько каналов.](https://komaroff-dev.ru/evidence/publisher/20261003/full/07-text-scores.webp) — 2026-10-03T18:49:05Z, 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75.

[Результат в MAX: исходный текст, вложение и небольшая ссылка «Источник». Кадр из опубликованного канала.](https://komaroff-dev.ru/evidence/publisher/20261003/full/09-max-source.webp) — 2026-10-03T18:08:28Z, 5f50077babfe5e8282c3a4312f9da8e6a9ba1b75.

[Материалы](https://komaroff-dev.ru/downloads/publisher-20261003.zip)
