# События аналитики (ClickHouse) Бот пишет все значимые действия в ClickHouse — единую широкую таблицу `analytics.events`. Дашборд (Superset) и ETL — отдельный финальный этап; здесь описан **контракт** событий: какие бывают `event_type` и что лежит в `payload`. > Источник правды в коде — `common/analytics/events.py` (`EventType`). > Расхождение со списком ниже ловит `tests/test_analytics.py`. > Добавляешь событие — добавь строку в таблицу. ## Поля события | Поле | Тип | Описание | | --- | --- | --- | | **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` | | `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). ## Примеры запросов ```sql -- активность по дням 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; ```