exostring 20135263c4 Add multi-route donor->recipient support, fix VK hashtag truncation
Posts can now be sourced from multiple VK groups, each routed to its own
Telegram/MAX destination(s) with independent on/off switches, configured
via data/routes.json (supports // line comments). Falls back to a single
route auto-generated from the legacy VK_SOURCE/TG_CHAT_ID/MAX_CHAT_ID env
vars if routes.json doesn't exist yet, so existing deployments keep working.

Routes are processed strictly sequentially within a cycle (no concurrency)
to keep flood control on VK/TG/MAX correct, since bot tokens are shared
across routes. DB schema gains route_id in the posts uniqueness key so the
same VK donor can safely feed multiple routes without status collisions.

Also removes the trailing-hashtag-stripping logic in text_formatter, which
was silently deleting VK posts' own hashtags whenever COMMON_TAGS wasn't
configured (it always wasn't) - posts are now forwarded unchanged.
2026-08-18 14:48:22 +05:00

RedAirsoft VK to Telegram & MAX Messenger Poster

Автоматизированный Docker-сервис для периодического парсинга новых постов из сообщества ВКонтакте (Red Airsoft | Страйкбол) и их одновременной публикации в Telegram и мессенджер МАКС (MAX Messenger) с отчётами администраторам.


📌 Инфраструктура и Деплой

Актуальный способ деплоя — Coolify, автодеплой по пушу в gitea/main. Старый ручной способ (checkout в /opt/redairsoft_poster/ + docker-compose руками через Proxmox, описанный ниже архивно) больше не используется — тот контейнер (redairsoft-vk-poster) был удалён 2026-08-15 как заброшенный.

Параметр Значение
Деплой Coolify, приложение "RedAirsoft Poster", сервер redairsoft-poster-lxc
URL http://hjbmqj1ohievdrebpltnzex0.192.168.1.106.sslip.io
Хост контейнера Тот же LXC 107 (192.168.1.106) — Coolify управляет им напрямую, не через /opt/redairsoft_poster/
Имя Docker-контейнера Динамическое, вида <coolify-app-id>-<deploy-id>, тег образа = хэш закоммиченного коммита. Смотреть через docker ps — актуальный это Up, с тегом = последний git log хэш
Gitea Репозиторий http://192.168.1.135:3000/exostring/redairsoft_vk_gt_max_sender
Хост Proxmox (для ручной диагностики) 192.168.1.222 (root SSH через ключ ~/.ssh/id_ed25519_proxmox)
Локальный Telegram Bot API Встроен в контейнер (http://127.0.0.1:8081), поддерживает загрузку видео до 2 ГБ
База данных SQLite /app/data/poster.db внутри контейнера (персистентный том)

Диагностика через SSH (read-only)

# Найти актуальный контейнер (смотреть на тег образа = свежий commit hash):
ssh -i ~/.ssh/id_ed25519_proxmox root@192.168.1.222 'pct exec 107 -- docker ps -a'

# Логи актуального контейнера:
ssh -i ~/.ssh/id_ed25519_proxmox root@192.168.1.222 'pct exec 107 -- docker logs --tail 100 <имя-контейнера>'

Деплой новой версии — просто git push в gitea/main, Coolify подхватывает автоматически.


⚙️ Как работает сервис

  1. Донор→рецепиент маршруты (data/routes.json):
    • Сервис может опрашивать сразу несколько групп ВК ("доноров"), и для каждой отдельно настроено, в какие Telegram и МАКС чаты публиковать ("рецепиенты"), плюс независимые вкл/выкл для TG и МАКС на каждом маршруте.
    • Список маршрутов хранится в data/routes.json (персистентный том, не в git). Если файла нет — при первом запуске он создаётся автоматически из старых .env-переменных VK_SOURCE/TG_CHAT_ID/MAX_CHAT_ID (один маршрут с id: "default"), так что апгрейд с однo-группового режима ничего не ломает.
    • Пример формата — routes.json.example в корне репозитория. Правка routes.json требует перезапуска контейнера, чтобы изменения подхватились.
    • Маршруты в рамках одного цикла опроса обрабатываются строго последовательно (без параллелизма) — это защищает от флуда на VK API, загрузке медиа в Telegram/локальный Bot API и отправке в МАКС, поскольку токены ботов общие на все маршруты.
  2. Опрос стены ВКонтакте:
    • Каждые 15 минут (CHECK_INTERVAL_MINUTES) делает запрос к методу wall.get для каждой группы из routes.json.
    • Фильтрует посты автора (без репостов и рекламы сторонних сообществ).
  3. Защита от спама и режим запуска (BOOTSTRAP_MODE):
    • Установлен режим BOOTSTRAP_MODE=skip_existing.
    • При первом запуске или перезапуске сервис запоминает текущие посты стены и ничего не публикует, пока в ВК не выйдет новый пост.
  4. Загрузка медиа и поддержка длинных видео:
    • Картинки скачиваются в максимальном разрешении.
    • Видео загружается через yt-dlp в качестве до 720p (длительностью до 2 часов и размером до 2000 МБ).
    • Встроенный в контейнер локальный telegram-bot-api загружает тяжелые видеофайлы напрямую в Telegram без ограничений облачного API (20-50 МБ).
  5. Форматирование текста:
    • Ссылки ВКонтакте вида [club123|Название] и [id123|Имя] преобразуются в корректные кликабельные HTML-ссылки.
    • Первый абзац / заголовок выделяется жирным шрифтом <b>...</b>.
    • Удаляются разделительные полосы (━━━, ───) и лишние переносы строк.
  6. Публикация в мессенджер МАКС:
    • Загрузка медиа в МАКС через /uploads?type=photo и /uploads?type=video.
    • Поллинг готовности видео (/videos/{token}) перед публикацией сообщения.
  7. Отчёты администраторам:
    • После успешной публикации или при ошибке бот отправляет отчёт со ссылками на оригинальный пост в ВК, Telegram и МАКС в личные сообщения администраторам (TG_ADMIN_IDS).
  8. Очистка диска и кэша:
    • Временные файлы удаляются сразу после публикации.
    • Фоновый воркер cleaner.py каждые 15 минут удаляет старые файлы из /tmp/poster_cache, гарантируя защиту активных скачиваний через реестр локов.

🔀 Маршруты донор→рецепиент (data/routes.json)

Формат одного маршрута:

{
  "id": "redairsoft_main",
  "name": "Red Airsoft (основная группа)",
  "vk_source": "public36860851",
  "tg_chat_id": "-1001303630155",
  "tg_enabled": true,
  "max_chat_id": "123456",
  "max_enabled": true
}
Поле Описание
id Уникальный технический идентификатор маршрута (используется в БД и логах)
name Человекочитаемое имя для отчётов администраторам
vk_source Группа-донор ВК: screen name, URL или owner_id
tg_chat_id / max_chat_id Куда публиковать (chat/channel ID)
tg_enabled / max_enabled Переключатель — публиковать ли в эту платформу для этого маршрута (только TG, только МАКС, или оба)

Можно завести несколько маршрутов с разными vk_source, каждый — в свои TG/МАКС чаты. Токены ботов (TG_BOT_TOKEN, MAX_BOT_TOKEN, VK_ACCESS_TOKEN) общие на все маршруты — один бот пишет в разные чаты.

vk_source принимает screen name, полный URL (vk.com и vk.ru) или owner_id — можно указывать как есть, без ручной нормализации.

Файл — обычный JSON, но допускает построчные комментарии // текст, чтобы подписывать, где какой маршрут (полноценных JSON-комментариев не существует, здесь это добавлено отдельно — строка, у которой после пробелов идёт //, вырезается перед парсингом):

[

// redairsoft
{
  "id": "redairsoft",
  "vk_source": "public36860851",
  "tg_chat_id": "-1001303630155",
  "max_chat_id": "-69722432869632"
},

// strike_expo
{
  "id": "strike_expo",
  "vk_source": "https://vk.ru/strike_expo",
  "tg_chat_id": "-1003099077190",
  "max_chat_id": "-77898705339648"
}

]

🔑 Конфигурация (.env)

Переменная Описание
VK_ACCESS_TOKEN Сервисный токен приложения ВК (d0a64...), общий на все маршруты
ROUTES_CONFIG_PATH Путь к файлу маршрутов, по умолчанию data/routes.json
VK_SOURCE / TG_CHAT_ID / MAX_CHAT_ID Легаси fallback: используются только для авто-генерации первого маршрута, если routes.json ещё не существует
TG_BOT_TOKEN Токен Telegram бота (от @BotFather), общий на все маршруты
TG_MEDIA_CHANNEL_ID Скрытый канал-хранилище (опционально)
TG_ADMIN_IDS Telegram ID админов через запятую (например 442509142)
LOCAL_BOT_API_URL http://127.0.0.1:8081 (встроен в контейнер)
MAX_BOT_TOKEN Токен бота в мессенджере МАКС, общий на все маршруты
BOOTSTRAP_MODE skip_existing
CHECK_INTERVAL_MINUTES 15
VIDEO_MAX_DURATION_SEC 7200 (2 часа)
VIDEO_MAX_SIZE_MB_LOCAL 2000 (2 ГБ)

📁 Структура исходного кода

├── src/
│   ├── config.py           # Настройки Pydantic Settings
│   ├── routes.py           # Загрузка/валидация data/routes.json (донор->рецепиент маршруты)
│   ├── database.py         # Хранилище SQLite (посты, статусы по route_id, даты)
│   ├── vk_client.py        # Клиент VK API (wall.get, извлечение медиа)
│   ├── media_processor.py  # Загрузка фото/видео (yt-dlp, aiohttp)
│   ├── text_formatter.py   # Конвертер ссылок ВК, очистка текста, HTML
│   ├── tg_poster.py        # Отправка в Telegram (Rich Message + Local API)
│   ├── max_poster.py       # Отправка в MAX Messenger (Uploads + Polling)
│   ├── cleaner.py          # Фоновая очистка временных файлов
│   ├── admin_notifier.py   # Агрегированные отчёты администраторам по всем маршрутам
│   └── main.py             # Точка входа, цикл опроса по всем маршрутам
├── Dockerfile              # Multi-stage образ: aiogram/telegram-bot-api + python:3.12-slim + ffmpeg
├── docker-compose.yml      # Конфигурация запуска сервиса
├── docker-entrypoint.sh    # Автозапуск локального Telegram Bot API + постера
├── requirements.txt        # Python зависимости (aiogram, yt-dlp, loguru, aiohttp, pydantic)
├── routes.json.example     # Пример формата data/routes.json
└── .env                    # Переменные окружения
S
Description
No description provided
Readme 382 KiB
Languages
Python 97.7%
Dockerfile 1.4%
Shell 0.9%