реструктуризация файлов проекта
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-08-30 10:08:43 +03:00
parent adc49c285c
commit 251fa0be11
21 changed files with 619 additions and 333 deletions
+23
View File
@@ -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**.
-1
View File
@@ -1,4 +1,3 @@
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
*$py.class *$py.class
__pycache__/season_links.cpython-314.pyc
+65 -3
View File
@@ -1,4 +1,66 @@
# Zilant2025 # Zilant / VOLK — Telegram poster и подписки
Zilantkon bot testarea **Версия:** 1.0.1 ([`VERSION`](VERSION))
test update #8
Автоматическая публикация постов и событий в 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
```
+2 -204
View File
@@ -1,205 +1,3 @@
# Отправка сообщений через Telegram бота # Перенесено
## Описание Документация: [`docs/telegram-messaging.md`](docs/telegram-messaging.md)
В проект добавлена возможность отправки сообщений пользователям 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 запросов
- Проверка корректности входных данных
- Логирование всех попыток отправки для аудита
- Обработка ошибок без раскрытия внутренней информации системы
+1
View File
@@ -0,0 +1 @@
1.0.1
+153
View File
@@ -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`.
+37
View File
@@ -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 при изменениях.
+75
View File
@@ -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).
+94
View File
@@ -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: <WEBHOOK_SECRET>
```
### Тело запроса
```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).
+53
View File
@@ -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 <category_id>` |
| `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`).
-2
View File
@@ -1,2 +0,0 @@
import secrets
print(secrets.token_hex(32))
+2 -1
View File
@@ -6,7 +6,8 @@ SCRIPTS_DIR="/opt/testbot-git"
VENV_ACTIVATE="$SCRIPTS_DIR/venv/bin/activate" 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") SCRIPTS=("vk_load.py" "zk_load.py" "db_update_shortname.py" "tg_publish.py" "evtg_publish.py")
# Интервалы между скриптами (в секундах) # Интервалы между скриптами (в секундах)
+2 -86
View File
@@ -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)
+41
View File
@@ -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 <source_db> <target_db> <username>
```
## 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 <user_id> "<message>"
python tools/dev/volk_telegram_testing.py
python tools/dev/volk_category_testing.py <category_id>
```
## legacy/ — снятые с пайплайна
| Файл | Назначение |
|------|------------|
| `evt_prefetch.py` | Массовая пометка видимых событий `marked_to_publication=1`. Убран из `runner.sh` в 1.0.0 |
```bash
./venv/bin/python tools/legacy/evt_prefetch.py
```
@@ -1,7 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Пример скрипта для отправки сообщений через Telegram бота Пример скрипта для отправки сообщений через Telegram бота.
Использование: python send_telegram_example.py <user_id> <message>
Использование (из корня проекта):
python tools/dev/send_telegram_example.py <user_id> <message>
""" """
import sys import sys
@@ -9,6 +11,12 @@ import requests
import json import json
import os 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): def send_message(user_id, message_text, bot_url=None, api_token=None):
""" """
Отправляет сообщение пользователю через Telegram бота Отправляет сообщение пользователю через Telegram бота
@@ -64,8 +72,8 @@ def send_message(user_id, message_text, bot_url=None, api_token=None):
def main(): def main():
if len(sys.argv) != 3: if len(sys.argv) != 3:
print("Использование: python send_telegram_example.py <user_id> <message>") print("Использование: python tools/dev/send_telegram_example.py <user_id> <message>")
print("Пример: python send_telegram_example.py 123456789 'Привет! Это тестовое сообщение.'") print("Пример: python tools/dev/send_telegram_example.py 123456789 'Привет! Это тестовое сообщение.'")
sys.exit(1) sys.exit(1)
try: try:
@@ -1,7 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Тестовый модуль для проверки загрузки описания категории. Тестовый модуль для проверки загрузки описания категории.
Использование: python volk_category_testing.py <category_id>
Использование (из корня проекта):
python tools/dev/volk_category_testing.py <category_id>
""" """
import os import os
import sys import sys
@@ -15,7 +17,8 @@ from urllib.request import urlopen, Request
from urllib.error import URLError, HTTPError from urllib.error import URLError, HTTPError
from dotenv import load_dotenv 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() load_dotenv()
# Параметры из .env # Параметры из .env
@@ -260,8 +263,8 @@ def main():
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=""" epilog="""
Примеры использования: Примеры использования:
python volk_category_testing.py volk26_nri python tools/dev/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 volk26_other volk26_third
""" """
) )
parser.add_argument( parser.add_argument(
@@ -7,12 +7,17 @@ import sys
import json import json
from datetime import datetime from datetime import datetime
from dotenv import load_dotenv 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 telegram_relay import bot_api_method_url, get_relay_url
from season_links import subscription_start_link from season_links import subscription_start_link
# Загрузка переменных окружения
load_dotenv()
# Настройки из переменных окружения # Настройки из переменных окружения
POSTER_BOT_TOKEN = os.getenv('POSTER_BOT_TOKEN') POSTER_BOT_TOKEN = os.getenv('POSTER_BOT_TOKEN')
RESPONDER_BOT_NAME = os.getenv('RESPONDER_BOT_NAME') RESPONDER_BOT_NAME = os.getenv('RESPONDER_BOT_NAME')
@@ -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 os
import mysql.connector import mysql.connector
from mysql.connector import Error from mysql.connector import Error
from datetime import datetime, timezone
from dotenv import load_dotenv 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() load_dotenv()
MDB_HOST = os.getenv('MDB_HOST') MDB_HOST = os.getenv('MDB_HOST')
@@ -12,6 +21,7 @@ MDB_USER = os.getenv('MDB_USER')
MDB_PW = os.getenv('MDB_PW') MDB_PW = os.getenv('MDB_PW')
MDBASE = os.getenv('MDBASE') MDBASE = os.getenv('MDBASE')
def prefetch_all_events(): def prefetch_all_events():
""" """
Проверяет записи в таблице events и устанавливает флаг marked_to_publication Проверяет записи в таблице events и устанавливает флаг marked_to_publication
@@ -20,40 +30,33 @@ def prefetch_all_events():
- не имеют флага marked_to_publication = 1 - не имеют флага marked_to_publication = 1
""" """
try: try:
# Подключение к базе данных
connection = mysql.connector.connect( connection = mysql.connector.connect(
host=MDB_HOST, host=MDB_HOST,
user=MDB_USER, user=MDB_USER,
password=MDB_PW, password=MDB_PW,
database=MDBASE database=MDBASE
) )
# Установка временной зоны соединения в UTC
cursor_temp = connection.cursor() cursor_temp = connection.cursor()
cursor_temp.execute("SET time_zone = '+00:00'") cursor_temp.execute("SET time_zone = '+00:00'")
cursor_temp.close() cursor_temp.close()
cursor = connection.cursor() cursor = connection.cursor()
# SQL запрос для обновления записей
update_query = """ update_query = """
UPDATE events UPDATE events
SET marked_to_publication = 1 SET marked_to_publication = 1
WHERE is_visible = 1 WHERE is_visible = 1
AND marked_to_publication = 0 AND marked_to_publication = 0
""" """
# Выполнение запроса
cursor.execute(update_query) cursor.execute(update_query)
updated_count = cursor.rowcount updated_count = cursor.rowcount
# Фиксация изменений
connection.commit() connection.commit()
print(f"Обновлено записей: {updated_count}") print(f"Обновлено записей: {updated_count}")
return updated_count return updated_count
except Error as e: except Error as e:
print(f"Ошибка базы данных: {e}") print(f"Ошибка базы данных: {e}")
if 'connection' in locals() and connection.is_connected(): if 'connection' in locals() and connection.is_connected():
@@ -67,5 +70,6 @@ def prefetch_all_events():
cursor.close() cursor.close()
connection.close() connection.close()
if __name__ == "__main__": if __name__ == "__main__":
prefetch_all_events() prefetch_all_events()
@@ -1,7 +1,9 @@
#!/bin/bash #!/bin/bash
# Использование: ./copy_db.sh <source_db> <target_db> <username> # Использование (из корня проекта):
# Пример: ./copy_db.sh old_db new_db appuser # bash tools/ops/copy_databases.sh <source_db> <target_db> <username>
# Пример:
# bash tools/ops/copy_databases.sh old_db new_db appuser
set -e # Завершать выполнение при любой ошибке set -e # Завершать выполнение при любой ошибке
+10
View File
@@ -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))
@@ -2,21 +2,24 @@
""" """
Ручная регистрация Telegram webhook через internal_relay (с secret_token). Ручная регистрация Telegram webhook через internal_relay (с secret_token).
Запуск на сервере бота: Запуск из корня проекта:
cd /opt/testbot-git ./venv/bin/python tools/ops/register_tg_webhook.py
./venv/bin/python register_tg_webhook.py
Печатает полный ответ setWebhook по нему видно, принял ли Telegram секрет. Печатает полный ответ setWebhook по нему видно, принял ли Telegram секрет.
""" """
import json import json
import sys 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 from dotenv import load_dotenv
import os
import requests 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() load_dotenv()
from telegram_relay import bot_api_method_url, get_relay_url from telegram_relay import bot_api_method_url, get_relay_url