From 251fa0be11fafa4982166ab25f5446fee56beecd Mon Sep 17 00:00:00 2001 From: gitadmin Date: Sun, 30 Aug 2026 10:08:43 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B5=D1=81=D1=82=D1=80=D1=83=D0=BA?= =?UTF-8?q?=D1=82=D1=83=D1=80=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20=D1=84?= =?UTF-8?q?=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/rules/version-and-docs.mdc | 23 ++ .gitignore | 1 - README.md | 68 +++++- TELEGRAM_MESSAGING.md | 206 +----------------- VERSION | 1 + docs/ARCHITECTURE.md | 153 +++++++++++++ docs/CHANGELOG.md | 37 ++++ docs/runner-setup.md | 75 +++++++ docs/telegram-messaging.md | 94 ++++++++ docs/volk-mode.md | 53 +++++ genkey.py | 2 - runner.sh | 3 +- services/RUNNER_SETUP.md | 88 +------- tools/README.md | 41 ++++ .../dev/send_telegram_example.py | 16 +- .../dev/volk_category_testing.py | 11 +- .../dev/volk_telegram_testing.py | 11 +- .../legacy/evt_prefetch.py | 40 ++-- .../ops/copy_databases.sh | 6 +- tools/ops/genkey.py | 10 + .../ops/register_tg_webhook.py | 13 +- 21 files changed, 619 insertions(+), 333 deletions(-) create mode 100644 .cursor/rules/version-and-docs.mdc create mode 100644 VERSION create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/CHANGELOG.md create mode 100644 docs/runner-setup.md create mode 100644 docs/telegram-messaging.md create mode 100644 docs/volk-mode.md delete mode 100644 genkey.py create mode 100644 tools/README.md rename send_telegram_example.py => tools/dev/send_telegram_example.py (83%) rename volk_category_testing.py => tools/dev/volk_category_testing.py (97%) rename volk_telegram_testing.py => tools/dev/volk_telegram_testing.py (99%) rename evt_prefetch.py => tools/legacy/evt_prefetch.py (72%) rename copy_databases.sh => tools/ops/copy_databases.sh (94%) create mode 100644 tools/ops/genkey.py rename register_tg_webhook.py => tools/ops/register_tg_webhook.py (88%) diff --git a/.cursor/rules/version-and-docs.mdc b/.cursor/rules/version-and-docs.mdc new file mode 100644 index 0000000..deb92db --- /dev/null +++ b/.cursor/rules/version-and-docs.mdc @@ -0,0 +1,23 @@ +--- +description: Поддерживать VERSION и docs/CHANGELOG при изменениях проекта +alwaysApply: true +--- + +# Версионирование и документация + +При **значимых** изменениях кода или поведения: + +1. Обновить [`VERSION`](../VERSION) (SemVer): + - **PATCH** — исправления, мелкие правки без смены API/поведения + - **MINOR** — новая функциональность, обратно совместимая + - **MAJOR** — ломающие изменения (формат ссылок, схема БД, API) + +2. Добавить запись в [`docs/CHANGELOG.md`](../docs/CHANGELOG.md) под новой версией (Added / Changed / Fixed / Removed). + +3. При изменении архитектуры, пайплайна, режимов ZILANT/VOLK или карты файлов — обновить [`docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md) и при необходимости [`README.md`](../README.md), [`docs/volk-mode.md`](../docs/volk-mode.md). + +4. Перенос скриптов в `tools/` — обновить [`tools/README.md`](../tools/README.md). + +5. Не поднимать версию за косметические правки без изменения поведения. + +Текущая базовая версия: **1.0.1**. diff --git a/.gitignore b/.gitignore index b8fb7ea..6e4266f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ __pycache__/ *.py[cod] *$py.class -__pycache__/season_links.cpython-314.pyc diff --git a/README.md b/README.md index 211c717..93aa6df 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,66 @@ -# Zilant2025 +# Zilant / VOLK — Telegram poster и подписки -Zilantkon bot testarea -test update #8 \ No newline at end of file +**Версия:** 1.0.1 ([`VERSION`](VERSION)) + +Автоматическая публикация постов и событий в Telegram-канал, веб-редактор, бот подписок с сезонными deep link. + +## Режимы + +| `WORKMODE` | Описание | +|------------|----------| +| `ZILANT` | VK + Zilant JSON → канал (основной пайплайн) | +| `VOLK` | VOLK API → категории и события → канал | + +Подробнее: [`docs/volk-mode.md`](docs/volk-mode.md). + +## Быстрый старт (сервер) + +```bash +# Deploy через Woodpecker → rsync + post-deploy.sh +# Сервисы: +sudo systemctl status testbot-git.service # бот :5005 +sudo systemctl status testedit-git.service # UI :5006 +sudo systemctl status runner.timer # hourly pipeline +``` + +Конфигурация — `.env` (не коммитится). Ключевые переменные: `RELAY_URL`, `WEBHOOK_URL`, `SEASON`, `MDB_*`. + +## Пайплайн (ZILANT) + +`runner.sh` по расписанию: + +`vk_load` → `zk_load` → `db_update_shortname` → `tg_publish` → `evtg_publish` + +Настройка timer: [`docs/runner-setup.md`](docs/runner-setup.md). + +## Документация + +| Документ | О чём | +|----------|--------| +| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Компоненты, карта файлов, deploy | +| [`docs/volk-mode.md`](docs/volk-mode.md) | Режим VOLK | +| [`docs/telegram-messaging.md`](docs/telegram-messaging.md) | API отправки сообщений пользователям | +| [`docs/runner-setup.md`](docs/runner-setup.md) | Systemd timer / cron для runner | +| [`docs/CHANGELOG.md`](docs/CHANGELOG.md) | История версий | +| [`tools/README.md`](tools/README.md) | Вспомогательные и legacy-скрипты | + +## Версионирование + +SemVer в [`VERSION`](VERSION). При изменениях обновлять `VERSION` и [`docs/CHANGELOG.md`](docs/CHANGELOG.md). + +## Структура каталогов + +``` +├── tg_mainbot.py, wsgi_bot.py # responder-бот +├── db_edit.py, wsgi_edit.py # веб-UI +├── tg_publish.py, evtg_publish.py # публикация (ZILANT) +├── volk_load.py, volk_cat_publish.py # VOLK +├── runner.sh, backupdb.sh +├── services/ # systemd units, post-deploy +├── templates/ # HTML UI +├── docs/ # документация +└── tools/ + ├── ops/ # genkey, register_tg_webhook, copy_databases + ├── dev/ # send_telegram_example, volk_*_testing + └── legacy/ # evt_prefetch +``` diff --git a/TELEGRAM_MESSAGING.md b/TELEGRAM_MESSAGING.md index 02202ae..d292c57 100644 --- a/TELEGRAM_MESSAGING.md +++ b/TELEGRAM_MESSAGING.md @@ -1,205 +1,3 @@ -# Отправка сообщений через Telegram бота +# Перенесено -## Описание - -В проект добавлена возможность отправки сообщений пользователям Telegram через бота по их ID. Функциональность включает: - -1. **API endpoint** для отправки сообщений из других скриптов -2. **Интерфейс в веб-приложении** для отправки сообщений через браузер -3. **Обработку ошибок** и логирование всех операций - -## Архитектура системы - -Система состоит из двух основных компонентов: - -- **Веб-интерфейс** (`db_edit.py`) - работает на порту **5006** (WEBCTRL_PORT) -- **Telegram бот** (`tg_mainbot.py`) - работает на порту **5005** (WEBHOOK_PORT) - -API endpoint `/send_message` находится в Telegram боте, поэтому все запросы должны отправляться на порт **5005**. - -### Конфигурация портов - -Порты и URL настраиваются в файле `.env`: -- `RELAY_URL` - базовый URL **internal_relay** (все исходящие вызовы Bot API идут только через него) -- `WEBHOOK_PORT` - порт для Telegram бота (по умолчанию 5005) -- `WEBHOOK_URL` - URL доставки апдейтов от internal_relay к боту (доступен с машины relay; не URL, который видит Telegram напрямую) -- `WEBCTRL_PORT` - порт для веб-интерфейса (по умолчанию 5006) - -Веб-интерфейс автоматически читает URL и порт бота из переменных окружения и использует их для отправки запросов. - -## Использование через веб-интерфейс - -В заголовке страницы "Управление постами VK и событиями" добавлены два поля: - -- **ID пользователя** - числовой ID пользователя в Telegram -- **Сообщение** - текст сообщения для отправки -- **Кнопка "Отправить"** - отправляет сообщение через бота - -### Особенности интерфейса: - -- Поля проверяются на заполненность перед отправкой -- ID пользователя проверяется на корректность (должен быть числом) -- Поддержка отправки по клавише Enter -- Индикатор загрузки во время отправки -- Автоматическая очистка полей после успешной отправки - -## Использование через API - -### Endpoint - -``` -POST /send_message -POST {WEBHOOK_PATH}/send_message -``` - -Где `WEBHOOK_PATH` - путь из переменной окружения `WEBHOOK_URL` (например, `/testbot`). - -### Примеры URL: - -- **Локально:** `http://localhost:5005/send_message` -- **Через reverse proxy:** `https://bot.aabpro.ru/testbot/send_message` - -### Заголовки запроса - -``` -Content-Type: application/json -X-API-Secret-Token: <секретный_токен> -``` - -### Параметры запроса - -```json -{ - "user_id": 123456789, - "message": "Текст сообщения" -} -``` - -### Аутентификация - -API защищен секретным токеном, который передается в заголовке `X-API-Secret-Token`. Токен берется из переменной окружения `WEBHOOK_SECRET`. - -### Ответы - -**Успешная отправка (200):** -```json -{ - "success": true, - "message": "Сообщение отправлено успешно" -} -``` - -**Ошибка (400):** -```json -{ - "success": false, - "error": "Описание ошибки" -} -``` - -### Пример использования из Python - -```python -import requests - -def send_telegram_message(user_id, message): - # Используем внешний URL через reverse proxy - url = "https://bot.aabpro.ru/testbot/send_message" - data = { - "user_id": user_id, - "message": message - } - headers = { - 'Content-Type': 'application/json', - 'X-API-Secret-Token': 'ваш_секретный_токен' - } - - response = requests.post(url, json=data, headers=headers) - result = response.json() - - if result["success"]: - print("Сообщение отправлено успешно!") - else: - print(f"Ошибка: {result['error']}") -``` - -### Пример использования из командной строки - -```bash -# Используя curl (через reverse proxy) -curl -X POST https://bot.aabpro.ru/testbot/send_message \ - -H "Content-Type: application/json" \ - -H "X-API-Secret-Token: ваш_секретный_токен" \ - -d '{"user_id": 123456789, "message": "Привет!"}' - -# Используя curl (локально) -curl -X POST http://localhost:5005/send_message \ - -H "Content-Type: application/json" \ - -H "X-API-Secret-Token: ваш_секретный_токен" \ - -d '{"user_id": 123456789, "message": "Привет!"}' - -# Используя примерный скрипт -python send_telegram_example.py 123456789 "Привет! Это тестовое сообщение." -``` - -## Обработка ошибок - -Система обрабатывает следующие типы ошибок: - -### HTTP ошибки: -1. **401 Unauthorized** - Неверный или отсутствующий API токен -2. **400 Bad Request** - Неверный ID пользователя или отсутствующие данные -3. **500 Internal Server Error** - Внутренняя ошибка сервера - -### Коды ошибок API: -- `INVALID_TOKEN` - Неверный токен аутентификации -- `NO_DATA` - Отсутствуют данные в запросе -- `NO_USER_ID` - Не указан ID пользователя -- `NO_MESSAGE` - Не указан текст сообщения -- `INVALID_USER_ID` - user_id не является числом -- `TELEGRAM_ERROR` - Ошибка Telegram API -- `INTERNAL_ERROR` - Внутренняя ошибка сервера - -### Ошибки Telegram API: -1. **403 Forbidden** - Пользователь заблокировал бота или не начал с ним диалог -2. **400 Bad Request** - Неверный ID пользователя -3. **429 Too Many Requests** - Превышен лимит отправки сообщений -4. **404 Not Found** - Пользователь не найден - -### Диагностика в веб-интерфейсе - -Веб-интерфейс предоставляет подробную диагностику ошибок: -- **Время ответа** сервера -- **HTTP статус** код -- **Код ошибки** API -- **Конкретные предложения** по решению проблемы -- **Автоматическое скрытие** уведомлений - -Все ошибки логируются в файл логов с указанием типа ошибки и ID пользователя. - -## Логирование - -Все операции отправки сообщений записываются в лог с указанием: - -- Времени операции -- ID пользователя -- Статуса отправки (успех/ошибка) -- Краткого описания ошибки (при наличии) - -Пример записи в логе: -``` -[2024-01-15 10:30:45] [TG_bot] ИНФО: Сообщение отправлено: Привет! Это тестовое сообщение... (Пользователь user_123456789) -``` - -## Требования - -- Telegram бот должен быть запущен и подключен через webhook -- Пользователь должен начать диалог с ботом (отправить команду /start) -- Пользователь не должен заблокировать бота - -## Безопасность - -- API endpoint доступен только для POST запросов -- Проверка корректности входных данных -- Логирование всех попыток отправки для аудита -- Обработка ошибок без раскрытия внутренней информации системы +Документация: [`docs/telegram-messaging.md`](docs/telegram-messaging.md) diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..7dea76e --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.1 diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..d98a220 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,153 @@ +# Архитектура проекта + +Версия: см. [`VERSION`](../VERSION). История изменений: [`docs/CHANGELOG.md`](CHANGELOG.md). + +## Назначение + +Система загружает посты и события из внешних источников, хранит их в MariaDB, публикует анонсы в Telegram-канал и даёт веб-интерфейс для редактирования и управления подписками через responder-бота. + +Поддерживаются два режима работы (`WORKMODE` в `.env`): + +| Режим | Источники данных | Публикация | +|-------|------------------|------------| +| **ZILANT** | VK (`vk_load.py`), JSON Zilant (`zk_load.py`) | `tg_publish.py`, `evtg_publish.py` | +| **VOLK** | VOLK API (`volk_load.py`) | `volk_cat_publish.py` | + +Подробнее о VOLK: [`docs/volk-mode.md`](volk-mode.md). + +## Компоненты runtime + +``` +┌─────────────────┐ rsync ┌──────────────────┐ +│ Woodpecker CI │ ─────────────► │ /opt/testbot-git │ +└─────────────────┘ └────────┬─────────┘ + │ + ┌──────────────────────────────────┼──────────────────────────┐ + ▼ ▼ ▼ + runner.timer testbot-git.service testedit-git.service + → runner.sh gunicorn :5005 gunicorn :5006 + vk_load, zk_load, wsgi_bot → tg_mainbot wsgi_edit → db_edit + db_update_shortname, + tg_publish, evtg_publish +``` + +### Responder-бот (подписки) + +- **`tg_mainbot.py`** — Flask + webhook, обработка `/start`, callback-кнопок подписки. +- **`wsgi_bot.py`** — точка входа gunicorn; однократная регистрация webhook с file lock. +- **`season_links.py`** — сезонные payload (`zk2026_post_*`, `subscribe_zk2026_*` и т.д.). +- Старые ссылки без префикса сезона → сообщение «сезон завершён» + кнопка в канал. + +### Poster (публикация в канал) + +- **`tg_publish.py`** — посты VK → Telegram. +- **`evtg_publish.py`** — события → Telegram. +- **`volk_cat_publish.py`** — категории VOLK → Telegram (режим VOLK). +- Все исходящие вызовы Bot API — через **`telegram_relay.py`** (`RELAY_URL`). + +### Веб-интерфейс + +- **`db_edit.py`** — CRUD постов/событий, кнопки рескана, публикации, «Назвать» (AI shortname). +- **`db_update_shortname.py`** — генерация коротких названий через RouterAI. +- **`test_tg_poster.py`** — тестовый пост в канал (кнопка «Тест» в UI). +- Шаблоны: `templates/`. + +### База данных + +- **`ensure_db.py`** — создание/миграция схемы (вызывается из loader-скриптов). +- **`backupdb.sh`** — дамп БД при deploy (`post-deploy.sh`). + +## Пайплайн runner.sh (ZILANT) + +Порядок выполнения (systemd timer `runner.timer`): + +1. `vk_load.py` — загрузка постов из VK +2. `zk_load.py` — загрузка событий из JSON +3. `db_update_shortname.py` — AI shortname для новых записей +4. `tg_publish.py` — публикация постов +5. `evtg_publish.py` — публикация событий + +> **`tools/legacy/evt_prefetch.py`** раньше стоял перед `evtg_publish.py`; с 1.0.0 снят с пайплайна. + +## Deploy + +- **`.woodpecker.yml`** — rsync на сервер, затем `services/post-deploy.sh`. +- **`post-deploy.sh`** — venv, pip, backup БД, обновление systemd units, restart сервисов. + +## Конфигурация (.env) + +| Переменная | Назначение | +|------------|------------| +| `WORKMODE` | `ZILANT` или `VOLK` | +| `SEASON` | Префикс сезона для deep link (`zk2026`) | +| `RELAY_URL` | Internal relay для Bot API | +| `WEBHOOK_URL`, `WEBHOOK_SECRET` | Доставка апдейтов боту | +| `MDB_*`, `MDBASE` | MariaDB | +| `POSTER_BOT_TOKEN`, `RESPONDER_BOT_TOKEN` | Токены ботов | + +## Карта файлов + +### Ядро (runtime) + +| Файл | Роль | +|------|------| +| `tg_mainbot.py` | Responder-бот, webhook | +| `wsgi_bot.py` | Gunicorn entry (бот) | +| `db_edit.py` | Веб-UI | +| `wsgi_edit.py` | Gunicorn entry (UI) | +| `tg_publish.py` | Публикация постов | +| `evtg_publish.py` | Публикация событий | +| `vk_load.py` | Загрузка VK | +| `zk_load.py` | Загрузка Zilant JSON | +| `volk_load.py` | Загрузка VOLK (режим VOLK) | +| `volk_cat_publish.py` | Публикация VOLK (режим VOLK) | +| `db_update_shortname.py` | AI shortname | +| `formatter.py` | Форматирование текста постов/событий | +| `ensure_db.py` | Схема БД | +| `telegram_relay.py` | Relay для Bot API | +| `season_links.py` | Сезонные ссылки и callback | +| `test_tg_poster.py` | Тестовая публикация (UI) | +| `runner.sh` | Пайплайн по расписанию | +| `backupdb.sh` | Бэкап БД | +| `gunicorn_bot.conf.py` | Stub-конфиг gunicorn (бот) | + +### Инфраструктура + +| Путь | Роль | +|------|------| +| `services/testbot-git.service` | systemd: бот :5005 | +| `services/testedit-git.service` | systemd: UI :5006 | +| `services/runner.service`, `runner.timer` | systemd: hourly runner | +| `services/post-deploy.sh` | Post-deploy | +| `.woodpecker.yml` | CI deploy | + +### Вспомогательные (`tools/`) + +| Путь | Роль | +|------|------| +| `tools/ops/genkey.py` | Генерация секретов | +| `tools/ops/register_tg_webhook.py` | Ручная регистрация webhook | +| `tools/ops/copy_databases.sh` | Клонирование MariaDB | +| `tools/dev/send_telegram_example.py` | CLI-пример `/send_message` | +| `tools/dev/volk_telegram_testing.py` | Отладка публикации в Telegram | +| `tools/dev/volk_category_testing.py` | Отладка загрузки категории VOLK | +| `tools/legacy/evt_prefetch.py` | Legacy: prefetch событий | + +См. [`tools/README.md`](../tools/README.md). + +### Документация + +| Файл | Содержание | +|------|------------| +| `README.md` | Быстрый старт | +| `docs/ARCHITECTURE.md` | Этот файл | +| `docs/volk-mode.md` | Режим VOLK | +| `docs/telegram-messaging.md` | API `/send_message` | +| `docs/runner-setup.md` | Настройка runner timer | +| `docs/CHANGELOG.md` | История версий | + +## Версионирование + +- Версия в корне: **`VERSION`** (SemVer: `MAJOR.MINOR.PATCH`). +- При каждом значимом изменении обновлять `VERSION` и запись в `docs/CHANGELOG.md`. +- Правило для агентов: `.cursor/rules/version-and-docs.mdc`. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 0000000..3596c07 --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1,37 @@ +# Changelog + +Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.1.0/). +Версия проекта — в файле [`VERSION`](../VERSION) (SemVer). + +## [1.0.1] - 2026-08-30 + +### Changed + +- Утилиты перенесены в `tools/`: + - `tools/ops/` — `register_tg_webhook.py`, `copy_databases.sh` (рядом с `genkey.py`) + - `tools/dev/` — `send_telegram_example.py`, `volk_telegram_testing.py`, `volk_category_testing.py` +- Скрипты в `tools/` загружают `.env` из корня проекта и добавляют корень в `sys.path` при импорте модулей ядра. + +### Documentation + +- Обновлены `tools/README.md`, `docs/ARCHITECTURE.md`, `docs/volk-mode.md`, `docs/telegram-messaging.md`, `README.md`. + +## [1.0.0] - 2026-08-30 + +### Added + +- Файл версии `VERSION` (SemVer). +- Документация: `README.md`, `docs/ARCHITECTURE.md`, `docs/volk-mode.md`. +- Каталог `tools/` с README; `tools/ops/genkey.py`, `tools/legacy/evt_prefetch.py`. +- Сезонные deep link и callback с префиксом `SEASON` (`season_links.py`, по умолчанию `zk2026`). +- Интеграция Telegram Bot API через internal relay (`telegram_relay.py`, `RELAY_URL`). + +### Changed + +- `evt_prefetch.py` перенесён в `tools/legacy/` — не входит в `runner.sh`. +- `genkey.py` перенесён в `tools/ops/`. +- Документация по runner и отправке сообщений — в `docs/`. + +### Documentation + +- Правило Cursor `.cursor/rules/version-and-docs.mdc` — поддержка версии и changelog при изменениях. diff --git a/docs/runner-setup.md b/docs/runner-setup.md new file mode 100644 index 0000000..a1fca34 --- /dev/null +++ b/docs/runner-setup.md @@ -0,0 +1,75 @@ +# Настройка автоматического запуска runner.sh + +См. также [`docs/ARCHITECTURE.md`](ARCHITECTURE.md) — состав пайплайна. + +Есть два способа настроить запуск `runner.sh` каждый час: + +## Вариант 1: Systemd Timer (рекомендуется) + +В проекте уже используются systemd-сервисы. + +### Установка + +1. Скопируйте файлы в systemd: + +```bash +sudo cp services/runner.service /etc/systemd/system/ +sudo cp services/runner.timer /etc/systemd/system/ +``` + +2. Или используйте скрипт установки: + +```bash +chmod +x services/install_runner_timer.sh +sudo bash services/install_runner_timer.sh +``` + +3. Вручную: + +```bash +sudo systemctl daemon-reload +sudo systemctl enable runner.timer +sudo systemctl start runner.timer +``` + +### Управление + +- Статус: `sudo systemctl status runner.timer` +- Остановить: `sudo systemctl stop runner.timer` +- Запустить: `sudo systemctl start runner.timer` +- Отключить: `sudo systemctl disable runner.timer` +- Логи: `sudo journalctl -u runner.service -f` +- Список timers: `sudo systemctl list-timers` + +### Расписание + +Файл `/etc/systemd/system/runner.timer`: + +- `OnCalendar=hourly` — каждый час +- `OnCalendar=*-*-* *:00:00` — в начале каждого часа +- `OnCalendar=*-*-* 0,6,12,18:00:00` — в 0, 6, 12, 18 часов +- `OnCalendar=Mon..Fri 09:00:00` — будни в 9:00 + +После изменения: `sudo systemctl daemon-reload` + +--- + +## Вариант 2: Cron + +```bash +sudo crontab -e +``` + +```bash +0 * * * * /bin/bash /opt/testbot-git/runner.sh >> /opt/testbot-git/runner_cron.log 2>&1 +``` + +Другие варианты: `30 * * * *`, `*/30 * * * *`, `0 9-17 * * *`. + +--- + +## Состав runner.sh (ZILANT) + +`vk_load.py` → `zk_load.py` → `db_update_shortname.py` → `tg_publish.py` → `evtg_publish.py` + +Legacy `tools/legacy/evt_prefetch.py` в пайплайн не входит (с 1.0.0). diff --git a/docs/telegram-messaging.md b/docs/telegram-messaging.md new file mode 100644 index 0000000..57a891c --- /dev/null +++ b/docs/telegram-messaging.md @@ -0,0 +1,94 @@ +# Отправка сообщений через Telegram бота + +## Описание + +В проект добавлена возможность отправки сообщений пользователям Telegram через бота по их ID. Функциональность включает: + +1. **API endpoint** для отправки сообщений из других скриптов +2. **Интерфейс в веб-приложении** для отправки сообщений через браузер +3. **Обработку ошибок** и логирование всех операций + +## Архитектура системы + +Система состоит из двух основных компонентов: + +- **Веб-интерфейс** (`db_edit.py`) - работает на порту **5006** (WEBCTRL_PORT) +- **Telegram бот** (`tg_mainbot.py`) - работает на порту **5005** (WEBHOOK_PORT) + +API endpoint `/send_message` находится в Telegram боте, поэтому все запросы должны отправляться на порт **5005**. + +### Конфигурация портов + +Порты и URL настраиваются в файле `.env`: + +- `RELAY_URL` - базовый URL **internal_relay** (все исходящие вызовы Bot API идут только через него) +- `WEBHOOK_PORT` - порт для Telegram бота (по умолчанию 5005) +- `WEBHOOK_URL` - URL доставки апдейтов от internal_relay к боту +- `WEBCTRL_PORT` - порт для веб-интерфейса (по умолчанию 5006) + +## Использование через веб-интерфейс + +В заголовке страницы добавлены поля **ID пользователя**, **Сообщение** и кнопка **Отправить**. + +## Использование через API + +### Endpoint + +``` +POST /send_message +POST {WEBHOOK_PATH}/send_message +``` + +### Заголовки + +``` +Content-Type: application/json +X-API-Secret-Token: +``` + +### Тело запроса + +```json +{ + "user_id": 123456789, + "message": "Текст сообщения" +} +``` + +### Пример (Python) + +```python +import requests + +def send_telegram_message(user_id, message): + url = "http://192.168.0.109:5005/testbot/send_message" + headers = { + "Content-Type": "application/json", + "X-API-Secret-Token": "ваш_секретный_токен", + } + response = requests.post(url, json={"user_id": user_id, "message": message}, headers=headers) + return response.json() +``` + +### Пример (curl) + +```bash +curl -X POST http://localhost:5005/testbot/send_message \ + -H "Content-Type: application/json" \ + -H "X-API-Secret-Token: ваш_секретный_токен" \ + -d '{"user_id": 123456789, "message": "Привет!"}' +``` + +Опционально: CLI-скрипт `tools/dev/send_telegram_example.py`. + +## Коды ошибок API + +- `INVALID_TOKEN`, `NO_DATA`, `NO_USER_ID`, `NO_MESSAGE`, `INVALID_USER_ID` +- `TELEGRAM_ERROR`, `INTERNAL_ERROR` + +## Требования + +- Бот запущен (webhook через `wsgi_bot.py`) +- Пользователь начал диалог с ботом (`/start`) + +См. также [`ARCHITECTURE.md`](ARCHITECTURE.md). diff --git a/docs/volk-mode.md b/docs/volk-mode.md new file mode 100644 index 0000000..df347b7 --- /dev/null +++ b/docs/volk-mode.md @@ -0,0 +1,53 @@ +# Режим VOLK + +Режим включается в `.env`: + +```env +WORKMODE = "VOLK" +``` + +Проект поддерживает оба режима — **ZILANT** и **VOLK**; переключение только через `WORKMODE`, без смены кодовой базы. + +## Отличия от ZILANT + +| Аспект | ZILANT | VOLK | +|--------|--------|------| +| Загрузка данных | `vk_load.py`, `zk_load.py` | `volk_load.py` | +| Публикация | `tg_publish.py`, `evtg_publish.py` | `volk_cat_publish.py` | +| Runner | Полный пайплайн ZILANT | Отдельные кнопки рескана в UI | +| UI | Посты + события VK/Zilant | Категории (площадки) + события VOLK | + +## Модули VOLK + +### `volk_load.py` + +- Загружает список категорий и событий с VOLK API. +- Переменные: `VOLK_CATEGORY_LIST`, `VOLK_CATEGORY_DESC_PREFIX`, `VOLK_EVENT_LIST_PREFIX`, `VOLK_API_VERSION`. +- Вызывается из `db_edit.py` (API `/api/run_volk_rescan`, кнопка «Рескан ВОЛК»). + +### `volk_cat_publish.py` + +- Публикует категории (площадки) в Telegram-канал. +- Функции `tg_post_category_by_id`, `tg_post_all_categories` — из веб-UI. + +### Шаблоны + +- `templates/edit_category.html` — редактирование площадки (только VOLK). +- Блоки `{% if g.workmode == 'VOLK' %}` в `templates/index.html`. + +## Отладочные скрипты (не в production-пайплайне) + +| Файл | Запуск | +|------|--------| +| `tools/dev/volk_category_testing.py` | `python tools/dev/volk_category_testing.py ` | +| `tools/dev/volk_telegram_testing.py` | `python tools/dev/volk_telegram_testing.py` | + +См. [`tools/README.md`](../tools/README.md). + +## Переключение режима на сервере + +1. Изменить `WORKMODE` в `/opt/testbot-git/.env`. +2. Перезапустить UI: `sudo systemctl restart testedit-git.service`. +3. Responder-бот (`testbot-git`) от режима не зависит; сезонные ссылки общие (`SEASON`). + +При смене режима проверьте переменные VOLK/ZILANT в `.env` (префиксы URL, API, `LOG_FILE`). diff --git a/genkey.py b/genkey.py deleted file mode 100644 index 9c41e7c..0000000 --- a/genkey.py +++ /dev/null @@ -1,2 +0,0 @@ -import secrets -print(secrets.token_hex(32)) diff --git a/runner.sh b/runner.sh index f6ebe37..07dacde 100644 --- a/runner.sh +++ b/runner.sh @@ -6,7 +6,8 @@ SCRIPTS_DIR="/opt/testbot-git" VENV_ACTIVATE="$SCRIPTS_DIR/venv/bin/activate" # Список скриптов для запуска в порядке выполнения -# SCRIPTS=("vk_load.py" "zk_load.py" "db_update_shortname.py" "evt_prefetch.py" "tg_publish.py" "evtg_publish.py") +# Legacy prefetch (не в пайплайне): tools/legacy/evt_prefetch.py +# SCRIPTS=("vk_load.py" "zk_load.py" "db_update_shortname.py" "tools/legacy/evt_prefetch.py" "tg_publish.py" "evtg_publish.py") SCRIPTS=("vk_load.py" "zk_load.py" "db_update_shortname.py" "tg_publish.py" "evtg_publish.py") # Интервалы между скриптами (в секундах) diff --git a/services/RUNNER_SETUP.md b/services/RUNNER_SETUP.md index 01c2e95..e1f6754 100644 --- a/services/RUNNER_SETUP.md +++ b/services/RUNNER_SETUP.md @@ -1,87 +1,3 @@ -# Настройка автоматического запуска runner.sh каждый час - -Есть два способа настроить запуск `runner.sh` каждый час: - -## Вариант 1: Systemd Timer (Рекомендуется) - -Этот вариант рекомендуется, так как в проекте уже используются systemd сервисы. - -### Установка: - -1. Скопируйте файлы в systemd: -```bash -sudo cp services/runner.service /etc/systemd/system/ -sudo cp services/runner.timer /etc/systemd/system/ -``` - -2. Или используйте скрипт установки: -```bash -chmod +x services/install_runner_timer.sh -sudo bash services/install_runner_timer.sh -``` - -3. Вручную: -```bash -sudo systemctl daemon-reload -sudo systemctl enable runner.timer -sudo systemctl start runner.timer -``` - -### Управление: - -- Проверить статус: `sudo systemctl status runner.timer` -- Остановить: `sudo systemctl stop runner.timer` -- Запустить: `sudo systemctl start runner.timer` -- Отключить: `sudo systemctl disable runner.timer` -- Просмотр логов: `sudo journalctl -u runner.service -f` -- Список всех timers: `sudo systemctl list-timers` - -### Настройка расписания: - -Отредактируйте файл `/etc/systemd/system/runner.timer`: - -- `OnCalendar=hourly` - каждый час -- `OnCalendar=*-*-* *:00:00` - каждый час в начале часа -- `OnCalendar=*-*-* 0,6,12,18:00:00` - в 0, 6, 12, 18 часов -- `OnCalendar=Mon..Fri 09:00:00` - каждый будний день в 9:00 - -После изменения: `sudo systemctl daemon-reload` - ---- - -## Вариант 2: Cron - -Классический способ для периодических задач. - -### Установка: - -1. Откройте crontab для редактирования: -```bash -sudo crontab -e -``` - -2. Добавьте строку: -```bash -0 * * * * /bin/bash /opt/testbot-git/runner.sh >> /opt/testbot-git/runner_cron.log 2>&1 -``` - -Это запустит скрипт в начале каждого часа (0 минут каждого часа). - -### Альтернативные варианты расписания: - -- `0 * * * *` - каждый час в начале часа (00:00, 01:00, 02:00...) -- `30 * * * *` - каждый час в 30 минут (00:30, 01:30, 02:30...) -- `*/30 * * * *` - каждые 30 минут -- `0 9-17 * * *` - каждый час с 9:00 до 17:00 - -### Просмотр логов: - -Логи будут записываться в `/opt/testbot-git/runner_cron.log` (если указан в crontab). - ---- - -## Рекомендации - -- **Systemd Timer** - лучше для интеграции с существующими сервисами, более гибкое управление, лучшие логи -- **Cron** - проще для простых задач, не требует root прав (можно использовать `crontab -e` без sudo) +# Перенесено +Документация: [`docs/runner-setup.md`](../docs/runner-setup.md) diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 0000000..d6909ac --- /dev/null +++ b/tools/README.md @@ -0,0 +1,41 @@ +# Вспомогательные скрипты + +Скрипты вне основного пайплайна (`runner.sh`) и systemd-сервисов. Запускать из **корня проекта** с активированным venv. + +## ops/ — эксплуатация + +| Файл | Назначение | +|------|------------| +| `genkey.py` | Генерация hex-ключа для `SECRET_KEY` / `WEBHOOK_SECRET` | +| `register_tg_webhook.py` | Ручная регистрация webhook (в норме — `wsgi_bot.py` при старте) | +| `copy_databases.sh` | Клонирование MariaDB между базами | + +```bash +python tools/ops/genkey.py +./venv/bin/python tools/ops/register_tg_webhook.py +bash tools/ops/copy_databases.sh +``` + +## dev/ — отладка и примеры + +| Файл | Назначение | +|------|------------| +| `send_telegram_example.py` | CLI-пример API `/send_message` | +| `volk_telegram_testing.py` | Отладочный тест публикации в Telegram | +| `volk_category_testing.py` | Отладочный тест загрузки категории VOLK | + +```bash +python tools/dev/send_telegram_example.py "" +python tools/dev/volk_telegram_testing.py +python tools/dev/volk_category_testing.py +``` + +## legacy/ — снятые с пайплайна + +| Файл | Назначение | +|------|------------| +| `evt_prefetch.py` | Массовая пометка видимых событий `marked_to_publication=1`. Убран из `runner.sh` в 1.0.0 | + +```bash +./venv/bin/python tools/legacy/evt_prefetch.py +``` diff --git a/send_telegram_example.py b/tools/dev/send_telegram_example.py similarity index 83% rename from send_telegram_example.py rename to tools/dev/send_telegram_example.py index 1c479b8..e496914 100644 --- a/send_telegram_example.py +++ b/tools/dev/send_telegram_example.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 """ -Пример скрипта для отправки сообщений через Telegram бота -Использование: python send_telegram_example.py +Пример скрипта для отправки сообщений через Telegram бота. + +Использование (из корня проекта): + python tools/dev/send_telegram_example.py """ import sys @@ -9,6 +11,12 @@ import requests import json import os +from dotenv import load_dotenv + +_PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +load_dotenv(os.path.join(_PROJECT_ROOT, ".env")) +load_dotenv() + def send_message(user_id, message_text, bot_url=None, api_token=None): """ Отправляет сообщение пользователю через Telegram бота @@ -64,8 +72,8 @@ def send_message(user_id, message_text, bot_url=None, api_token=None): def main(): if len(sys.argv) != 3: - print("Использование: python send_telegram_example.py ") - print("Пример: python send_telegram_example.py 123456789 'Привет! Это тестовое сообщение.'") + print("Использование: python tools/dev/send_telegram_example.py ") + print("Пример: python tools/dev/send_telegram_example.py 123456789 'Привет! Это тестовое сообщение.'") sys.exit(1) try: diff --git a/volk_category_testing.py b/tools/dev/volk_category_testing.py similarity index 97% rename from volk_category_testing.py rename to tools/dev/volk_category_testing.py index 4422f18..14fa9dd 100644 --- a/volk_category_testing.py +++ b/tools/dev/volk_category_testing.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 """ Тестовый модуль для проверки загрузки описания категории. -Использование: python volk_category_testing.py + +Использование (из корня проекта): + python tools/dev/volk_category_testing.py """ import os import sys @@ -15,7 +17,8 @@ from urllib.request import urlopen, Request from urllib.error import URLError, HTTPError from dotenv import load_dotenv -# Загрузка переменных окружения +_PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +load_dotenv(os.path.join(_PROJECT_ROOT, ".env")) load_dotenv() # Параметры из .env @@ -260,8 +263,8 @@ def main(): formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Примеры использования: - python volk_category_testing.py volk26_nri - python volk_category_testing.py volk26_nri volk26_other volk26_third + python tools/dev/volk_category_testing.py volk26_nri + python tools/dev/volk_category_testing.py volk26_nri volk26_other volk26_third """ ) parser.add_argument( diff --git a/volk_telegram_testing.py b/tools/dev/volk_telegram_testing.py similarity index 99% rename from volk_telegram_testing.py rename to tools/dev/volk_telegram_testing.py index 8e892b7..235a2cb 100644 --- a/volk_telegram_testing.py +++ b/tools/dev/volk_telegram_testing.py @@ -7,12 +7,17 @@ import sys import json from datetime import datetime from dotenv import load_dotenv + +_PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +if _PROJECT_ROOT not in sys.path: + sys.path.insert(0, _PROJECT_ROOT) + +load_dotenv(os.path.join(_PROJECT_ROOT, ".env")) +load_dotenv() + from telegram_relay import bot_api_method_url, get_relay_url from season_links import subscription_start_link -# Загрузка переменных окружения -load_dotenv() - # Настройки из переменных окружения POSTER_BOT_TOKEN = os.getenv('POSTER_BOT_TOKEN') RESPONDER_BOT_NAME = os.getenv('RESPONDER_BOT_NAME') diff --git a/evt_prefetch.py b/tools/legacy/evt_prefetch.py similarity index 72% rename from evt_prefetch.py rename to tools/legacy/evt_prefetch.py index 17dfea2..56ce467 100644 --- a/evt_prefetch.py +++ b/tools/legacy/evt_prefetch.py @@ -1,10 +1,19 @@ +#!/usr/bin/env python3 +"""LEGACY — не входит в пайплайн runner.sh с версии 1.0.0. + +Проставляет marked_to_publication = 1 видимым событиям (is_visible = 1), +у которых флаг ещё не установлен. + +Запуск вручную из корня проекта: + ./venv/bin/python tools/legacy/evt_prefetch.py +""" import os import mysql.connector from mysql.connector import Error -from datetime import datetime, timezone from dotenv import load_dotenv -# Загрузка переменных окружения +_PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +load_dotenv(os.path.join(_PROJECT_ROOT, ".env")) load_dotenv() MDB_HOST = os.getenv('MDB_HOST') @@ -12,6 +21,7 @@ MDB_USER = os.getenv('MDB_USER') MDB_PW = os.getenv('MDB_PW') MDBASE = os.getenv('MDBASE') + def prefetch_all_events(): """ Проверяет записи в таблице events и устанавливает флаг marked_to_publication @@ -20,40 +30,33 @@ def prefetch_all_events(): - не имеют флага marked_to_publication = 1 """ try: - # Подключение к базе данных connection = mysql.connector.connect( host=MDB_HOST, user=MDB_USER, password=MDB_PW, database=MDBASE ) - - # Установка временной зоны соединения в UTC + cursor_temp = connection.cursor() cursor_temp.execute("SET time_zone = '+00:00'") cursor_temp.close() - + cursor = connection.cursor() - - # SQL запрос для обновления записей + update_query = """ - UPDATE events - SET marked_to_publication = 1 - WHERE is_visible = 1 + UPDATE events + SET marked_to_publication = 1 + WHERE is_visible = 1 AND marked_to_publication = 0 """ - - # Выполнение запроса + cursor.execute(update_query) updated_count = cursor.rowcount - - # Фиксация изменений connection.commit() - + print(f"Обновлено записей: {updated_count}") - return updated_count - + except Error as e: print(f"Ошибка базы данных: {e}") if 'connection' in locals() and connection.is_connected(): @@ -67,5 +70,6 @@ def prefetch_all_events(): cursor.close() connection.close() + if __name__ == "__main__": prefetch_all_events() diff --git a/copy_databases.sh b/tools/ops/copy_databases.sh similarity index 94% rename from copy_databases.sh rename to tools/ops/copy_databases.sh index 5837928..ebcf86a 100644 --- a/copy_databases.sh +++ b/tools/ops/copy_databases.sh @@ -1,7 +1,9 @@ #!/bin/bash -# Использование: ./copy_db.sh -# Пример: ./copy_db.sh old_db new_db appuser +# Использование (из корня проекта): +# bash tools/ops/copy_databases.sh +# Пример: +# bash tools/ops/copy_databases.sh old_db new_db appuser set -e # Завершать выполнение при любой ошибке diff --git a/tools/ops/genkey.py b/tools/ops/genkey.py new file mode 100644 index 0000000..d90534a --- /dev/null +++ b/tools/ops/genkey.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +"""Генерация случайного hex-ключа для SECRET_KEY или WEBHOOK_SECRET. + +Запуск из корня проекта: + python tools/ops/genkey.py +""" +import secrets + +if __name__ == "__main__": + print(secrets.token_hex(32)) diff --git a/register_tg_webhook.py b/tools/ops/register_tg_webhook.py similarity index 88% rename from register_tg_webhook.py rename to tools/ops/register_tg_webhook.py index 29fafe4..ca172b5 100644 --- a/register_tg_webhook.py +++ b/tools/ops/register_tg_webhook.py @@ -2,21 +2,24 @@ """ Ручная регистрация Telegram webhook через internal_relay (с secret_token). -Запуск на сервере бота: - cd /opt/testbot-git - ./venv/bin/python register_tg_webhook.py +Запуск из корня проекта: + ./venv/bin/python tools/ops/register_tg_webhook.py Печатает полный ответ setWebhook — по нему видно, принял ли Telegram секрет. """ import json import sys +import os + +_PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +if _PROJECT_ROOT not in sys.path: + sys.path.insert(0, _PROJECT_ROOT) from dotenv import load_dotenv -import os import requests -load_dotenv(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")) +load_dotenv(os.path.join(_PROJECT_ROOT, ".env")) load_dotenv() from telegram_relay import bot_api_method_url, get_relay_url