> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bedolagam.ru/llms.txt
> Use this file to discover all available pages before exploring further.

# Конкурсы и игры

> Ежедневные мини-игры с призами и реферальные конкурсы

## Обзор

Система конкурсов состоит из двух подсистем:

* **Ежедневные игры** — мини-игры с мгновенными призами (дни подписки, баланс)
* **Реферальные конкурсы** — соревнования по привлечению рефералов с рейтингами

Обе подсистемы управляются общим флагом `CONTESTS_ENABLED`, но работают на разных моделях данных.

```env theme={null}
CONTESTS_ENABLED=true
CONTESTS_BUTTON_VISIBLE=true
```

<Note>
  Участие в конкурсах доступно только пользователям с **активной подпиской** (платной или триальной).
</Note>

## Ежедневные игры

### Архитектура

```
ContestTemplate → ContestRound → ContestAttempt
    (шаблон)       (раунд)       (попытка)
```

* **Шаблон** — определяет тип игры, расписание, призы
* **Раунд** — конкретный экземпляр игры с временным окном
* **Попытка** — одна попытка пользователя (уникальна: один раунд — одна попытка)

### 7 типов игр

| Игра               | Механика            | Ввод   | Описание                               |
| ------------------ | ------------------- | ------ | -------------------------------------- |
| **Quest Buttons**  | Сетка 3x3           | Кнопка | Найти скрытую ноду в сетке             |
| **Lock Hack**      | 20 замков           | Кнопка | Найти единственный открытый замок      |
| **Server Lottery** | 10 флагов стран     | Кнопка | Угадать правильный сервер              |
| **Blitz Reaction** | Одна кнопка         | Кнопка | Кто первый нажмёт — тот победил        |
| **Letter Cipher**  | Числовой шифр       | Текст  | Расшифровать слово (A=1, B=2...)       |
| **Emoji Guess**    | Перемешанные эмодзи | Текст  | Угадать название сервиса по подсказкам |
| **Anagram**        | Перемешанные буквы  | Текст  | Составить слово из букв                |

### Расписание и ротация

Каждый шаблон имеет:

* `schedule_times` — список времён запуска (формат `HH:MM`, через запятую)
* `cooldown_hours` — длительность раунда (по умолчанию 24ч)
* `is_enabled` — вкл/выкл для конкретной игры

Фоновый сервис `ContestRotationService` проверяет расписание каждые 60 секунд. При наступлении времени создаёт новый раунд и:

1. Анонсирует в канале подписки (`CHANNEL_SUB_ID`)
2. Рассылает всем пользователям с активной подпиской

### Призы

| Тип приза | Описание                               |
| --------- | -------------------------------------- |
| `days`    | Продление подписки на N дней           |
| `balance` | Начисление на баланс (в копейках)      |
| `custom`  | Произвольный текст (ручное выполнение) |

### Определение победителя

Используется атомарная проверка с блокировкой строки (`SELECT FOR UPDATE`):

1. Пользователь отправляет ответ
2. Стратегия игры проверяет правильность
3. Если верно — блокируется строка раунда, проверяется `winners_count < max_winners`
4. При победе приз выдаётся автоматически

### Настройки шаблонов по умолчанию

| Игра           | Расписание   | Макс. победителей | Приз   |
| -------------- | ------------ | ----------------- | ------ |
| Quest Buttons  | 10:00, 18:00 | 3                 | 1 день |
| Lock Hack      | 09:00, 19:00 | 1                 | 5 дней |
| Letter Cipher  | 12:00, 20:00 | 1                 | 1 день |
| Server Lottery | 15:00        | 1                 | 7 дней |
| Blitz Reaction | 11:00, 21:00 | 1                 | 1 день |
| Emoji Guess    | 13:00        | 1                 | 1 день |
| Anagram        | 17:00        | 1                 | 1 день |

<Info>
  Все 7 шаблонов создаются автоматически при первом запуске, но **отключены по умолчанию**. Включайте их по мере необходимости.
</Info>

## Реферальные конкурсы

### Как работают

Реферальные конкурсы — это соревнования с ограниченным временем, где пользователи зарабатывают очки за привлечение рефералов.

### Типы конкурсов

