# 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_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 ₽ ### Логин в Remnawave Логин создаётся в формате: ```text Oreol-- ``` Пример: ```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 ` — найти и синхронизировать пользователя - `/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` - начисленные и ожидающие применения бонусы реферерам - `payment_orders` - ручные платежи, статусы и выданные доступы ### Статусы заказов оплаты Сейчас используются: - `PENDING` — заказ создан, чек ещё не отправлен - `REVIEW` — чек отправлен на проверку - `REJECTED` — платёж отклонён модератором - `FULFILLED` — доступ выдан или продлён ## Реферальная система Что реализовано: - каждому пользователю создаётся персональный код - можно сгенерировать ссылку вида `https://t.me/?start=ref_` - сам переход по ссылке скидку не активирует - чтобы получить скидку, пользователь должен вручную ввести код до оплаты - размер скидки задаётся в `REFERRAL_DISCOUNT_PERCENT` - за каждую подтверждённую оплату по рефкоду реферер получает бонус в днях - размер бонуса задаётся в `REFERRAL_BONUS_DAYS` ## Тикеты поддержки Сценарий: 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_PLANS=30:250,180:600,365:1000 PAYMENT_TRANSFER_TEXT=Карта 0000 0000 0000 0000; банк OREOL; получатель OREOL VPN PAYMENT_REVIEW_LINK=https://t.me/c/3646494169/56/57 PAYMENT_INTERNAL_SQUAD_UUIDS=11111111-1111-1111-1111-111111111111 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 < 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` Если хотите открыть 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` - открытие раздела подписки через кнопку в панели - ввод реферального кода - отправку чека - подтверждение оплаты модератором - выдачу ссылки пользователю - поддержку через тикет