Документация Блока 3 и версия 0.3.5

- docs/events.md дополнен схемой ClickHouse, агрегатами, регламентом хранения и списком уведомлений в чат логов
- README описывает аналитику, уведомления, бэкап событий и структуру проекта
- DEPLOY.md описывает сервис ClickHouse, проверку, настройку бэкапа и cron
- примеры сообщений коммитов приведены к стилю без префиксов
- версия 0.3.5
This commit is contained in:
MpakobecEx 2026-10-16 15:30:00 +05:00
parent dbf78aa1d5
commit cd9004c10f
5 changed files with 153 additions and 13 deletions

View File

@ -15,9 +15,13 @@ cd DispelEvilBot
```bash
cp env/test.env.example env/test.env
# заполнить: BOT_TOKEN, DB_PASSWORD, CHAT_ID (основной чат), ALERT_CHAT_ID (опц.), ADMIN_IDS (опц.)
# заполнить: BOT_TOKEN, DB_PASSWORD, CHAT_ID (основной чат), ALERT_CHAT_ID (опц.),
# ADMIN_IDS (опц.), CLICKHOUSE_PASSWORD (аналитика событий)
```
Порт ClickHouse на хосте (`CLICKHOUSE_HTTP_PORT`) у контуров должен различаться:
`prod` — `8123`, `test` — `8124` (иначе контейнеры конфликтуют на одной VPS).
## 3. Логин в registry (если пакет приватный)
```bash
@ -46,7 +50,62 @@ docker compose -p test --env-file env/test.env -f docker-compose.prod.yml logs -
1. `postgres` поднимается и проходит healthcheck;
2. `migrate` (**Flyway**) накатывает миграции из `sql/` — для пустой БД с нуля;
3. `bot` стартует (`Запуск DispelEvilBot v<версия> (APP_ENV=...)`).
3. `clickhouse` поднимается и применяет `clickhouse/init/001_events.sql` —
**только при первой инициализации** пустого data-каталога;
4. `bot` стартует (`Запуск DispelEvilBot v<версия> (APP_ENV=...)`), аналитика и рассылки — в нём же.
Проверка контура (сервисы, миграции, Postgres, ClickHouse):
```bash
make check E=test
```
## ClickHouse (аналитика событий)
События всех действий бота пишутся в `analytics.events` (контракт — [`docs/events.md`](docs/events.md)).
ClickHouse наружу **не публикуется**: только loopback хоста (`127.0.0.1:${CLICKHOUSE_HTTP_PORT}`).
```bash
make ch E=test # clickhouse-client контура
# пример: события за сегодня по типам
docker compose -p test --env-file env/test.env -f docker-compose.prod.yml exec -T clickhouse \
clickhouse-client --user "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD" \
--query "SELECT event_type, count() FROM analytics.events WHERE toDate(event_time) = today() GROUP BY event_type"
```
Если ClickHouse недоступен, бот продолжает работать: события уходят в stdout (WARNING),
полностью выключить можно флагом `CLICKHOUSE_ENABLED=false`.
Изменения схемы: `clickhouse/init/*.sql` применяются лишь на пустом data-каталоге,
поэтому на существующем контуре DDL нужно применить вручную (`make ch E=...`).
## Бэкап событий на домашний сервер
Сырые события живут на VPS ограниченное время: раз в сутки вчерашняя партиция
выгружается в `Parquet.gz`, передаётся на домашний сервер, импортируется в тамошний
ClickHouse и сверяется по количеству строк — **только после сверки** партиции старше
`BACKUP_GRACE_DAYS` (по умолчанию 7) удаляются на VPS. На VPS остаются агрегаты
(`events_daily`, `money_daily`).
Разовая настройка:
```bash
cp env/backup.env.example env/backup.env # BACKUP_REMOTE, BACKUP_DIR, BACKUP_SSH_KEY, ...
# на домашнем сервере (в каталоге, куда кладутся архивы) разместить:
# scripts/clickhouse_import.sh
# clickhouse/init/001_events.sql -> как 001_events.sql рядом со скриптом
```
Проверочный прогон (без cron): `make backup E=test`.
Cron на VPS (вчерашний день уже закрыт, время московское):
```cron
0 4 * * * cd /opt/DispelEvilBot && ./scripts/clickhouse_backup.sh test >> /var/log/clickhouse-backup-test.log 2>&1
```
Ошибка на любом шаге оставляет данные на VPS (партиции не удаляются) и пишет понятный лог.
Импорт дня идемпотентен: повторный прогон не дублирует строки.
## Обновление и откат
@ -67,7 +126,13 @@ docker compose -p <контур> --env-file env/<контур>.env -f docker-com
## Переменные окружения
См. `env/prod.env.example` / `env/test.env.example`.
См. `env/prod.env.example` / `env/test.env.example` (контуры) и `env/backup.env.example` (бэкап событий).
## Дашборд (позже)
Superset поднимается на домашнем сервере и читает **локальный** ClickHouse с полной
историей событий (сюда ежедневно приезжают архивы). Отдельный read-only пользователь
ClickHouse и сам дашборд — финальный этап, в этот контур не входят.
## Перенос данных на прод (позже, короткое окно простоя)

View File

@ -6,9 +6,12 @@ Telegram-бот клана Dispel Evil (aiogram 3 + Postgres). Монорепо
## Структура
```
common/ # общий пакет: config, logging, db, repositories, models, texts
bot/ # сервис бота: handlers, services, keyboards, notifications
common/ # общий пакет: config, logging, db, repositories, models, texts, analytics
bot/ # сервис бота: handlers, services, keyboards, middlewares, notifications
sql/ # миграции Flyway (V__*.sql, R__*.sql)
clickhouse/ # init-скрипты ClickHouse (схема аналитики)
docs/ # документация (docs/events.md — события аналитики)
scripts/ # эксплуатационные скрипты (проверка контура, бэкап событий)
images/ # статичные картинки (приветствие)
deploy/ # материалы деплоя (Caddyfile, инструкции)
tests/ # тесты
@ -21,6 +24,7 @@ docker-compose.yml / docker-compose.prod.yml
- Python 3.12
- PostgreSQL 14+
- ClickHouse 25.3 (аналитика событий; можно выключить — `CLICKHOUSE_ENABLED=false`)
## Настройка
@ -44,9 +48,25 @@ APP_ENV=prod python -m bot # prod
Схема управляется миграциями **Flyway** в `sql/` (`V__*.sql` — versioned, `R__*.sql` — repeatable).
При Docker-развёртывании накат выполняется автоматически сервисом `migrate` перед ботом.
## Аналитика (ClickHouse)
Все значимые действия бота пишутся в ClickHouse в единую таблицу `analytics.events`.
Контракт событий — [`docs/events.md`](docs/events.md) (типы, payload, примеры запросов).
- схема — `clickhouse/init/001_events.sql` (применяется при первой инициализации контейнера);
- клиент и батчинг — `common/analytics/` (запись неблокирующая, сбой ClickHouse не влияет на бота);
- сырые события живут на VPS 7 дней и ежедневно уезжают архивом на домашний сервер,
на VPS остаются агрегаты (`events_daily`, `money_daily`) — см. [DEPLOY.md](DEPLOY.md);
- выключается флагом `CLICKHOUSE_ENABLED=false`.
## Уведомления в чат логов
В `ALERT_CHAT_ID` (если задан) уходят: вход и выход участников, успешные очищения и
ежечасный пинг «бот живой». Отправка не критична: ошибки только пишутся в лог.
## Docker
Локальный запуск (postgres + migrate + bot):
Локальный запуск (postgres + migrate + clickhouse + bot):
```bash
cp .env.example .env # заполнить
@ -71,9 +91,20 @@ black --check .
pytest
```
## Бэкап событий
Сырые события ClickHouse ежедневно выгружаются архивом на домашний сервер (по cron),
на VPS остаются только агрегаты и последние дни сырых данных:
```bash
make backup E=test # ./scripts/clickhouse_backup.sh test
```
Настройка и расписание — см. [`DEPLOY.md`](DEPLOY.md).
## Версионирование
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.3.4`.
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.3.5`.
- Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`).
- Docker-образы тегируются версией (Блок 2), события ClickHouse получают `bot_version` (Блок 3).
- Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`.
@ -90,14 +121,14 @@ pytest
- Переход на «ветки + PR» и защиту ветки — когда появится второй разработчик.
```bash
# обычная работа
# обычная работа: сообщение без префиксов, по существу изменения
git add -A
git commit -m "feat: ..."
git commit -m "Краткое описание изменения"
git push origin master
# выпуск релиза
# 1) поднять __version__ в common/version.py
git commit -am "chore: bump version to X.Y"
git commit -am "Версия X.Y"
git tag -a vX.Y -m "Release vX.Y"
git push origin master --tags
```
@ -109,4 +140,6 @@ git push origin master --tags
## Переменные окружения
См. `.env.example`.
- общие — `.env.example` (включая `CLICKHOUSE_*` и параметры батчинга `ANALYTICS_*`);
- контуры — `env/prod.env.example`, `env/test.env.example`;
- бэкап событий — `env/backup.env.example`.

View File

@ -7,4 +7,4 @@
from __future__ import annotations
__version__ = "0.3.4"
__version__ = "0.3.5"

View File

@ -8,6 +8,48 @@
> Расхождение со списком ниже ловит `tests/test_analytics.py`.
> Добавляешь событие — добавь строку в таблицу.
## Схема в ClickHouse
DDL — `clickhouse/init/001_events.sql` (применяется при первой инициализации контейнера).
- `analytics.events` — сырые события, `MergeTree`, `PARTITION BY toYYYYMMDD(event_time)`,
`ORDER BY (event_time, event_type, user_id)`. Партиционирование по дню выбрано специально:
сырые партиции ежедневно удаляются после бэкапа (дешёвый `DROP PARTITION`, без mutation).
- `analytics.events_daily` — агрегат (`SummingMergeTree`): `day`, `event_type`, `user_id`,
`chat_id`, `events`. Строится материализованным представлением `events_daily_mv`.
- `analytics.money_daily` — агрегат по деньгам: `day`, `event_type`, `user_id`,
`amount_sum`, `events` (из `payload.amount` событий `money_accrual`/`money_spend`).
Агрегаты остаются на VPS навсегда, поэтому активность, топ команд и денежные потоки
доступны и после удаления сырых событий. Запросы к агрегатам суммируют `events`/`amount_sum`
(фон слияний `SummingMergeTree` не гарантирует полную свёртку в любой момент).
## Хранение и бэкап
| Что | Где | Сколько |
| --- | --- | --- |
| Сырые события | VPS, `analytics.events` | последние `BACKUP_GRACE_DAYS` дней (по умолчанию 7) |
| Агрегаты | VPS | постоянно |
| Полная история | домашний сервер, свой ClickHouse | архивы `events_YYYY-MM-DD.parquet.gz` (ежедневно) |
Каждую ночь (по Москве) `scripts/clickhouse_backup.sh` выгружает вчерашнюю партицию,
передаёт её на домашний сервер, импортирует и **сверяет количество строк**; только после
успешной сверки на VPS удаляются партиции старше грейса. Ошибка бэкапа данные не удаляет.
Подробности — `DEPLOY.md`.
## Уведомления в чат логов
Помимо записи в ClickHouse часть событий дублируется сообщением в `ALERT_CHAT_ID`
(если он задан; `0` — выключено). Отправка не критична: ошибки только логируются.
| Уведомление | Когда | Событие в ClickHouse |
| --- | --- | --- |
| Вход участника | кто-то вошёл в основной чат | `join` |
| Выход участника | кто-то вышел из основного чата | `leave` |
| Очищение | успешное `/очищение` (`+опыт`, `+монеты`) | `purification` |
| Пинг «бот живой» | раз в час | `bot_alive` |
| Попытка доступа извне | не участник чата стучится в бота | `access_denied` |
## Поля события
| Поле | Тип | Описание |

View File

@ -7,4 +7,4 @@ from common import __version__
def test_version_is_set():
assert __version__
assert __version__ == "0.3.4"
assert __version__ == "0.3.5"