> ## 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.

# Remnawave

> Интеграция с VPN панелью Remnawave

## Обзор

Бот управляет VPN подписками через API панели [Remnawave](https://github.com/remnawave/backend). Все операции с пользователями, серверами и трафиком проходят через единый API клиент.

### Поддерживаемые версии панели

| Версия панели | Что появилось в боте |
| - | - |
| **3.3.0** | [GeoCheck нод](#geocheck-нод) из админки кабинета (`v4.1.0`) |
| **3.0.0** | Числовая панельная идентичность вместо UUID — **ломающее изменение**, см. [Обновление на Remnawave 3.0.0](#обновление-на-remnawave-300) (`v4.0.0`) |
| **2.8.0** | Обновлённый API-клиент, события `service.api_token_created/deleted`, `user.expiration`; локальная генерация crypt-ссылок Happ (`v3.62.0`, `v3.63.0`) |
| **2.7.0** | `MONTH_ROLLING`, per-inbound метрики, вложенные `versions`/`system`, `torrent_blocker.report`, `subpage_config_changed` |

## Обновление на Remnawave 3.0.0

> [!WARNING]
>
> **Ломающее изменение в боте `v4.0.0`.** В Remnawave 3.0.0 у пользователей панели больше нет прежних UUID — идентичность стала **числовой**. Бот перешёл на неё целиком: клиент API, сервисный слой, хендлеры, миддлвари, Web API и кабинет.

Что нужно сделать при обновлении:

**1. Обновите панель до 3.0.0**

Бот версии `v4.0.0` и новее работает с числовой идентичностью. Обновляйте панель и бота согласованно.

**2. Перепишите TRAFFIC\_EXCLUDED\_USER\_UUIDS**

Переменная переименована в `TRAFFIC_EXCLUDED_USER_IDS`, и формат значений изменился: вместо UUID нужны **числовые id** панельных пользователей. Автоматически сконвертировать нельзя — прежних UUID в панели уже не существует. Возьмите id заново из панели.

```env theme={null}
# Было (до v4.0.0)
TRAFFIC_EXCLUDED_USER_UUIDS=8d0c33bb-...,4a81705d-...

# Стало
TRAFFIC_EXCLUDED_USER_IDS=1042,1087
```

**3. Дождитесь бэкфила**

При первом старте бот проставляет числовые `remnawave_id` существующим подпискам, сверяясь с панелью. Прогон идёт батчами и переживает перезапуск — вручную ничего запускать не нужно.

> [!NOTE]
>
> Бэкфил устроен консервативно: подписка без надёжного соответствия в панели остаётся без `remnawave_id` и попадает в лог, а не привязывается наугад. Такие подписки синхронизируются после того, как соответствие появится.

### Совместимость с Remnawave 2.7.0

Начиная с версии 2.7.0 панели поддерживаются:

* **MONTH\_ROLLING** — стратегия сброса трафика с помесячным скользящим окном
* **Per-inbound метрики** — разбивка трафика по отдельным inbound в реальном времени
* **Обновлённая модель данных нод** — вложенные объекты `versions` / `system` вместо плоских полей
* **Webhook `torrent_blocker.report`** — событие при обнаружении торрент-трафика
* **Webhook `subpage_config_changed`** — автоматическая инвалидация кэша конфигурации приложения при изменении подстраницы

## Настройка подключения

```env theme={null}
REMNAWAVE_API_URL=https://panel.example.com
REMNAWAVE_API_KEY=your_panel_api_key
```

> [!WARNING]
>
> Если бот и панель на одном сервере в Docker, используйте внутренний адрес: `REMNAWAVE_API_URL=http://remnawave:3000`

## Защита панели

Для панелей, защищённых через [remnawave-reverse-proxy](https://github.com/eGamesAPI/remnawave-reverse-proxy):

```env theme={null}
# Для панелей установленных скриптом eGames
REMNAWAVE_SECRET_KEY=XXXXXXX:DDDDDDDD

# Или если ключ и значение одинаковые
REMNAWAVE_SECRET_KEY=secret_key_name
```

## Методы авторизации

| Конфигурация | Authorization | X-Api-Key |
| - | - | - |
| Только API Key | `Bearer <api_key>` | `<api_key>` |
| Basic Auth | `Bearer <api_key>` | `Basic <user:pass>` |
| Caddy + API Key | `Basic <caddy_token>` | `<api_key>` |
| Cookies (eGames) | `Bearer <api_key>` | `<api_key>` + cookies |

## Автосинхронизация

```env theme={null}
REMNAWAVE_AUTO_SYNC_ENABLED=true
REMNAWAVE_AUTO_SYNC_TIMES=03:00,15:00
```

Бот синхронизирует данные с панелью:

* **При старте бота** — полная синхронизация
* **По расписанию** — в указанное время
* **По команде** — через админ-панель

Синхронизируются:

* Серверы (сквады) и их статус
* Пользователи — сопоставление Telegram ID с аккаунтами панели
* Подписки — актуальные даты и параметры

### Двунаправленная синхронизация `end_date`

Панель является авторитетным источником для `end_date` на **активных** подписках. Как webhook-событие `user.modified`, так и пакетная синхронизация обновляют `end_date` в обоих направлениях — вперёд и назад.

Ранее принимались только обновления «вперёд» (дата в панели > локальная дата). Теперь изменения, сделанные администратором в панели (например, сокращение подписки), корректно передаются в бота.

> [!NOTE]
>
> 60-секундная защита между webhook-событием и пакетной синхронизацией по-прежнему предотвращает конфликты.

### Синхронизация в мультитарифном режиме

При `MULTI_TARIFF_ENABLED=true` каждая подписка получает собственного пользователя в панели (`user_{telegram_id}_{short_id}`). Синхронизация работает по `remnawave_uuid` на уровне подписки, а не пользователя.

* **Panel → Bot**: создание подписок для новых пользователей панели
* **Bot → Panel**: matching по суффиксу `_short_id` в username
* **Email/OAuth**: синхронизация всех пользователей панели для email
* **Автопривязка**: legacy user-level UUID автоматически привязывается к подписке при миграции

## Webhooks от панели (real-time события)

Для мгновенной реакции на события подписок вместо периодической синхронизации.

### Настройка

**1. Переменные окружения:**

```env theme={null}
REMNAWAVE_WEBHOOK_ENABLED=true
REMNAWAVE_WEBHOOK_PATH=/remnawave-webhook
REMNAWAVE_WEBHOOK_SECRET=your_secret_min_32_chars_here
```

Сгенерируйте секрет: `openssl rand -hex 32`

> [!NOTE]
>
> **Усиление безопасности webhook-верификации.** Все платёжные провайдеры переведены на fail-closed модель: CryptoBot, Heleket и Tribute отклоняют запросы при отсутствии API-ключа. Для сравнения подписей используется `hmac.compare_digest` (timing-safe). CloudPayments отклоняет запросы при отсутствии заголовка подписи.

**2. В панели Remnawave:**

* URL: `https://hooks.domain.com/remnawave-webhook`
* Secret: тот же, что в `REMNAWAVE_WEBHOOK_SECRET`

**3. Проверка:**

```bash theme={null}
curl -s https://hooks.domain.com/remnawave-webhook | jq
# {"status": "ok", "service": "remnawave_webhook", "enabled": true}
```

### Поддерживаемые события

| Событие | Описание |
| - | - |
| `user.expired` | Подписка истекла |
| `user.disabled` | Подписка деактивирована |
| `user.enabled` | Подписка активирована |
| `user.limited` | Превышен лимит трафика |
| `user.traffic_reset` | Трафик сброшен |
| `user.modified` | Данные подписки изменены |
| `user.deleted` | Пользователь удалён |
| `user.revoked` | Ключи подписки отозваны |
| `user.created` | Пользователь создан |
| `user.expires_in_*` | Предупреждения об истечении (72/48/24ч) |
| `user.first_connected` | Первое подключение |
| `user.bandwidth_usage_threshold_reached` | Порог трафика достигнут |
| `user_hwid_devices.*` | Устройство добавлено/удалено |
| `torrent_blocker.report` | Торрент-блокировщик: обнаружено использование торрентов |
| `subpage_config_changed` | Конфигурация подстраницы изменена (автосброс кэша) |
| `user.expiration` | Истечение подписки (формат Remnawave 2.8.0) |
| `service.api_token_created` | Создан API-токен панели (2.8.0) |
| `service.api_token_deleted` | Удалён API-токен панели (2.8.0) |
| `node.*`, `service.*` | Административные события |

> [!NOTE]
>
> При включённых webhooks бот защищает подписки от перезаписи данными из периодической синхронизации в течение 60 секунд после получения события.

### Уведомления пользователям

При получении webhook-событий бот может отправлять уведомления пользователям в Telegram (или по email для email-only пользователей).

#### Главный переключатель

```env theme={null}
# Включить уведомления пользователям от вебхуков (по умолчанию true)
WEBHOOK_NOTIFY_USER_ENABLED=true
```

Если `false` — ни одно пользовательское уведомление не отправляется, даже если отдельные типы включены.

#### Настройка по типам

Каждый тип уведомления можно включить или выключить отдельно. Все по умолчанию **включены**.

| Переменная | По умолчанию | События | Что получает пользователь |
| - | - | - | - |
| `WEBHOOK_NOTIFY_SUB_STATUS` | `true` | `user.disabled`, `user.enabled` | Подписка отключена / активирована |
| `WEBHOOK_NOTIFY_SUB_EXPIRED` | `true` | `user.expired`, `user.expired_24_hours_ago` | Подписка истекла / истекла вчера |
| `WEBHOOK_NOTIFY_SUB_EXPIRING` | `true` | `user.expires_in_72h/48h/24h` | Подписка истекает через 3/2 дня / завтра |
| `WEBHOOK_NOTIFY_SUB_LIMITED` | `true` | `user.limited` | Лимит трафика исчерпан |
| `WEBHOOK_NOTIFY_TRAFFIC_RESET` | `true` | `user.traffic_reset` | Трафик сброшен, лимит доступен |
| `WEBHOOK_NOTIFY_SUB_DELETED` | `true` | `user.deleted` | Подписка удалена из панели |
| `WEBHOOK_NOTIFY_SUB_REVOKED` | `true` | `user.revoked` | Ключ подписки обновлён |
| `WEBHOOK_NOTIFY_FIRST_CONNECTED` | `true` | `user.first_connected` | Первое подключение к VPN |
| `WEBHOOK_NOTIFY_NOT_CONNECTED` | `true` | `user.not_connected` | Подписка активна, но нет подключения |
| `WEBHOOK_NOTIFY_BANDWIDTH_THRESHOLD` | `true` | `user.bandwidth_usage_threshold_reached` | Использовано N% трафика |
| `WEBHOOK_NOTIFY_DEVICES` | `true` | `user_hwid_devices.added/deleted` | Новое устройство / устройство удалено |

#### Примеры сообщений

**Подписка истекает завтра:**

```
🔴 Подписка истекает завтра!

Осталось менее 24 часов. Продлите подписку прямо сейчас.

[Продлить подписку] [Закрыть]
```

**Лимит трафика исчерпан:**

```
⚠️ Достигнут лимит трафика

Вы исчерпали весь доступный трафик.
Докупите трафик или дождитесь сброса.

[Докупить трафик] [Моя подписка] [Закрыть]
```

**Первое подключение:**

```
🎉 Первое подключение!

Вы впервые подключились к VPN. Добро пожаловать!

[Моя подписка] [Закрыть]
```

#### Тихие события

Некоторые webhook-события обрабатываются **без уведомления** пользователю:

| Событие | Что происходит |
| - | - |
| `user.modified` | Синхронизация данных подписки — панель авторитетна. Обновляет трафик, статус и `end_date` в обоих направлениях (вперёд и назад) |
| `user.created` | Логирование создания пользователя в панели |

> [!NOTE]
>
> Все переменные `WEBHOOK_NOTIFY_*` можно менять **без перезапуска бота** через админ-панель (Настройки).

## Мониторинг серверов

| Переменная | Описание |
| - | - |
| `SERVER_STATUS_MODE` | `disabled`, `external_link`, `external_link_miniapp`, `xray` |
| `SERVER_STATUS_EXTERNAL_URL` | Ссылка на внешний мониторинг |
| `SERVER_STATUS_METRICS_URL` | URL метрик XrayChecker |
| `MONITORING_INTERVAL` | Интервал проверки (секунды, по умолчанию 60) |

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

* Падении сервера (недоступен)
* Высокой нагрузке CPU/RAM
* Аномальном трафике
* Восстановлении после падения

## GeoCheck нод

Проверка географии выхода ноды прямо из админки кабинета (`v4.1.0`). Кнопка в карточке ноды открывает модалку: выбор маршрута (по умолчанию / конкретный IP / интерфейс), ожидание результата, затем отчёт картинкой или тем же отчётом в JSON. Есть копирование JSON, скачивание SVG, повтор проверки и полноэкранный режим.

> [!NOTE]
>
> Требуется **Remnawave Panel и Node 3.3.0+**. Кнопка показывается только для подключённых нод подходящей версии — на старых нодах её просто нет.
>
> Проверка асинхронная: постановка задачи возвращает `job_id`, результат опрашивается отдельно. Нода может отвечать до минуты.

## Имена пользователей в панели

Шаблон имени задаётся `REMNAWAVE_USER_USERNAME_TEMPLATE` (по умолчанию `user_{telegram_id}`).

> [!NOTE]
>
> Начиная с `v3.66.0` кириллица в подставляемых значениях **транслитерируется** — панель не принимает не-ASCII в имени пользователя, и раньше такие пользователи не создавались.

## Happ CryptoLink

Remnawave 2.8.0 удалил серверную генерацию crypt-ссылок (`/api/system/tools/happ/encrypt`). Начиная с `v3.63.0` бот шифрует ссылки **локально** публичным RSA-ключом Happ — так же, как это делает subpage панели (`happ://crypt4/...`). Официальный Happ API (`crypto.happ.su` → `happ://crypt5/...`) остаётся запасным путём.

| Переменная | Описание | По умолчанию |
| - | - | - |
| `HAPP_CRYPTOLINK_LOCAL_ENCRYPTION_ENABLED` | Локальная генерация ссылок | `true` |
| `HAPP_CRYPTOLINK_API_FALLBACK_ENABLED` | Запасной путь через внешний Happ API | `true` |

> [!WARNING]
>
> Выключатели нужны на крайний случай. `HAPP_CRYPTOLINK_LOCAL_ENCRYPTION_ENABLED=false` — если Happ ротирует ключ и до обновления бота ссылки нужно гнать через API. `HAPP_CRYPTOLINK_API_FALLBACK_ENABLED=false` — если обращения к внешнему сервису недопустимы; тогда crypt-ссылки останутся только у тех пользователей, у кого они уже сохранены в базе.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.