uNews — единая система публикации новостей, патчноутов и отчётов разработки по проектам Антона.
Текущая версия: 0.3.9. Она не принимает служебные Markdown-файлы из news/ за патчноуты и поддерживает type: documentation как документационное обновление. Стабильная версия до автоматизации сохранена в ветке stable/manual-publishing-v0.1.0.
Главная идея: каждый проект хранит свои новости в папке news/, а uNews забирает эти патчноуты и публикует их в Telegram-канал через бота.
- публиковать текстовые сообщения в Telegram;
- публиковать пост с одной картинкой;
- публиковать альбом из нескольких картинок через
sendMediaGroup; - брать порядок картинок из YAML-поля
images; - использовать
imageкак запасной вариант для одной картинки; - проверять публикацию без отправки в Telegram;
- ограничивать подпись под Telegram-альбомом без изменения исходного
.md; - публиковать до 20 готовых записей за один запуск с паузой не менее 61 секунды;
- фиксировать
data/published.jsonсразу после каждого успешного Telegram-поста; - сохранять безопасную причину фатального сбоя в
data/health.jsonиdata/errors.json; - коммитить диагностическое состояние до окончательного завершения workflow с ошибкой;
- автоматически закрывать служебный Issue после восстановления очереди;
- скачивать каждое pending-изображение через GET вместо одной проверки HEAD;
- проверять сигнатуру, формат, размеры, PNG CRC и zlib-декодирование;
- повторно проверять файл непосредственно перед Telegram;
- передавать Telegram уже проверенные bytes как multipart Blob.
- автоматически создавать безопасную уменьшенную PNG-копию только для Telegram, сохраняя исходное изображение без изменений;
- игнорировать служебные Markdown-файлы в
news/, если имя не похоже на датированный патчноутYYYY-MM-DD-*.md.
500 Tower DefenseuSugaruDreamuChurchGOARTTime Rift- другие проекты автора
- В любом публичном репозитории
sunpoleсоздаётся папкаnews/. - В неё добавляются Markdown-патчноут и изображение с полями
versionиqueued_at. - uNews просыпается раз в четыре часа или вручную.
- Каждый pending-файл скачивается через GET и проходит byte-level image integrity проверку.
- Внутри проекта более ранняя версия всегда идёт первой.
- Среди проектов выбирается самая старая запись по
queued_at. - Непосредственно перед Telegram выбранное изображение скачивается и проверяется повторно.
- Telegram получает проверенный Blob, а не исходный raw URL.
- За запуск публикуется до 20 готовых Telegram-постов в строгом FIFO-порядке с паузой 61 секунду.
- Каждый успешный результат немедленно записывается и отправляется в GitHub через
data/published.json. - После завершения запуска обновляются
data/health.jsonиdata/errors.json; при фатальном сбое state коммитится до возврата ошибки.
Приватные репозитории не сканируются. Полная схема: docs/QUEUE_ARCHITECTURE.md. Точный контракт изображений: docs/IMAGE_INTEGRITY.md. Нормализация длинных PNG: docs/TELEGRAM_PHOTO_NORMALIZATION.md.
news/
├── 2026-06-14-500td-v1-0-2-pages-preview.md
├── 2026-06-14-500td-v1-0-2-pages-preview.png
└── 2026-06-14-500td-v1-0-2-pages-preview_2.png
type: patch
project: 500 Tower Defense
series: 500td
title: Версия 1.0.2 подготовлена для веб-запуска
version: 1.0.2
queued_at: 2026-07-18T15:40:00Z
repo_url: https://github.com/sunpole/500
web_url: https://sunpole.github.io/500/previews/500td/1.0.2/
image: 2026-06-14-500td-v1-0-2-pages-preview.png
images:
- 2026-06-14-500td-v1-0-2-pages-preview_2.png
- 2026-06-14-500td-v1-0-2-pages-preview.pngtype— тип публикации:intro,patch,report,note,docs,documentation,feature,bugfix,release.project— название проекта.series— короткий ключ серии, например500td,usugar,udream.title— заголовок публикации.version— версия, если она есть.queued_at— точное UTC-время постановки в очередь в ISO 8601.repo_url— ссылка на GitHub-репозиторий.web_url— ссылка на рабочую веб-версию или preview.image— одна картинка или fallback.images— список картинок для Telegram-альбома.
Если указано images, порядок картинок берётся строго из YAML. Первая картинка получает подпись, остальные отправляются без подписи.
Внутри патчноута можно добавить блок:
Короткий текст для Telegram:
Текст после этого блока будет использован как короткая подпись к Telegram-публикации. Это удобно, если полный патчноут длинный.
Проверка без отправки:
npm run publish:projects:check -- "../500_td_game/news/2026-06-14-500td-v1-0-2-pages-preview.md"
npm run publish:all:check
npm run diagnose:telegram
npm run check:images
npm testОжидаемый результат для альбома:
{
"method": "sendMediaGroup",
"captionWasTruncated": false
}Основной путь реальной публикации — только GitHub Actions. Проект-источник кладёт патчноут и изображение в публичную папку news/, после чего workflow Publish all project news в uNews находит новый файл, проверяет правила публикации, отправляет пост в @uNewsLog и обновляет data/published.json.
Локально разрешены только безопасные команды:
npm run publish:projects:check -- "../500_td_game/news/2026-06-14-500td-v1-0-2-pages-preview.md"
npm run publish:all:check
npm run diagnose:telegram
npm run check:fixtures
npm run check:images
npm testКоманды npm run publish:projects и npm run publish:all по умолчанию блокируют реальную отправку с локального компьютера. Они должны отправлять Telegram-посты только внутри GitHub Actions, где GITHUB_ACTIONS=true.
Локальные секреты должны храниться только в .env. Этот файл нельзя добавлять в GitHub.
Финальная подпись Telegram собирается автоматически через policy-слой. Текст из блока Короткий текст для Telegram не публикуется “как есть”: к нему добавляются обязательные ссылка и хештеги.
Правило ссылки:
- если есть
web_url, используется он; - если
web_urlнет, используетсяrepo_url; - если указан
branchи нетweb_url, формируется ссылка на GitHub-ветку; - если нет ни
web_url, ниrepo_url, публикация блокируется.
Обязательные хештеги:
uSugar→#uSugar #тыСахар #uNews #SunpoleuNews→#uNews #тыНовости #SunpoleuDream→#uDream #тыСон #uNews #SunpoleuChurch→#uChurch #тыЦерковь #uNews #Sunpole500 Tower Defense→#500TD #500ТД #uNews #Sunpole
Если для проекта нет mapping, check падает и mapping нужно добавить до публикации.
Для type: patch, docs, documentation, feature, bugfix и release финальная подпись обязательно содержит слово “патч”, “обновление”, “релиз” или “документационное обновление”. Если автор забыл это в коротком тексте, policy добавляет компактную вводную фразу автоматически.
Публикация блокируется, если в патчноуте есть подозрение на секреты, .env, token-like строки, TELEGRAM_BOT_TOKEN, DEEPSEEK_API_KEY. Для uSugar дополнительно блокируются приватные Telegram identifiers, ngrok-ссылки и явные glucose-like медицинские значения.
Перед реальной публикацией можно безопасно проверить Telegram-настройки:
npm run diagnose:telegramДиагностика показывает только наличие переменных, результат getMe, целевой канал и результат getChat. Она не печатает токен целиком или частично.
Ожидаемые локальные переменные:
TELEGRAM_BOT_TOKEN=...
TELEGRAM_CHANNEL_ID=@uNewsLog
BOT_USERNAME=@uNewsDev_botЕсли публикация падает с Telegram sendPhoto failed: Unauthorized или диагностика показывает bot getMe: FAILED (401), проблема в TELEGRAM_BOT_TOKEN: токен отсутствует, отозван, введён неверно или не относится к нужному Telegram-боту. В этом случае нужно заменить TELEGRAM_BOT_TOKEN в локальном .env на актуальный токен @uNewsDev_bot.
Если локально публикация работает, а GitHub Actions падает, проверьте repository secrets для sunpole/uNews:
TELEGRAM_BOT_TOKENTELEGRAM_CHANNEL_ID
Значения секретов нельзя публиковать в README, логах, issue, pull request или патчноутах.