2026-07-09 16:45:17 +03:00
add
2026-07-09 16:45:17 +03:00
2026-05-20 10:11:47 +03:00
add
2026-07-09 16:45:17 +03:00
2026-05-20 10:11:47 +03:00
add
2026-07-09 16:45:17 +03:00
2026-04-22 18:34:13 +03:00
add
2026-07-09 16:45:17 +03:00
add
2026-07-09 16:45:17 +03:00
2026-04-22 18:34:13 +03:00
add
2026-07-09 16:45:17 +03:00
add
2026-07-09 16:45:17 +03:00
2026-04-22 18:34:13 +03:00
2026-04-22 18:34:13 +03:00
2026-04-22 18:34:13 +03:00
add
2026-07-09 16:45:17 +03:00
2026-04-22 18:34:13 +03:00

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 и отправляет пользователю подписку

Тарифы

Тарифы задаются одной переменной:

PAYMENT_PLANS=30:250,180:600,365:1000

Формат:

  • дни:цена_в_рублях
  • элементы разделяются запятой

Пример выше означает:

  • 30 дней = 250 ₽
  • 180 дней = 600 ₽
  • 365 дней = 1000 ₽

Логин в Remnawave

Логин создаётся в формате:

Oreol-<telegram_id>-<username>

Пример:

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

    • форматирование дат
    • форматирование трафика
    • форматирование карточек доступа

Структура проекта

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

    • начисленные и ожидающие применения бонусы реферерам
  • payment_orders

    • ручные платежи, статусы и выданные доступы

Статусы заказов оплаты

Сейчас используются:

  • PENDING — заказ создан, чек ещё не отправлен
  • REVIEW — чек отправлен на проверку
  • REJECTED — платёж отклонён модератором
  • FULFILLED — доступ выдан или продлён

Реферальная система

Что реализовано:

  • каждому пользователю создаётся персональный код
  • можно сгенерировать ссылку вида https://t.me/<bot_username>?start=ref_<CODE>
  • сам переход по ссылке скидку не активирует
  • чтобы получить скидку, пользователь должен вручную ввести код до оплаты
  • размер скидки задаётся в 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. Скопировать шаблон:
copy .env.example .env
  1. Заполнить обязательные значения

  2. Проверить:

  • корректен ли BOT_TOKEN
  • доступен ли REMNAWAVE_BASE_URL
  • валиден ли REMNAWAVE_API_TOKEN
  • существует ли MariaDB и есть ли права на создание БД, если CREATE_DATABASE_ON_START=true
  • указаны ли реальные UUID групп в PAYMENT_INTERNAL_SQUAD_UUIDS
  • указаны ли реальные реквизиты в PAYMENT_TRANSFER_TEXT

Пример важных настроек

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 вида:

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

run.bat

Что делает батник:

  • переходит в папку проекта
  • проверяет .env
  • создаёт .venv, если его нет
  • ставит зависимости
  • запускает main.py

Вариант 2. Ручной локальный запуск

py -3 -m venv .venv
.venv\Scripts\python -m pip install -e .[dev]
.venv\Scripts\python main.py

Вариант 3. Docker

docker compose up --build

Это полный запуск вместе с MariaDB. База данных уже есть в проекте: сервис db описан в docker-compose.yml, данные сохраняются в volume mariadb_data.

Что нужно установить заранее

  • Docker Desktop на Windows/macOS
  • или Docker Engine + Docker Compose plugin на Linux

Проверьте, что команды доступны:

docker --version
docker compose version

Ubuntu Server 24.04: установка Docker Engine

Для Ubuntu Server 24.04 рекомендуется ставить Docker Engine из официального Docker-репозитория.

  1. Обновить пакеты и поставить базовые утилиты:
sudo apt update
sudo apt install -y ca-certificates curl git
  1. Добавить официальный GPG-ключ Docker:
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
  1. Подключить Docker repository:
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
  1. Установить Docker Engine и Compose plugin:
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  1. Включить Docker после перезагрузки:
sudo systemctl enable docker --now
  1. Разрешить запуск Docker без sudo:
sudo usermod -aG docker $USER

После этого выйдите из SSH-сессии и зайдите снова, либо выполните:

newgrp docker
  1. Проверить установку:
docker --version
docker compose version
docker ps

Если проект будет стоять на VPS или выделенном сервере, этого достаточно: отдельный Docker Desktop на Linux Server не нужен.

Что нужно подготовить перед первым запуском

  1. Скопировать пример конфига:
cp .env.example .env

На Windows PowerShell:

Copy-Item .env.example .env
  1. Открыть .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 контейнер бота сам получает:

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.

Первый запуск

Запустите сборку и контейнеры:

docker compose up -d --build

Будут подняты два сервиса:

  • db — MariaDB 11.7
  • bot — приложение на Python

Полный сценарий для Ubuntu Server обычно выглядит так:

git clone <URL_репозитория> telegabot
cd telegabot
cp .env.example .env
nano .env
docker compose up -d --build

Как проверить, что всё поднялось

Посмотреть список контейнеров:

docker compose ps

Посмотреть логи бота:

docker compose logs -f bot

Посмотреть логи базы:

docker compose logs -f db

Проверить, что бот видит базу и база отвечает:

docker compose exec db mariadb -u"$DB_USER" -p"$DB_PASSWORD" -e "SHOW DATABASES;"

Если нужно зайти в MariaDB вручную:

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

Остановка и повторный запуск

Остановить контейнеры:

docker compose down

Запустить снова без пересборки:

docker compose up -d

Пересобрать после изменения кода:

docker compose up -d --build

Как удалить всё вместе с базой

Если нужен полный сброс, включая MariaDB volume:

docker compose down -v

После этого база будет создана заново при следующем запуске.

Обновление на Ubuntu Server

Если вы обновили код проекта:

git pull
docker compose up -d --build

Если меняли только .env, обычно достаточно:

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

Если видите ошибку вида:

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

Проверка проекта

Синтаксис

py -3 -m compileall app main.py tests

Тесты

.venv\Scripts\python -m pytest

Если основная .venv невалидна из-за переезда проекта между машинами, пересоздай её.

Что уже протестировано

Покрыто тестами:

  • парсинг конфигурации
  • разбор support/review ссылок Telegram
  • парсинг тарифов
  • форматтеры текста
  • UI панели
  • базовые методы RemnawaveApiClient

Последняя локальная проверка в этой сетевой копии:

  • py -3 -m compileall app main.py tests — успешно
  • pytest26 passed

Известные нюансы

1. Перенос .venv между машинами

Если скопировать проект вместе с уже готовой .venv в другую папку, на другой компьютер или на сетевой диск, окружение может ссылаться на старый путь Python.

Типичная ошибка:

did not find executable at ...python.exe

Решение:

  1. удалить .venv
  2. создать её заново
  3. снова установить зависимости

2. Ошибка Unknown database

Если MariaDB доступна, но самой базы из DB_NAME ещё нет, можно увидеть:

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
    • открытие раздела подписки через кнопку в панели
    • ввод реферального кода
    • отправку чека
    • подтверждение оплаты модератором
    • выдачу ссылки пользователю
    • поддержку через тикет
Description
No description provided
Readme 6.8 MiB
Languages
Python 99.5%
Batchfile 0.5%