DispelEvilBot/docs/events.md

8.7 KiB
Raw Permalink Blame History

События аналитики (ClickHouse)

Бот пишет все значимые действия в ClickHouse — единую широкую таблицу analytics.events. Дашборд (Superset) и ETL — отдельный финальный этап; здесь описан контракт событий: какие бывают event_type и что лежит в payload.

Источник правды в коде — common/analytics/events.py (EventType). Расхождение со списком ниже ловит 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 (ежедневно)

Каждую ночь (по Москве) домашний сервер сам забирает вчерашнюю партицию с VPS (pull по ssh), импортирует её в свой ClickHouse и сверяет количество строк; только после успешной сверки он просит VPS удалить партиции старше грейса. Ошибка бэкапа данные не удаляет. Подробности — DEPLOY.md и deploy/ssh-access.md.

Уведомления в чат логов

Помимо записи в ClickHouse часть событий дублируется сообщением в ALERT_CHAT_ID (если он задан; 0 — выключено). Отправка не критична: ошибки только логируются.

Уведомление Когда Событие в ClickHouse
Вход участника кто-то вошёл в основной чат join
Выход участника кто-то вышел из основного чата leave
Очищение успешное /очищение (+опыт, +монеты) purification
Пинг «бот живой» раз в час bot_alive
Попытка доступа извне не участник чата стучится в бота access_denied

Поля события

Поле Тип Описание
event_time DateTime('UTC') Момент события (UTC, без таймзоны)
event_type LowCardinality(String) Тип события (см. каталог ниже)
user_id Int64 Telegram-ID пользователя (0 — системное событие)
username String @username на момент события (может быть пустым)
chat_id Int64 ID чата (обычно основной чат клана)
payload String Дополнительные данные события в виде JSON
bot_version String Версия бота из common/version.py
prod UInt8 1 — прод-контур, 0 — тестовый

Каталог событий

Тип события Когда пишется payload
message Любое сообщение в основном чате message_id, chat_type, content_type, text_len, is_command
command Использование команды (/инфо, /очищение, …) command, args_count
purification Ежедневное очищение (/очищение) success, exp_add, money_add
exp_accrual Начисление опыта за очищение amount, streak
level_up Повышение уровня по итогам очищения level_from, level_to
money_accrual Начисление валюты (монетка за активность, награда за очищение) amount, reason, action_id
money_spend Трата валюты (покупка, обнуление баланса при удалении) amount, reason, product_id
eggs Получение яичек (/яички) amount
purchase_title Покупка звания title_id, price, success
purchase_donat Покупка доната donat_id, price, success
join Вход участника в основной чат chat_title
leave Выход/исключение участника chat_title, kicked, by_id, by_username
nick_set Смена ника (/ник) nick
subscribe Подписка на рассылки (/подписка) —
unsubscribe Отписка от рассылок (/отписка) —
access_denied Отказ в доступе (не участник чата и не админ) reason
bot_start Старт бота app_env
bot_stop Остановка бота app_env, uptime_seconds
bot_alive Ежечасный пинг «бот живой» app_env, uptime_seconds
error Необработанная ошибка при обработке апдейта error_type, error, update_type

Поведение при недоступности ClickHouse

Аналитика — некритичная подсистема: её сбой не должен влиять на бота.

  • события копятся в буфере и отправляются батчами (по размеру и по таймеру);
  • если ClickHouse недоступен, буфер не растёт бесконечно: при переполнении самые старые записи отбрасываются со счётчиком в логе;
  • если записать батч не удалось, события выводятся в stdout (WARNING, первые несколько строк + общее количество) и отбрасываются — данные не пропадают молча;
  • при остановке бота выполняется финальный сброс буфера;
  • выключение целиком — CLICKHOUSE_ENABLED=false (тогда record() — no-op).

Примеры запросов

-- активность по дням
SELECT toDate(event_time) AS day, count() FROM analytics.events GROUP BY day ORDER BY day;

-- топ команд за неделю
SELECT JSONExtractString(payload, 'command') AS cmd, count() AS uses
FROM analytics.events
WHERE event_type = 'command' AND event_time >= now() - INTERVAL 7 DAY
GROUP BY cmd ORDER BY uses DESC;

-- покупки за месяц
SELECT event_type, count(), sum(JSONExtractInt(payload, 'price'))
FROM analytics.events
WHERE event_type IN ('purchase_title', 'purchase_donat') AND event_time >= now() - INTERVAL 30 DAY
GROUP BY event_type;

-- ошибки
SELECT event_time, JSONExtractString(payload, 'error_type'), JSONExtractString(payload, 'error')
FROM analytics.events WHERE event_type = 'error' ORDER BY event_time DESC LIMIT 50;