Files
RemnaWaveBOT/README.md
2026-05-06 15:41:56 +03:00

861 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OREOL VPN Bot
Telegram-бот на `aiogram 3` с интеграцией в `Remnawave API`, локальным кэшем в `MariaDB`, пользовательской панелью, тикетами поддержки, реферальной системой и ручной оплатой с подтверждением админом.
Этот README написан как рабочий handoff-документ: по нему можно поднять проект, понять текущую архитектуру и продолжить разработку в новой сессии без восстановления контекста по кускам.
## Что умеет бот
- Показывает стартовую панель пользователя через `/start`
- Подтягивает аккаунты Remnawave по `telegramId`
- Хранит локальный кэш пользователей и истории подписок в MariaDB
- Поддерживает привязку существующего аккаунта Remnawave по `short_uuid`
- Показывает профиль, статус подписки, срок и трафик
- Работает с реферальными кодами, промокодами и скидкой перед оплатой
- Поддерживает ручную оплату переводом с отправкой чека в бот
- Отправляет чек в отдельный Telegram-канал/топик на проверку
- Даёт админу или модератору подтвердить или отклонить оплату
- После подтверждения создаёт или продлевает доступ в Remnawave
- Поддерживает тикеты в отдельный канал и ответы только от админа/модератора
- Показывает админам отдельную Telegram-панель с метриками и быстрыми действиями
## Текущий сценарий оплаты
Сейчас в проекте не используется Telegram Stars. Оплата работает вручную:
1. Пользователь нажимает `Купить подписку`
2. Бот показывает список тарифов из `PAYMENT_PRODUCT_PLANS`, если они заданы, иначе из `PAYMENT_PLANS`
3. Если у пользователя применён реферальный код или промокод, скидка учитывается до выбора тарифа
4. После выбора тарифа бот показывает реквизиты из `PAYMENT_TRANSFER_TEXT`
5. Пользователь отправляет чек следующим сообщением в бота
6. Бот пересылает чек в review-чат оплаты
7. Админ или модератор нажимает `Подтвердить` или `Отклонить`
8. При подтверждении бот создаёт или продлевает доступ в Remnawave и отправляет пользователю подписку
### Тарифы
Обычные тарифы по срокам задаются одной переменной:
```env
PAYMENT_PLANS=30:250,180:600,365:1000
```
Формат:
- `дни:цена_в_рублях`
- элементы разделяются запятой
Пример выше означает:
- 30 дней = 250 ₽
- 180 дней = 600 ₽
- 365 дней = 1000 ₽
Для разных типов подписки можно включить продуктовые тарифы:
```env
PAYMENT_PRODUCT_PLANS=vpn_white:30:450,white:30:250,vpn:30:200
PAYMENT_VPN_SQUAD_UUIDS=11111111-1111-1111-1111-111111111111
PAYMENT_WHITE_SQUAD_UUIDS=22222222-2222-2222-2222-222222222222
```
Если `PAYMENT_PRODUCT_PLANS` заполнен, пользователь увидит выбор:
- `VPN + Белые списки` — получает UUID из `PAYMENT_VPN_SQUAD_UUIDS` и `PAYMENT_WHITE_SQUAD_UUIDS`
- `Белые списки` — получает только `PAYMENT_WHITE_SQUAD_UUIDS`
- `VPN` — получает только `PAYMENT_VPN_SQUAD_UUIDS`
Формат `PAYMENT_PRODUCT_PLANS`: `код:дни:цена_в_рублях`. Доступные коды: `vpn_white`, `white`, `vpn`. Эти значения также можно менять в админ-настройках бота.
### Логин в Remnawave
Логин создаётся в формате:
```text
Oreol-<telegram_id>-<username>
```
Пример:
```text
Oreol-591220249-slkes
```
Если username отсутствует, используется `first_name`, а если и его нет, то `user`.
Важно:
- логин обрезается до 36 символов, чтобы соответствовать ограничениям Remnawave
- повторная покупка у того же пользователя не должна создавать новый логин, а должна продлевать уже существующий доступ
## Важное замечание по базе
Если таблица `payment_orders` уже была создана старой версией проекта, в ней мог остаться `UNIQUE` на `provision_username`.
Для текущей логики это неверно, потому что один и тот же логин может использоваться в нескольких заказах одного пользователя при продлении.
Правильное состояние:
- в ORM `provision_username` обычный индекс без `UNIQUE`
- в `sql/schema.sql` тоже без `UNIQUE`
Если таблица уже существует в MariaDB, может потребоваться вручную убрать старое уникальное ограничение.
## Архитектура
### Основные слои
- `app/main.py`
- инициализация приложения
- подключение к БД
- создание клиента Remnawave
- создание `SyncService`, `SupportTicketService`, `PaymentService`
- запуск aiogram
- `app/config.py`
- все настройки из `.env`
- парсинг ID админов и модераторов
- парсинг ссылок Telegram вида `https://t.me/c/.../.../...`
- парсинг тарифов оплаты
- `app/services/remnawave_client.py`
- прямой HTTP-клиент к Remnawave API через `httpx`
- `GET /users/by-telegram-id/{telegramId}`
- `GET /users/{uuid}`
- `GET /users/by-short-uuid/{shortUuid}`
- `GET /users/by-username/{username}`
- `POST /users`
- `PATCH /users`
- `POST /users/resolve`
- `GET /users`
- `GET /users/{uuid}/subscription-request-history`
- `app/services/sync_service.py`
- регистрация Telegram-пользователей
- синхронизация пользователей из Remnawave в MariaDB
- привязка аккаунтов по `short_uuid`
- реферальные коды и приглашения
- `app/services/payment_service.py`
- список доступных тарифов
- создание ручного заказа
- расчёт скидки по реферальному коду
- хранение статусов заказа
- создание нового доступа в Remnawave
- продление существующего доступа в Remnawave
- `app/services/support_ticket_service.py`
- создание тикетов
- поиск тикета по support message ID
- отметка, что тикет обработан
- `app/bot/handlers/main.py`
- все команды и callback-и
- стартовая панель
- меню оплаты
- FSM для тикетов, рефкодов и чека оплаты
- приём ответа модератора на тикет
- приём кнопок `Подтвердить / Отклонить` в review-канале оплаты
- `app/bot/ui/panel.py`
- рендер панели пользователя
- инлайн-кнопки разделов
- тексты профиля, подписки, рефералки, правил и поддержки
- `app/utils/formatters.py`
- форматирование дат
- форматирование трафика
- форматирование карточек доступа
## Структура проекта
```text
telegabot/
├── app/
│ ├── bot/
│ │ ├── handlers/
│ │ │ └── main.py
│ │ └── ui/
│ │ └── panel.py
│ ├── db/
│ │ ├── base.py
│ │ ├── models.py
│ │ └── session.py
│ ├── schemas/
│ │ └── remnawave.py
│ ├── services/
│ │ ├── payment_service.py
│ │ ├── remnawave_client.py
│ │ ├── support_ticket_service.py
│ │ └── sync_service.py
│ ├── utils/
│ │ └── formatters.py
│ ├── config.py
│ └── main.py
├── assets/
│ └── main.png
├── sql/
│ └── schema.sql
├── tests/
│ ├── test_config.py
│ ├── test_formatters.py
│ ├── test_panel_ui.py
│ └── test_remnawave_client.py
├── .env.example
├── docker-compose.yml
├── Dockerfile
├── main.py
├── pyproject.toml
├── README.md
└── run.bat
```
## Команды бота
### Пользовательские
- `/start` — открыть стартовую панель
### Админские
- `Админ-панель` в UI — сводка по боту, поиск пользователя, полная синхронизация, быстрые переходы в рабочие чаты
- `/lookup <uuid|id|username|short_uuid>` — найти и синхронизировать пользователя
- `/sync_all` — массовая синхронизация пользователей Remnawave в MariaDB
## База данных
### Основные таблицы
- `telegram_users`
- Telegram-пользователи, взаимодействовавшие с ботом
- `remnawave_users`
- локальный кэш пользователей Remnawave
- `internal_squads`
- справочник внутренних групп
- `remnawave_user_internal_squads`
- связь many-to-many пользователей и групп
- `subscription_request_logs`
- история запросов подписки
- `support_tickets`
- тикеты поддержки
- `referral_codes`
- персональные коды пользователей
- `referral_invites`
- кто кого пригласил
- `referral_bonuses`
- начисленные и ожидающие применения бонусы реферерам
- `promo_codes`
- промокоды, скидка, срок действия и ограничение по тарифу
- `promo_code_applications`
- активный промокод, который пользователь ввёл перед оплатой
- `payment_orders`
- ручные платежи, статусы и выданные доступы
### Статусы заказов оплаты
Сейчас используются:
- `PENDING` — заказ создан, чек ещё не отправлен
- `REVIEW` — чек отправлен на проверку
- `REJECTED` — платёж отклонён модератором
- `FULFILLED` — доступ выдан или продлён
## Реферальная система
Что реализовано:
- каждому пользователю создаётся персональный код
- можно сгенерировать ссылку вида `https://t.me/<bot_username>?start=ref_<CODE>`
- сам переход по ссылке скидку не активирует
- чтобы получить скидку, пользователь должен вручную ввести код до оплаты
- размер скидки задаётся в `REFERRAL_DISCOUNT_PERCENT`
- за каждую подтверждённую оплату по рефкоду реферер получает бонус в днях
- размер бонуса задаётся в `REFERRAL_BONUS_DAYS`
### Промокоды
Админ может создать промокод из UI: `Админ-панель` -> `Промокоды` -> `Создать промокод`.
Сценарий создания:
1. Админ вводит слово промокода
2. Выбирает область действия: все тарифы или один тариф
3. Вводит процент скидки
4. Вводит срок действия
Пользователь вводит промокод в том же разделе, где вводится реферальный код. Если промокод ограничен одним тарифом, скидка применяется только к этому тарифу. Если у пользователя есть и реферальное приглашение, и активный промокод, при оплате используется скидка промокода; реферальный бонус рефереру всё равно начислится после подтверждённой оплаты.
Форматы срока действия:
- `7` — 7 дней
- `24h` или `24ч` — 24 часа
- `2026-05-31` — до конца указанной даты
- `0` — без срока действия
## Тикеты поддержки
Сценарий:
1. Пользователь открывает раздел `Поддержка`
2. Нажимает `Создать тикет`
3. Отправляет следующим сообщением вопрос или описание проблемы
4. Бот отправляет тикет в support-чат
5. Админ или модератор отвечает реплаем на сообщение с тикетом
6. Бот доставляет ответ пользователю в личный чат
Доступ к ответу на тикет есть только у:
- пользователей из `BOT_ADMIN_IDS`
- пользователей из `BOT_MODERATOR_IDS`
## Переменные окружения
Полный шаблон лежит в `.env.example`.
Критический минимум для запуска:
- `BOT_TOKEN`
- `REMNAWAVE_BASE_URL`
- `REMNAWAVE_API_TOKEN`
- `DB_HOST`
- `DB_PORT`
- `DB_NAME`
- `DB_USER`
- `DB_PASSWORD`
- `PAYMENT_INTERNAL_SQUAD_UUIDS`
Для полного сценария поддержки и оплаты также нужны:
- `BOT_PUBLIC_USERNAME`
- `BOT_SUPPORT_TICKET_LINK` или `BOT_SUPPORT_TICKET_CHAT_ID`
- `PAYMENT_TRANSFER_TEXT`
- `PAYMENT_REVIEW_LINK` или `PAYMENT_REVIEW_CHAT_ID`
## Как настроить `.env`
1. Скопировать шаблон:
```bat
copy .env.example .env
```
2. Заполнить обязательные значения
3. Проверить:
- корректен ли `BOT_TOKEN`
- доступен ли `REMNAWAVE_BASE_URL`
- валиден ли `REMNAWAVE_API_TOKEN`
- существует ли MariaDB и есть ли права на создание БД, если `CREATE_DATABASE_ON_START=true`
- указаны ли реальные UUID групп в `PAYMENT_INTERNAL_SQUAD_UUIDS`
- указаны ли реальные реквизиты в `PAYMENT_TRANSFER_TEXT`
## Пример важных настроек
```env
BOT_BRAND_NAME=OREOL VPN
BOT_PUBLIC_USERNAME=oreol_vpn_bot
DB_HOST=127.0.0.1
DB_PORT=3306
DB_NAME=oreolvpn
DB_USER=oreolvpn
DB_PASSWORD=strong_password
PAYMENT_PRODUCT_PLANS=vpn_white:30:450,white:30:250,vpn:30:200
PAYMENT_TRANSFER_TEXT=Карта 0000 0000 0000 0000; банк OREOL; получатель OREOL VPN
PAYMENT_REVIEW_LINK=https://t.me/c/3646494169/56/57
PAYMENT_VPN_SQUAD_UUIDS=11111111-1111-1111-1111-111111111111
PAYMENT_WHITE_SQUAD_UUIDS=22222222-2222-2222-2222-222222222222
REFERRAL_DISCOUNT_PERCENT=5
PAYMENT_USERNAME_PREFIX=Oreol
```
## Поддержка Telegram-ссылок `t.me/c/...`
Проект умеет разбирать приватные ссылки Telegram вида:
```text
https://t.me/c/3646494169/56/57
```
Разбор происходит так:
- chat id = `-1003646494169`
- thread id = `56`
- последний сегмент `57` — это message id, для маршрутизации топика он не нужен
Это используется для:
- `BOT_SUPPORT_TICKET_LINK`
- `PAYMENT_REVIEW_LINK`
## Как запускать
### Вариант 1. Windows через `run.bat`
```bat
run.bat
```
Что делает батник:
- переходит в папку проекта
- проверяет `.env`
- создаёт `.venv`, если его нет
- ставит зависимости
- запускает `main.py`
### Вариант 2. Ручной локальный запуск
```bat
py -3 -m venv .venv
.venv\Scripts\python -m pip install -e .[dev]
.venv\Scripts\python main.py
```
### Вариант 3. Docker
```bash
docker compose up --build
```
Это полный запуск вместе с MariaDB. База данных уже есть в проекте: сервис `db` описан в `docker-compose.yml`, данные сохраняются в volume `mariadb_data`.
#### Что нужно установить заранее
- Docker Desktop на Windows/macOS
- или Docker Engine + Docker Compose plugin на Linux
Проверьте, что команды доступны:
```bash
docker --version
docker compose version
```
#### Ubuntu Server 24.04: установка Docker Engine
Для `Ubuntu Server 24.04` рекомендуется ставить Docker Engine из официального Docker-репозитория.
1. Обновить пакеты и поставить базовые утилиты:
```bash
sudo apt update
sudo apt install -y ca-certificates curl git
```
2. Добавить официальный GPG-ключ Docker:
```bash
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
```
3. Подключить Docker repository:
```bash
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
```
4. Установить Docker Engine и Compose plugin:
```bash
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
5. Включить Docker после перезагрузки:
```bash
sudo systemctl enable docker --now
```
6. Разрешить запуск Docker без `sudo`:
```bash
sudo usermod -aG docker $USER
```
После этого выйдите из SSH-сессии и зайдите снова, либо выполните:
```bash
newgrp docker
```
7. Проверить установку:
```bash
docker --version
docker compose version
docker ps
```
Если проект будет стоять на VPS или выделенном сервере, этого достаточно: отдельный Docker Desktop на Linux Server не нужен.
#### Что нужно подготовить перед первым запуском
1. Скопировать пример конфига:
```bash
cp .env.example .env
```
На Windows PowerShell:
```powershell
Copy-Item .env.example .env
```
2. Открыть `.env` и заполнить обязательные значения:
- `BOT_TOKEN`
- `BOT_ADMIN_IDS`
- `REMNAWAVE_BASE_URL`
- `REMNAWAVE_API_TOKEN`
- `DB_NAME`
- `DB_USER`
- `DB_PASSWORD`
- `DB_ROOT_PASSWORD`
- `PAYMENT_INTERNAL_SQUAD_UUIDS`
Обычно ещё сразу настраивают:
- `BOT_PUBLIC_USERNAME`
- `BOT_SUPPORT_URL`
- `BOT_SUPPORT_TICKET_LINK` или `BOT_SUPPORT_TICKET_CHAT_ID`
- `PAYMENT_TRANSFER_TEXT`
- `PAYMENT_REVIEW_LINK` или `PAYMENT_REVIEW_CHAT_ID`
#### Важный момент по базе данных в Docker
При запуске через `docker compose` контейнер бота сам получает:
```env
DB_HOST=db
DB_PORT=3306
```
Это уже задано в `docker-compose.yml`. В `.env` для Docker главное задать:
- `DB_NAME`
- `DB_USER`
- `DB_PASSWORD`
- `DB_ROOT_PASSWORD`
Если вы запускаете проект только в Docker, `DB_HOST` и `DB_PORT` можно не менять вручную.
`DB_ROOT_PASSWORD` нужен только контейнеру MariaDB. Сам бот подключается к базе исключительно через:
- `DB_USER`
- `DB_PASSWORD`
Это важно:
- `DB_ROOT_PASSWORD` не используется приложением для SQLAlchemy-подключения
- если в `.env` указать `DB_USER=root`, но оставить `DB_PASSWORD` пустым, бот попытается зайти как `root` без пароля и упадёт с `Access denied`
Для production рекомендуется не использовать `root` для бота, а создать отдельного пользователя БД через:
- `DB_USER`
- `DB_PASSWORD`
MariaDB в текущем `docker-compose.yml` не публикуется наружу на хост, чтобы база не торчала во внешний интернет по `3306`. Бот подключается к ней по внутренней Docker-сети через hostname `db`.
#### Первый запуск
Запустите сборку и контейнеры:
```bash
docker compose up -d --build
```
Будут подняты два сервиса:
- `db` — MariaDB 11.7
- `bot` — приложение на Python
Полный сценарий для Ubuntu Server обычно выглядит так:
```bash
git clone <URL_репозитория> telegabot
cd telegabot
cp .env.example .env
nano .env
docker compose up -d --build
```
#### Как проверить, что всё поднялось
Посмотреть список контейнеров:
```bash
docker compose ps
```
Посмотреть логи бота:
```bash
docker compose logs -f bot
```
Посмотреть логи базы:
```bash
docker compose logs -f db
```
Проверить, что бот видит базу и база отвечает:
```bash
docker compose exec db mariadb -u"$DB_USER" -p"$DB_PASSWORD" -e "SHOW DATABASES;"
```
Если нужно зайти в MariaDB вручную:
```bash
docker compose exec db mariadb -u"$DB_USER" -p"$DB_PASSWORD" "$DB_NAME"
```
Если всё настроено правильно, сценарий такой:
1. поднимается `db`
2. healthcheck MariaDB становится `healthy`
3. стартует `bot`
4. бот при старте создаёт БД и таблицы, если включены:
- `CREATE_DATABASE_ON_START=true`
- `CREATE_TABLES_ON_START=true`
#### Остановка и повторный запуск
Остановить контейнеры:
```bash
docker compose down
```
Запустить снова без пересборки:
```bash
docker compose up -d
```
Пересобрать после изменения кода:
```bash
docker compose up -d --build
```
#### Как удалить всё вместе с базой
Если нужен полный сброс, включая MariaDB volume:
```bash
docker compose down -v
```
После этого база будет создана заново при следующем запуске.
#### Обновление на Ubuntu Server
Если вы обновили код проекта:
```bash
git pull
docker compose up -d --build
```
Если меняли только `.env`, обычно достаточно:
```bash
docker compose up -d
```
#### Если бот не стартует
Проверьте по пунктам:
- заполнен ли `BOT_TOKEN`
- доступен ли `REMNAWAVE_BASE_URL`
- корректен ли `REMNAWAVE_API_TOKEN`
- заполнен ли `PAYMENT_INTERNAL_SQUAD_UUIDS`
- не пустые ли `DB_PASSWORD` и `DB_ROOT_PASSWORD`
- есть ли у сервиса `db` статус `healthy` в `docker compose ps`
Если видите ошибку вида:
```text
Access denied for user 'root' ... (using password: NO)
```
проверьте:
- не стоит ли в `.env` `DB_USER=root`
- не пустой ли `DB_PASSWORD`
- совпадают ли `DB_USER` и `DB_PASSWORD` с пользователем MariaDB, который был создан при первом старте контейнера
Если контейнер `db` уже один раз поднимался с неправильными DB-переменными, volume MariaDB мог сохранить старого пользователя и старый пароль. Тогда есть два варианта:
- если данных ещё не жалко: `docker compose down -v` и затем `docker compose up -d --build`
- если данные нужно сохранить: зайти в MariaDB и вручную создать/выдать права нужному пользователю
Если хотите открыть MariaDB наружу для внешнего клиента, это нужно делать осознанно: добавьте `ports` обратно в сервис `db` и отдельно ограничьте доступ через firewall или private network.
#### Минимальный сценарий запуска в Docker
1. Скопировать `.env.example` в `.env`
2. Заполнить Telegram, Remnawave и DB-переменные
3. Выполнить `docker compose up -d --build`
4. Проверить `docker compose logs -f bot`
## Проверка проекта
### Синтаксис
```bat
py -3 -m compileall app main.py tests
```
### Тесты
```bat
.venv\Scripts\python -m pytest
```
Если основная `.venv` невалидна из-за переезда проекта между машинами, пересоздай её.
## Что уже протестировано
Покрыто тестами:
- парсинг конфигурации
- разбор support/review ссылок Telegram
- парсинг тарифов
- форматтеры текста
- UI панели
- базовые методы `RemnawaveApiClient`
Последняя локальная проверка в этой сетевой копии:
- `py -3 -m compileall app main.py tests` — успешно
- `pytest``26 passed`
## Известные нюансы
### 1. Перенос `.venv` между машинами
Если скопировать проект вместе с уже готовой `.venv` в другую папку, на другой компьютер или на сетевой диск, окружение может ссылаться на старый путь Python.
Типичная ошибка:
```text
did not find executable at ...python.exe
```
Решение:
1. удалить `.venv`
2. создать её заново
3. снова установить зависимости
### 2. Ошибка `Unknown database`
Если MariaDB доступна, но самой базы из `DB_NAME` ещё нет, можно увидеть:
```text
OperationalError: (1049, "Unknown database '...'" )
```
Что делать:
- оставить `CREATE_DATABASE_ON_START=true`, если у пользователя есть права на создание БД
- либо создать БД вручную
### 3. Прокси Telegram
Для `TELEGRAM_PROXY_URL` подходят обычные proxy URL:
- `http://user:pass@host:port`
- `socks5://host:port`
Не подходят клиентские ссылки вроде:
- `vless://...`
- `vmess://...`
- `trojan://...`
- `tg://proxy?...` для aiogram напрямую
### 4. Review-чат оплаты должен быть настроен
Если `PAYMENT_REVIEW_LINK` и `PAYMENT_REVIEW_CHAT_ID` пустые, пользователь сможет выбрать тариф, но бот не сможет отправить чек на модерацию.
### 5. Internal squads обязательны для выдачи доступа
Если `PAYMENT_INTERNAL_SQUAD_UUIDS` пустой, бот не сможет корректно создать доступ в Remnawave.
## Безопасность
Нельзя публиковать:
- реальный `.env`
- `BOT_TOKEN`
- `REMNAWAVE_API_TOKEN`
- `REMNAWAVE_CADDY_API_KEY`
- банковские реквизиты из рабочего `PAYMENT_TRANSFER_TEXT`
Передавать между сессиями безопасно:
- код проекта
- `README.md`
- `.env.example`
## Что логично делать дальше
Следующие разумные шаги по проекту:
- добавить миграции через Alembic
- хранить отдельный audit log по модерации оплат
- сделать уведомление модераторам о новых чеках
- добавить историю заказов пользователя в панели
- сделать админ-панель со списком платежей
- добавить продление конкретного существующего аккаунта по выбору, если у пользователя их несколько
- добавить отдельный статус `APPROVED_BUT_NOT_DELIVERED`, если бот не смог отправить результат пользователю
- покрыть тестами `PaymentService` и FSM оплаты
## Быстрый handoff
Если проект открывает новая сессия, порядок такой:
1. Прочитать этот README
2. Проверить `.env`
3. Проверить, рабочая ли `.venv`
4. Убедиться, что доступны MariaDB и Remnawave
5. Запустить `run.bat` или `python main.py`
6. Проверить руками:
- `/start`
- открытие раздела подписки через кнопку в панели
- ввод реферального кода или промокода
- отправку чека
- подтверждение оплаты модератором
- выдачу ссылки пользователю
- поддержку через тикет