| Тип                   | Что считается очком                            |
| --------------------- | ---------------------------------------------- |
| `referral_paid`       | Реферал зарегистрировался **и** купил подписку |
| `referral_registered` | Реферал просто зарегистрировался               |

### Создание конкурса (FSM)

1. Выбор режима: `referral_paid` или `referral_registered`
2. Название конкурса
3. Описание (опционально, `-` для пропуска)
4. Текст призов (опционально)
5. Дата начала (`дд.мм.гггг ЧЧ:ММ`)
6. Дата окончания
7. Время ежедневной сводки (`ЧЧ:ММ` или несколько через запятую)

### Ежедневные сводки

* Отправляются в канал подписки и чат администраторов
* Поддержка нескольких времён в день (`daily_summary_times`)
* По завершении конкурса — финальная сводка (отправляется один раз)
* Содержание: название, топ-5, общее количество рефералов, призы, оставшееся время

### Виртуальные участники

Администраторы могут добавлять "призраков" для создания видимости конкуренции:

* **Индивидуально** — имя + количество рефералов
* **Массово ("Массовка")** — 1-50 призраков с одинаковым количеством рефералов и случайными именами
* В админке отмечены иконкой призрака
* В публичном канале выглядят как обычные участники

### Лидерборд

Лидерборд объединяет реальных участников (из `referral_contest_events`) и виртуальных. Доступен из админки (топ-10) и в ежедневных сводках (топ-5).

## Управление (админ-панель)

### Ежедневные игры

| Действие              | Описание                                                     |
| --------------------- | ------------------------------------------------------------ |
| Список шаблонов       | Все 7 игр с состоянием вкл/выкл                              |
| Вкл/Выкл              | Переключение конкретной игры                                 |
| Запуск раунда         | Немедленный запуск (ручной или с автовключением)             |
| Редактирование        | Тип приза, значение, макс. победителей, расписание, cooldown |
| Сброс попыток         | Сбросить попытки активного раунда                            |
| Закрытие раунда       | Принудительно завершить текущий раунд                        |
| **Массовые операции** | Закрыть все раунды, сбросить все попытки, запустить все      |

### Реферальные конкурсы

| Действие              | Описание                                                |
| --------------------- | ------------------------------------------------------- |
| Список конкурсов      | С пагинацией (5 на страницу)                            |
| Детали                | Статус, период, призы, топ-5, всего событий             |
| Вкл/Выкл              | Переключение активности                                 |
| Статистика            | Участники, рефералы, платные/бесплатные, суммы          |
| Синхронизация         | Синхронизация событий с реальными платежами             |
| Очистка               | Удаление невалидных событий                             |
| Виртуальные участники | Добавление, редактирование, удаление, массовое создание |

## Видимость

Конкурсы имеют многоуровневую систему видимости:

1. **`CONTESTS_ENABLED`** — глобальный переключатель (фоновые сервисы, обработчики)
2. **`CONTESTS_BUTTON_VISIBLE`** — видимость кнопки "Конкурсы" в главном меню
3. **Вкл/выкл шаблона** — каждая ежедневная игра переключается отдельно
4. **Активность конкурса** — каждый реферальный конкурс переключается отдельно
5. **Требование подписки** — только пользователи с активной/триальной подпиской

## Настройка

| Переменная                | По умолчанию | Описание                                   |
| ------------------------- | ------------ | ------------------------------------------ |
| `CONTESTS_ENABLED`        | `false`      | Глобальный переключатель системы конкурсов |
| `CONTESTS_BUTTON_VISIBLE` | `false`      | Показывать кнопку "Конкурсы" в меню        |
| `TIMEZONE`                | `UTC`        | Часовой пояс для расписания                |
| `CHANNEL_SUB_ID`          | —            | ID канала для анонсов                      |

## Cabinet и Web API

### Cabinet (для пользователей)

* `GET /contests/count` — количество доступных (неотыгранных) игр
* `GET /contests` — список доступных игр со статусом
* `GET /contests/{round_id}` — данные игры
* `POST /contests/{round_id}/answer` — отправка ответа

### Web API (для администраторов)

Полный CRUD для шаблонов, раундов, попыток (ежедневные игры) и реферальных конкурсов с аутентификацией по API-токену.
