diff --git a/README.md b/README.md
index 6d6f0d0..4e7cd6b 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,168 @@
-[English](https://github.com/denpiligrim/3dp-manager/blob/main/README_EN.md)
+[English](/README_EN.md) | [中文](/README_CN.md) | [فارسی](/README_IR.md) | [Türkmençe](/README_TK.md)
+
+

+
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
-> [!WARNING]
-> **Это бета-версия!**
->
-> Программа находится в активной разработке. Возможны баги, нестабильность и изменения API.
-> Используйте с осторожностью.
+Утилита для автогенерации инбаундов к панели [3x-ui](https://github.com/MHSanaei/3x-ui), формирования единой подписки и настройки перенаправления трафика с промежуточного сервера на основной.
-3DP-MANAGER — утилита, которая позволяет регулярно генерировать входящие подключения для панели 3X-UI на основе списка разрешённых доменов (whitelist). Белый список общий, но вы также можете добавить свой собственный список, назвав его `my_whitelist.txt` и поместив его в папку `/opt/3dp-manager/app` на вашем сервере.
-Обсуждения доступны в Телеграм канале: [@denpiligrim_web](https://t.me/denpiligrim_web/719)
+**Поддержать проект**
+
+- Реквизиты / донаты:
+ - Карта МИР: `2204320436318077`
+ - Карта MasterCard: `5395452209474530`
+ - ЮМоney: `4100116897060652`
+ - PayPal: `vasiljevdenisx@gmail.com`
+ - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - Bybit ID: `165292278`
+
+## Описание
+
+Главная цель утилиты — сделать так, чтобы ваш трафик не выглядел одинаковым. Бот генерирует по заданному интервалу 10 подключений с разными параметрами:
+
+- протоколы: `vless`, `vmess`, `shadowsocks`, `trojan`;
+- порты: `443`, `8443` (фиксированные) и случайные из диапазона `10000-60000`;
+- транспорт: `tcp`, `websocket`, `grpc`, `xhttp`;
+- SNI берутся из белого списка доменов (whitelist); можно использовать свой список.
+
+Все подключения объединяются в одну подписку со статичным URL. Бот работает с панелью `3x-ui` и не вмешивается в её работу напрямую, используя открытое API панели.
+
+Вторичная цель — стабильность подключения: клиент получает 10 вариантов поключений и может выбрать любое из них.
+
+Дополнительно: бот можно использовать в каскадной схеме. Сервис перенаправления автоматически настроит переадресацию подписки и трафика к основному серверу.
+
+Рекомендации:
+
+- Используйте HTTPS для подписки (домен + SSL сертификат).
+- Интервал генерации задавайте ≥ 10 минут; для стабильности рекомендуется — раз в сутки (1440 минут).
+- В клиенте установите автообновление чаще (например, каждый час), чтобы была синхронизация с сервером.
+
+## Возможности
+
+- Генерация 10 разнообразных подключений
+- Формирование единой подписки со статичным URL
+- Поддержка кастомного `whitelist` доменов
+- Автоматическая настройка перенаправления трафика (опционально)
+
+## Требования
+
+- Ubuntu 20.04 (и выше)
+- Панель `3x-ui`
+- Домен + SSL сертификат (опционально)
+
+---
+
+## Установка
+
+Установите проект на сервер командой:
-### Установка
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-### Обновление
+Краткое описание: запускает скрипт установки и разворачивает контейнеры и сервисы.
+
+## Обновление
+
+Обновление до последней версии:
+
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
```
-### Удаление
+Краткое описание: подтягивает последние изменения и перезапускает контейнеры.
+
+## Удаление
+
+Полное удаление сервиса:
+
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
```
+Краткое описание: удаляет контейнеры и файлы конфигурации, возвращая систему к состоянию до установки.
+
---
-Сервис подписок и перенаправления входящего трафика работает совместно с **3DP-MANAGER** и позволяет перенаправлять весь входящий трафик с промежуточного сервера на основной сервер. Перенаправляются те же порты: `443`, `8443` и диапазон `10000-60000`. Сервис также создаёт ссылку на подписку, автоматически заменяя IP-адрес или домен в конфигурациях. Перенаправления настраиваются добавлением правил iptables в конфигурационный файл ufw, что обеспечивает стабильную работу в сочетании с файрволом. Рекомендуется устанавливать сервис на чистый сервер без ранее настроенных правил.
+## Установка сервиса перенаправления (forwarding)
+
+Сервис перенаправления позволяет проксировать входящие порты с промежуточного сервера на основной.
-### Установка перенаправления
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
```
-### Удаление перенаправления
+Краткое описание: добавляет правила перенаправления и создает сервис для обновления подписки.
+
+## Удаление перенаправления
+
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
```
+Краткое описание: удаляет правила и отключает сервис перенаправления.
+
---
-### Показать URL подписки
+## Показать URL подписки
+
+Команда для вывода текущего URL подписки из среды контейнера:
+
```bash
cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
```
----
+Краткое описание: выводит статичный URL подписки, который можно использовать в клиентах. Работает как на основном, так и на промежуточном сервере.
+
+## Сбор доменов из мульти-подписок
+
+Утилита извлекает домены из подписок и формирует `whitelist` для генератора.
-### Утилита для сбора доменов из мульти-подписок
-Позволяет собирать домены из подписок с несколькими конфигурациями. В интернете встречается много подобных списков конфигураций, использующих белый SNI. Инструмент позволяет извлечь домены и подготовить готовый список для дальнейшего использования в генераторе входящих подключений.
-Вставьте ссылку на подписку в скрипт и запустите команду в окружении `Node.js`.
```bash
node get_domains.js
```
-### Использовать свой белый список
-Файл должен иметь структуру аналогичную `whitelist.txt`. Переименуйте имя файла на `my_whitelist.txt`. Загрузите файл в папку `/opt/3dp-manager/app` на сервере и выполните команду:
+Краткое описание: добавьте ссылку на мульти-подписку в скрипт и запустите команду — на выходе получите список доменов. Неободим `Node.js` для работы скрипта.
+
+## Использование собственного белого списка
+
+1. Подготовьте файл в формате `whitelist.txt`.
+2. Переименуйте на `my_whitelist.txt` и скопируйте в папку `/opt/3dp-manager/app`.
+
```bash
cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
-```
\ No newline at end of file
+```
+
+Краткое описание: добавляет ваш файл доменов в контейнер приложения. Чтобы сразу же сгенерировать инбаунды с новыи списком, выполните `docker exec -it node sh` и затем `node index.js`.
+
+---
+
+## Замечания и текущие ограничения
+
+- Общий список доменов работает не у всех провайдеров, поэтому рекомендуется составить и использовать свой whitelist.
+
+---
+
+## Внести вклад
+
+Буду рад любому вкладу в разработку проекта! Простой процесс для контрибьюторов:
+
+1. Форкните репозиторий на GitHub.
+2. Создайте ветку с осмысленным именем, например `feature/add-README` или `fix/whitelist-load`.
+3. Внесите изменения и добавьте короткое описание в коммите.
+4. Запустите локально базовые проверки (если есть).
+5. Отправьте ветку в ваш форк и создайте Pull Request в основной репозиторий.
+
+Советы: описывайте изменения в PR, указывайте цель и тестовые шаги. Если изменения большие — разделяйте на маленькие коммиты.
+
+---
+
+## Обсуждение
+
+- Телеграм: [@denpiligrim_web](https://t.me/denpiligrim_web)
+- Раздел Issues в данном репозитории
\ No newline at end of file
diff --git a/README_CN.md b/README_CN.md
new file mode 100644
index 0000000..e4d0b75
--- /dev/null
+++ b/README_CN.md
@@ -0,0 +1,167 @@
+[Русский](/README.md) | [English](/README_EN.md) | [فارسی](/README_IR.md) | [Türkmençe](/README_TK.md)
+
+
+
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+
+# 3DP-MANAGER
+
+这是一个用于为 `3x-ui` 面板自动生成入站连接、生成统一订阅并将流量从中继服务器转发到主服务器的实用工具。
+
+**支持项目**
+
+- 捐助/支付信息:
+ - MIR 卡: `2204320436318077`
+ - MasterCard: `5395452209474530`
+ - PayPal: `vasiljevdenisx@gmail.com`
+ - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - 比特币: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - Bybit ID: `165292278`
+
+## 描述
+
+该工具的主要目标是使您的流量看起来不那么统一。机器人会在设定间隔生成 10 个具有不同参数的连接:
+
+- 协议:`vless`、`vmess`、`shadowsocks`、`trojan`
+- 端口:`443`、`8443`(固定)以及 `10000-60000` 范围内的随机端口
+- 传输:`tcp`、`websocket`、`grpc`、`xhttp`
+- SNI 从域名白名单(`whitelist`)中获取;也可以使用自定义列表
+
+所有连接会合并为具有静态 URL 的单一订阅。该机器人通过 `3x-ui` 面板的公共 API 工作,不会直接修改面板内部。
+
+次要目标是提高连接稳定性:客户端会收到 10 个可选连接,用户可以选择任意一个。
+
+此外,机器人可用于级联部署。转发服务会自动配置将订阅和流量重定向到主服务器。
+
+建议:
+
+- 订阅请使用 HTTPS(域名 + SSL 证书)。
+- 生成间隔设置为 ≥ 10 分钟;为稳定性建议每天一次(1440 分钟)。
+- 在客户端设置更频繁的自动更新(例如每小时),以便与服务器同步。
+
+## 功能
+
+- 生成 10 个多样化连接
+- 形成具有静态 URL 的统一订阅
+- 支持自定义 `whitelist` 域名
+- 可选的自动流量转发配置
+
+## 要求
+
+- Ubuntu 20.04 或更高
+- `3x-ui` 面板
+- 域名 + SSL 证书(可选)
+
+---
+
+## 安装
+
+在服务器上运行以下命令安装项目:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
+```
+
+简要说明:运行安装脚本并部署容器和服务。
+
+## 更新
+
+更新到最新版本:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
+```
+
+简要说明:拉取最新更改并重启容器。
+
+## 删除
+
+彻底删除服务:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
+```
+
+简要说明:删除容器和配置文件,恢复到安装前状态。
+
+---
+
+## 安装转发服务(forwarding)
+
+转发服务允许将中继服务器的入站端口代理到主服务器。
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
+```
+
+简要说明:添加转发规则并创建用于更新订阅的服务。
+
+## 删除转发
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
+```
+
+简要说明:删除规则并禁用转发服务。
+
+---
+
+## 显示订阅 URL
+
+在容器环境中打印当前订阅 URL 的命令:
+
+```bash
+cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
+```
+
+简要说明:打印可在客户端使用的静态订阅 URL,适用于主服务器和中继服务器。
+
+## 从多订阅收集域名
+
+该工具可从订阅中提取域名并为生成器构建 `whitelist`。
+
+```bash
+node get_domains.js
+```
+
+简要说明:在脚本中添加多订阅链接并运行命令 — 输出为域名列表。运行脚本需要 `Node.js`。
+
+## 使用自定义白名单
+
+1. 准备一个与 `whitelist.txt` 相同格式的文件。
+2. 将其重命名为 `my_whitelist.txt` 并复制到 `/opt/3dp-manager/app`。
+
+```bash
+cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
+```
+
+简要说明:将您的域名文件添加到应用容器中。
+
+---
+
+## 注意与当前限制
+
+- 共享的域名列表可能并非对所有提供商都有效;建议准备并使用自定义 `whitelist`。
+
+---
+
+## 贡献
+
+欢迎贡献!简单的贡献流程:
+
+1. 在 GitHub 上 fork 仓库。
+2. 创建有意义的分支名,例如 `feature/add-README` 或 `fix/whitelist-load`。
+3. 提交更改并添加简短的提交信息。
+4. 在本地运行检查(如有)。
+5. 推送分支到您的 fork 并创建 Pull Request。
+
+建议:在 PR 中说明更改和测试步骤;较大更改请拆成小提交。
+
+---
+
+## 讨论
+
+- Telegram: [@denpiligrim_web](https://t.me/denpiligrim_web)
+- Issues
\ No newline at end of file
diff --git a/README_EN.md b/README_EN.md
index 50dd28e..42919be 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -1,62 +1,167 @@
-[Русский](https://github.com/denpiligrim/3dp-manager/blob/main/README.md)
+[Русский](/README.md) | [中文](/README_CN.md) | [فارسی](/README_IR.md) | [Türkmençe](/README_TK.md)
+
+
+
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
# 3DP-MANAGER
-> [!WARNING]
-> **This is a beta version!**
->
-> The program is currently under active development. Bugs, instability, and API changes are possible.
-> Use with caution.
+A utility for auto-generating inbound connections for the [3x-ui](https://github.com/MHSanaei/3x-ui) panel, creating a single subscription URL, and configuring traffic forwarding from an intermediate server to the main server.
-3DP-MANAGER is a utility that allows you to regularly generate inbound connections for the 3X-UI panel based on a whitelist of domains. The whitelist is shared, but you can also add your own list by naming it `my_whitelist.txt` and adding it to the `/opt/3dp-manager/app` folder on your server.
-Discussions are available on the Telegram channel: [@denpiligrim_web](https://t.me/denpiligrim_web/719)
+**Support the project**
+
+- Donation / payment details:
+ - MIR card: `2204320436318077`
+ - MasterCard: `5395452209474530`
+ - PayPal: `vasiljevdenisx@gmail.com`
+ - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - Bybit ID: `165292278`
+
+## Description
+
+The main goal of the utility is to make your traffic appear non-uniform. The bot generates 10 connection entries at a configured interval with varying parameters:
+
+- Protocols: `vless`, `vmess`, `shadowsocks`, `trojan`;
+- Ports: `443`, `8443` (fixed) and random ports from the range `10000-60000`;
+- Transport: `tcp`, `websocket`, `grpc`, `xhttp`;
+- SNI values are taken from a whitelist of domains (`whitelist`); you can use your own list.
+
+All generated connections are combined into a single subscription with a static URL. The bot works with the `3x-ui` panel using its public API and does not directly modify the panel internals.
+
+The secondary goal is connection stability: the client receives 10 alternate connection options and can choose any of them.
+
+Additionally, the bot can be used in a cascading setup. The forwarding service will automatically configure the subscription and traffic redirection to the main server.
+
+Recommendations:
+
+- Use HTTPS for the subscription (domain + SSL certificate).
+- Set the generation interval to ≥ 10 minutes; for stability, once per day (1440 minutes) is recommended.
+- Configure the client to check for updates more frequently (for example, hourly) to stay synchronized with the server.
+
+## Features
+
+- Generates 10 diverse connection entries
+- Creates a single subscription with a static URL
+- Supports custom `whitelist` domains
+- Optional automatic configuration of traffic forwarding
+
+## Requirements
+
+- Ubuntu 20.04 or newer
+- `3x-ui` panel
+- Domain + SSL certificate (optional)
+
+---
+
+## Installation
+
+Install the project on your server with:
-### Install
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
```
-### Update
+Short description: runs the installer script and deploys containers and services.
+
+## Update
+
+Update to the latest version:
+
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
```
-### Delete
+Short description: pulls the latest changes and restarts containers.
+
+## Removal
+
+Remove the service completely:
+
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
```
+Short description: removes containers and configuration files, restoring the system to the pre-install state.
+
---
-The subscription and inbound redirection service works in conjunction with **3DP-MANAGER** and allows you to redirect all inbound traffic from the intermediate server to the main server. The same ports are redirected, namely `443`, `8443`, and the range `10000-60000`. The service also creates a link to the subscription, automatically replacing the IP address or domain in the configurations. Redirects are configured by adding iptables rules to the ufw configuration file, which ensures stable operation in conjunction with the firewall. It is recommended to install the service on a clean server without any previously installed rules.
-### Install forwarding
+## Install forwarding service
+
+The forwarding service allows proxying incoming ports from the intermediate server to the main server.
+
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
```
-### Delete forwarding
+Short description: adds forwarding rules and creates a service to update the subscription.
+
+## Remove forwarding
+
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
```
+Short description: removes rules and disables the forwarding service.
+
---
-### Show sub URL
+## Show subscription URL
+
+Command to print the current subscription URL from the container environment:
+
```bash
cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
```
----
+Short description: prints the static subscription URL that can be used in clients. Works on both main and intermediate servers.
+
+## Collect domains from multi-subscriptions
+
+The utility extracts domains from subscriptions and builds a `whitelist` for the generator.
-### Utility for collecting domains from multi-subscriptions
-Allows you to collect domains from subscriptions with multiple configurations. There are many similar configuration lists circulating on the internet that use white SNI. The tool allows you to extract domains and prepare a ready-made list for further use in the inbound generator.
-Insert a subscription link into the script and run the command in the `Node.js` environment.
```bash
node get_domains.js
```
-### Use your whitelist
-The file should have a structure similar to `whitelist.txt`. Rename the file to `my_whitelist.txt`. Upload the file to the `/opt/3dp-manager/app` folder on the server and run the command:
+Short description: add a multi-subscription link to the script and run the command — the output will be a list of domains. `Node.js` is required to run the script.
+
+## Use your own whitelist
+
+1. Prepare a file in the format of `whitelist.txt`.
+2. Rename it to `my_whitelist.txt` and copy it into `/opt/3dp-manager/app`.
+
```bash
cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
-```
\ No newline at end of file
+```
+
+Short description: adds your domain file into the application container.
+
+---
+
+## Notes and current limitations
+
+- The shared domain list may not work with all providers; it is recommended to prepare and use your own `whitelist`.
+
+---
+
+## Contributing
+
+Contributions are welcome! Simple contributor workflow:
+
+1. Fork the repository on GitHub.
+2. Create a branch with a meaningful name, e.g. `feature/add-README` or `fix/whitelist-load`.
+3. Make changes and add a short commit message.
+4. Run local checks if available.
+5. Push the branch to your fork and create a Pull Request to the main repository.
+
+Tips: describe changes and testing steps in the PR. For large changes, split into smaller commits.
+
+---
+
+## Discussion
+
+- Telegram: [@denpiligrim_web](https://t.me/denpiligrim_web)
+- Issues
\ No newline at end of file
diff --git a/README_IR.md b/README_IR.md
new file mode 100644
index 0000000..f65de74
--- /dev/null
+++ b/README_IR.md
@@ -0,0 +1,167 @@
+[Русский](/README.md) | [English](/README_EN.md) | [中文](/README_CN.md) | [Türkmençe](/README_TK.md)
+
+
+
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+
+# 3DP-MANAGER
+
+ابزاری برای تولید خودکار اتصالهای inbound برای پنل `3x-ui`، ساخت یک اشتراک یکپارچه و هدایت ترافیک از سرور واسط به سرور اصلی.
+
+**حمایت از پروژه**
+
+- اطلاعات پرداخت/اهدا:
+ - کارت MIR: `2204320436318077`
+ - کارت MasterCard: `5395452209474530`
+ - PayPal: `vasiljevdenisx@gmail.com`
+ - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - بیتکوین: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - Bybit ID: `165292278`
+
+## توضیحات
+
+هدف اصلی این ابزار این است که ترافیک شما یکسان به نظر نرسد. ربات در فواصل زمانی مشخص 10 اتصال با پارامترهای مختلف تولید میکند:
+
+- پروتکلها: `vless`, `vmess`, `shadowsocks`, `trojan`
+- پورتها: `443`, `8443` (ثابت) و پورتهای تصادفی در بازه `10000-60000`
+- لایه انتقال: `tcp`, `websocket`, `grpc`, `xhttp`
+- SNI از لیست سفید دامنهها گرفته میشود (`whitelist`); میتوانید از لیست خود استفاده کنید.
+
+تمام اتصالات در یک اشتراک با URL ثابت تجمیع میشوند. ربات با پنل `3x-ui` از طریق API باز کار میکند و بهطور مستقیم در عملکرد داخلی پنل دخالت نمیکند.
+
+هدف ثانویه افزایش پایداری اتصال است: مشتری 10 گزینه اتصال دریافت میکند و میتواند هر کدام را انتخاب کند.
+
+علاوه بر این، ربات میتواند در طرحهای آبشاری استفاده شود. سرویس فورواردینگ بهطور خودکار بازنشانی اشتراک و ترافیک به سرور اصلی را تنظیم میکند.
+
+توصیهها:
+
+- برای اشتراک از HTTPS استفاده کنید (دامنه + گواهی SSL).
+- فاصله تولید را ≥ 10 دقیقه در نظر بگیرید؛ برای پایداری توصیه میشود یکبار در روز (1440 دقیقه) تنظیم شود.
+- در کلاینت بهروزرسانی خودکار را بیشتر تنظیم کنید (مثلاً هر ساعت) تا همگامسازی با سرور انجام شود.
+
+## قابلیتها
+
+- تولید 10 اتصال متنوع
+- تشکیل یک اشتراک یکپارچه با URL ثابت
+- پشتیبانی از دامنههای `whitelist` سفارشی
+- پیکربندی خودکار فورواردینگ ترافیک (اختیاری)
+
+## نیازمندیها
+
+- Ubuntu 20.04 یا بالاتر
+- پنل `3x-ui`
+- دامنه + گواهی SSL (اختیاری)
+
+---
+
+## نصب
+
+برای نصب پروژه در سرور، دستور زیر را اجرا کنید:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
+```
+
+توضیح کوتاه: اسکریپت نصب را اجرا کرده و کانتینرها و سرویسها را راهاندازی میکند.
+
+## بهروزرسانی
+
+برای بهروزرسانی به آخرین نسخه:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
+```
+
+توضیح کوتاه: تغییرات جدید را میکشد و کانتینرها را ریاستارت میکند.
+
+## حذف
+
+برای حذف کامل سرویس:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
+```
+
+توضیح کوتاه: کانتینرها و فایلهای پیکربندی را حذف میکند و سیستم را به وضعیت پیش از نصب برمیگرداند.
+
+---
+
+## نصب سرویس فورواردینگ (forwarding)
+
+سرویس فورواردینگ امکان پراکسی کردن پورتهای ورودی از سرور واسط به سرور اصلی را فراهم میکند.
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
+```
+
+توضیح کوتاه: قوانین فورواردینگ را اضافه کرده و سرویسی برای بهروزرسانی اشتراک ایجاد میکند.
+
+## حذف فورواردینگ
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
+```
+
+توضیح کوتاه: قوانین را حذف کرده و سرویس فورواردینگ را غیرفعال میکند.
+
+---
+
+## نمایش URL اشتراک
+
+دستور برای نمایش URL اشتراک فعلی در محیط کانتینر:
+
+```bash
+cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
+```
+
+توضیح کوتاه: URL ثابت اشتراک را نمایش میدهد که میتوان در کلاینتها استفاده کرد. هم در سرور اصلی و هم در سرور واسط کار میکند.
+
+## جمعآوری دامنهها از چندین اشتراک
+
+ابزار دامنهها را از اشتراکهای دارای چندین پیکربندی استخراج کرده و `whitelist` تولید میکند.
+
+```bash
+node get_domains.js
+```
+
+توضیح کوتاه: لینک چندین اشتراک را در اسکریپت قرار داده و فرمان را اجرا کنید — خروجی فهرست دامنهها خواهد بود. نیازمند `Node.js` است.
+
+## استفاده از whitelist شخصی
+
+1. فایلی با ساختار مشابه `whitelist.txt` آماده کنید.
+2. آن را به `my_whitelist.txt` تغییر نام دهید و در پوشه `/opt/3dp-manager/app` کپی کنید.
+
+```bash
+cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
+```
+
+توضیح کوتاه: فایل دامنه شما را به کانتینر برنامه اضافه میکند.
+
+---
+
+## نکات و محدودیتهای فعلی
+
+- لیست عمومی دامنهها برای همه ارائهدهندگان کار نمیکند؛ پیشنهاد میشود `whitelist` خود را تهیه و استفاده کنید.
+
+---
+
+## مشارکت
+
+از هرگونه مشارکت در توسعه پروژه خوشحال میشوم! روند ساده برای مشارکتکنندگان:
+
+1. مخزن را در GitHub fork کنید.
+2. شاخهای با نام معنادار ایجاد کنید مانند `feature/add-README` یا `fix/whitelist-load`.
+3. تغییرات را اعمال کرده و یک پیام کوتاه در کامیت اضافه کنید.
+4. در صورت وجود، بررسیهای محلی را اجرا کنید.
+5. شاخه را به fork خود پوش کرده و Pull Request ایجاد کنید.
+
+نکات: تغییرات را در PR شرح دهید و مراحل تست را بنویسید. در صورت تغییرات بزرگ، آنها را به کامیتهای کوچک تقسیم کنید.
+
+---
+
+## بحث
+
+- تلگرام: [@denpiligrim_web](https://t.me/denpiligrim_web)
+- Issues
\ No newline at end of file
diff --git a/README_TK.md b/README_TK.md
new file mode 100644
index 0000000..7b5617e
--- /dev/null
+++ b/README_TK.md
@@ -0,0 +1,155 @@
+[Русский](/README.md) | [English](/README_EN.md) | [中文](/README_CN.md) | [فارسی](/README_IR.md)
+
+
+
+ [](https://www.gnu.org/licenses/gpl-3.0) [](https://t.me/denpiligrim_web) [](https://www.youtube.com/@denpiligrim)
+
+# 3DP-MANAGER
+
+Bu gural `3x-ui` paneli üçin awtomatiki ýagdaýda inbound baglanyşlaryny döredýär, birleşdirilen abunany (subscription) döretmäge we aragatnaşyk serwerinden esasy servere trafigiň ugradylyşyny sazlamaga kömek edýär.
+
+**Taslamany goldaň**
+
+- Töleg / goşant maglumatlary:
+ - MIR kart: `2204320436318077`
+ - MasterCard: `5395452209474530`
+ - PayPal: `vasiljevdenisx@gmail.com`
+ - USDT | ETH (ERC20 | BEP20): `0x6fe140040f6Cdc1E1Ff2136cd1d60C0165809463`
+ - USDT | TRX (TRC20): `TEWxXmJxvkAmhshp7E61XJGHB3VyM9hNAb`
+ - Bitcoin: `bc1qctntwncsv2yn02x2vgnkrqm00c4h04c0afkgpl`
+ - TON: `UQCZ3MiwyYHXftPItMMzJRYRiKHugr16jFMq2nfOQOOoemLy`
+ - Bybit ID: `165292278`
+
+## Düşündiriş
+
+Esasy maksady trafigiňizi birmeňzeş bolmaz ýaly etmekdir. Bot bellenen aralykda dürli parametrler bilen 10 sany baglanyşygy döredýär:
+
+- Protokollar: `vless`, `vmess`, `shadowsocks`, `trojan`;
+- Portlar: `443`, `8443` (hemişelik) we `10000-60000` aralygyndan tötänleýin portlar;
+- Transport: `tcp`, `websocket`, `grpc`, `xhttp`;
+- SNI `whitelist` domen sanawyndan alynýar; öz sanawyňyzy hem ulanyp bilersiňiz.
+
+Ähli döredilen baglanyşyklary biri-birine birleşdirip, statik URL bilen bir abuna döredilýär. Bot `3x-ui` paneliniň açyk API-sini ulanýar we paneliň içki konfigurasiýasyna gönüden-göni aralaşmaýar.
+
+Ikinji maksady baglanyşygyň durnuklylygyny ýokarlandyrmakdyr: müşderi 10 dürli baglanyşyk opsiýasyny alýar we islese haýsyny islese saýlap bilýär.
+
+Göçürme (forwarding) hyzmaty bilen birleşdirilende, bot abonnament we trafik diňe esasy servere ugradylyşyny awtomatiki sazlar.
+
+Maslahatchylyklar:
+
+- Abuna üçin HTTPS ulanyň (domain + SSL şahadatnamasy).
+- Dörediş aralygyny ≥ 10 minut goýuň; durnuklylyk üçin her gün bir gezek (1440 minut) maslahat berilýär.
+- Müşderide awtomatiki täzelenmäni ýygylaşdyryň (meselem, her sagat) — serwera sazlaşyklylyk üçin.
+
+## Imkançylyklar
+
+- 10 dürli baglanyşygy öndürýär
+- Statik URL bilen bir abuna döredýär
+- Öz `whitelist` domenleriňizi goldaýar
+- Trafigiň awtomatik forwarding sazlamalary (islege bagly)
+
+## Talaplar
+
+- Ubuntu 20.04 ýa-da täze
+- `3x-ui` paneli
+- Domain + SSL şahadatnamasy (islege bagly)
+
+---
+
+## Gurnama
+
+Taslamany serwera gurnamak üçin şu komandany ýerine ýetiriň:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/install.sh)
+```
+
+Gysgaça: gurnama skripti işledilýär we konteýnerler we hyzmatlar döredilýär.
+
+## Täzelik
+
+Soňky wersiýa çenli täzeläň:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/update.sh)
+```
+
+Gysgaça: iň soňky üýtgeşmeleri çekýär we konteýnerleri gaýtadan işledýär.
+
+## Pozmak
+
+Hyzmaty doly pozmak üçin:
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/delete.sh)
+```
+
+Gysgaça: konteýnerleri we konfigurasiýa faýllaryny pozup, systemany gurnamadan öňki ýagdaýyna getirer.
+
+---
+
+## Forwarding hyzmatyny gurnamak
+
+Forwarding hyzmaty aragatnaşyk serwerinden esasy servere gelýän portlary proxy arkaly geçirýär.
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_install.sh)
+```
+
+## Forwarding-i pozmak
+
+```bash
+bash <(curl -fsSL https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/forwarding_delete.sh)
+```
+
+---
+
+## Abuna URL-ni görkezmek
+
+Konteýner gurşawynda häzirki abuna URL-ni görkezýän komanda:
+
+```bash
+cd /opt/3dp-manager && docker compose exec node env | grep SUB_URL | cut -d'=' -f2
+```
+
+## Multi-abunalardan domenleri toplamak
+
+Gural birden köp konfigurasiýa içeren abunalardan domenleri çykaryp `whitelist` düzýär.
+
+```bash
+node get_domains.js
+```
+
+## Öz `whitelist`-iňizi ulanmak
+
+1. `whitelist.txt` bilen meňzeş struktura eýe faýl taýýarlaň.
+2. Ony `my_whitelist.txt` diýip atlandyryp `/opt/3dp-manager/app` içine göçüriň.
+
+```bash
+cd /opt/3dp-manager && docker cp ./app/my_whitelist.txt node:/app/my_whitelist.txt
+```
+
+---
+
+## Bellikler we çäklendirmeler
+
+- Umumy domen sanawy ähli üpjün edijilerde işlemeýär; öz `whitelist`-iňizi düzmegiňizi maslahat berýäris.
+
+---
+
+## Goşanty
+
+Proýekte goşant bermäge hoş geldiňiz! Goşant prosesi ýönekeý:
+
+1. GitHub-da repo-ny fork ediň.
+2. Meni manly at bilen şahamça (branch) dörediň, meselem `feature/add-README` ýa-da `fix/whitelist-load`.
+3. Üýtgeşmeleri giriziň we gysga commit ýazmagy unutmaň.
+4. Lokal barlaglary işlediň (bardy bolsa).
+5. Şahamçany fork-a iteriň we Pull Request dörediň.
+
+---
+
+## Çeperdeşlik
+
+- Telegram: [@denpiligrim_web](https://t.me/denpiligrim_web)
+- Issues
\ No newline at end of file
diff --git a/app/.env.example b/app/.env.example
index caf9611..3d17eb8 100644
--- a/app/.env.example
+++ b/app/.env.example
@@ -2,6 +2,7 @@ SUB_TOKEN=$SUB_TOKEN
UI_URL=$UI_URL
UI_LOGIN=$UI_LOGIN
UI_PASSWORD=$UI_PASSWORD
+COUNTRY_FLAG=$COUNTRY_FLAG
NGINX_PORT=$NGINX_PORT
UI_HOST=$UI_HOST
UI_PROTO=$UI_PROTO
diff --git a/app/assets/flags.json b/app/assets/flags.json
new file mode 100644
index 0000000..4c0a92a
--- /dev/null
+++ b/app/assets/flags.json
@@ -0,0 +1,1307 @@
+[
+ {
+ "name": "Ascension Island",
+ "code": "AC",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%A8"
+ },
+ {
+ "name": "Andorra",
+ "code": "AD",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%A9"
+ },
+ {
+ "name": "United Arab Emirates",
+ "code": "AE",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%AA"
+ },
+ {
+ "name": "Afghanistan",
+ "code": "AF",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%AB"
+ },
+ {
+ "name": "Antigua & Barbuda",
+ "code": "AG",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%AC"
+ },
+ {
+ "name": "Anguilla",
+ "code": "AI",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%AE"
+ },
+ {
+ "name": "Albania",
+ "code": "AL",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%B1"
+ },
+ {
+ "name": "Armenia",
+ "code": "AM",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%B2"
+ },
+ {
+ "name": "Angola",
+ "code": "AO",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%B4"
+ },
+ {
+ "name": "Antarctica",
+ "code": "AQ",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%B6"
+ },
+ {
+ "name": "Argentina",
+ "code": "AR",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%B7"
+ },
+ {
+ "name": "American Samoa",
+ "code": "AS",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%B8"
+ },
+ {
+ "name": "Austria",
+ "code": "AT",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%B9"
+ },
+ {
+ "name": "Australia",
+ "code": "AU",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%BA"
+ },
+ {
+ "name": "Aruba",
+ "code": "AW",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%BC"
+ },
+ {
+ "name": "Åland Islands",
+ "code": "AX",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%BD"
+ },
+ {
+ "name": "Azerbaijan",
+ "code": "AZ",
+ "emoji": "%F0%9F%87%A6%F0%9F%87%BF"
+ },
+ {
+ "name": "Bosnia & Herzegovina",
+ "code": "BA",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%A6"
+ },
+ {
+ "name": "Barbados",
+ "code": "BB",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%A7"
+ },
+ {
+ "name": "Bangladesh",
+ "code": "BD",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%A9"
+ },
+ {
+ "name": "Belgium",
+ "code": "BE",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%AA"
+ },
+ {
+ "name": "Burkina Faso",
+ "code": "BF",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%AB"
+ },
+ {
+ "name": "Bulgaria",
+ "code": "BG",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%AC"
+ },
+ {
+ "name": "Bahrain",
+ "code": "BH",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%AD"
+ },
+ {
+ "name": "Burundi",
+ "code": "BI",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%AE"
+ },
+ {
+ "name": "Benin",
+ "code": "BJ",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%AF"
+ },
+ {
+ "name": "St. Barthélemy",
+ "code": "BL",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%B1"
+ },
+ {
+ "name": "Bermuda",
+ "code": "BM",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%B2"
+ },
+ {
+ "name": "Brunei",
+ "code": "BN",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%B3"
+ },
+ {
+ "name": "Bolivia",
+ "code": "BO",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%B4"
+ },
+ {
+ "name": "Caribbean Netherlands",
+ "code": "BQ",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%B6"
+ },
+ {
+ "name": "Brazil",
+ "code": "BR",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%B7"
+ },
+ {
+ "name": "Bahamas",
+ "code": "BS",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%B8"
+ },
+ {
+ "name": "Bhutan",
+ "code": "BT",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%B9"
+ },
+ {
+ "name": "Bouvet Island",
+ "code": "BV",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%BB"
+ },
+ {
+ "name": "Botswana",
+ "code": "BW",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%BC"
+ },
+ {
+ "name": "Belarus",
+ "code": "BY",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%BE"
+ },
+ {
+ "name": "Belize",
+ "code": "BZ",
+ "emoji": "%F0%9F%87%A7%F0%9F%87%BF"
+ },
+ {
+ "name": "Canada",
+ "code": "CA",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%A6"
+ },
+ {
+ "name": "Cocos (Keeling) Islands",
+ "code": "CC",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%A8"
+ },
+ {
+ "name": "Congo - Kinshasa",
+ "code": "CD",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%A9"
+ },
+ {
+ "name": "Central African Republic",
+ "code": "CF",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%AB"
+ },
+ {
+ "name": "Congo - Brazzaville",
+ "code": "CG",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%AC"
+ },
+ {
+ "name": "Switzerland",
+ "code": "CH",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%AD"
+ },
+ {
+ "name": "Côte d’Ivoire",
+ "code": "CI",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%AE"
+ },
+ {
+ "name": "Cook Islands",
+ "code": "CK",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%B0"
+ },
+ {
+ "name": "Chile",
+ "code": "CL",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%B1"
+ },
+ {
+ "name": "Cameroon",
+ "code": "CM",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%B2"
+ },
+ {
+ "name": "China",
+ "code": "CN",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%B3"
+ },
+ {
+ "name": "Colombia",
+ "code": "CO",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%B4"
+ },
+ {
+ "name": "Clipperton Island",
+ "code": "CP",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%B5"
+ },
+ {
+ "name": "Costa Rica",
+ "code": "CR",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%B7"
+ },
+ {
+ "name": "Cuba",
+ "code": "CU",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%BA"
+ },
+ {
+ "name": "Cape Verde",
+ "code": "CV",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%BB"
+ },
+ {
+ "name": "Curaçao",
+ "code": "CW",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%BC"
+ },
+ {
+ "name": "Christmas Island",
+ "code": "CX",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%BD"
+ },
+ {
+ "name": "Cyprus",
+ "code": "CY",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%BE"
+ },
+ {
+ "name": "Czechia",
+ "code": "CZ",
+ "emoji": "%F0%9F%87%A8%F0%9F%87%BF"
+ },
+ {
+ "name": "Germany",
+ "code": "DE",
+ "emoji": "%F0%9F%87%A9%F0%9F%87%AA"
+ },
+ {
+ "name": "Diego Garcia",
+ "code": "DG",
+ "emoji": "%F0%9F%87%A9%F0%9F%87%AC"
+ },
+ {
+ "name": "Djibouti",
+ "code": "DJ",
+ "emoji": "%F0%9F%87%A9%F0%9F%87%AF"
+ },
+ {
+ "name": "Denmark",
+ "code": "DK",
+ "emoji": "%F0%9F%87%A9%F0%9F%87%B0"
+ },
+ {
+ "name": "Dominica",
+ "code": "DM",
+ "emoji": "%F0%9F%87%A9%F0%9F%87%B2"
+ },
+ {
+ "name": "Dominican Republic",
+ "code": "DO",
+ "emoji": "%F0%9F%87%A9%F0%9F%87%B4"
+ },
+ {
+ "name": "Algeria",
+ "code": "DZ",
+ "emoji": "%F0%9F%87%A9%F0%9F%87%BF"
+ },
+ {
+ "name": "Ceuta & Melilla",
+ "code": "EA",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%A6"
+ },
+ {
+ "name": "Ecuador",
+ "code": "EC",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%A8"
+ },
+ {
+ "name": "Estonia",
+ "code": "EE",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%AA"
+ },
+ {
+ "name": "Egypt",
+ "code": "EG",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%AC"
+ },
+ {
+ "name": "Western Sahara",
+ "code": "EH",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%AD"
+ },
+ {
+ "name": "Eritrea",
+ "code": "ER",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%B7"
+ },
+ {
+ "name": "Spain",
+ "code": "ES",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%B8"
+ },
+ {
+ "name": "Ethiopia",
+ "code": "ET",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%B9"
+ },
+ {
+ "name": "European Union",
+ "code": "EU",
+ "emoji": "%F0%9F%87%AA%F0%9F%87%BA"
+ },
+ {
+ "name": "Finland",
+ "code": "FI",
+ "emoji": "%F0%9F%87%AB%F0%9F%87%AE"
+ },
+ {
+ "name": "Fiji",
+ "code": "FJ",
+ "emoji": "%F0%9F%87%AB%F0%9F%87%AF"
+ },
+ {
+ "name": "Falkland Islands",
+ "code": "FK",
+ "emoji": "%F0%9F%87%AB%F0%9F%87%B0"
+ },
+ {
+ "name": "Micronesia",
+ "code": "FM",
+ "emoji": "%F0%9F%87%AB%F0%9F%87%B2"
+ },
+ {
+ "name": "Faroe Islands",
+ "code": "FO",
+ "emoji": "%F0%9F%87%AB%F0%9F%87%B4"
+ },
+ {
+ "name": "France",
+ "code": "FR",
+ "emoji": "%F0%9F%87%AB%F0%9F%87%B7"
+ },
+ {
+ "name": "Gabon",
+ "code": "GA",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%A6"
+ },
+ {
+ "name": "United Kingdom",
+ "code": "GB",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%A7"
+ },
+ {
+ "name": "Grenada",
+ "code": "GD",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%A9"
+ },
+ {
+ "name": "Georgia",
+ "code": "GE",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%AA"
+ },
+ {
+ "name": "French Guiana",
+ "code": "GF",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%AB"
+ },
+ {
+ "name": "Guernsey",
+ "code": "GG",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%AC"
+ },
+ {
+ "name": "Ghana",
+ "code": "GH",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%AD"
+ },
+ {
+ "name": "Gibraltar",
+ "code": "GI",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%AE"
+ },
+ {
+ "name": "Greenland",
+ "code": "GL",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%B1"
+ },
+ {
+ "name": "Gambia",
+ "code": "GM",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%B2"
+ },
+ {
+ "name": "Guinea",
+ "code": "GN",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%B3"
+ },
+ {
+ "name": "Guadeloupe",
+ "code": "GP",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%B5"
+ },
+ {
+ "name": "Equatorial Guinea",
+ "code": "GQ",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%B6"
+ },
+ {
+ "name": "Greece",
+ "code": "GR",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%B7"
+ },
+ {
+ "name": "South Georgia & South Sandwich Islands",
+ "code": "GS",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%B8"
+ },
+ {
+ "name": "Guatemala",
+ "code": "GT",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%B9"
+ },
+ {
+ "name": "Guam",
+ "code": "GU",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%BA"
+ },
+ {
+ "name": "Guinea-Bissau",
+ "code": "GW",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%BC"
+ },
+ {
+ "name": "Guyana",
+ "code": "GY",
+ "emoji": "%F0%9F%87%AC%F0%9F%87%BE"
+ },
+ {
+ "name": "Hong Kong SAR China",
+ "code": "HK",
+ "emoji": "%F0%9F%87%AD%F0%9F%87%B0"
+ },
+ {
+ "name": "Heard & McDonald Islands",
+ "code": "HM",
+ "emoji": "%F0%9F%87%AD%F0%9F%87%B2"
+ },
+ {
+ "name": "Honduras",
+ "code": "HN",
+ "emoji": "%F0%9F%87%AD%F0%9F%87%B3"
+ },
+ {
+ "name": "Croatia",
+ "code": "HR",
+ "emoji": "%F0%9F%87%AD%F0%9F%87%B7"
+ },
+ {
+ "name": "Haiti",
+ "code": "HT",
+ "emoji": "%F0%9F%87%AD%F0%9F%87%B9"
+ },
+ {
+ "name": "Hungary",
+ "code": "HU",
+ "emoji": "%F0%9F%87%AD%F0%9F%87%BA"
+ },
+ {
+ "name": "Canary Islands",
+ "code": "IC",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%A8"
+ },
+ {
+ "name": "Indonesia",
+ "code": "ID",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%A9"
+ },
+ {
+ "name": "Ireland",
+ "code": "IE",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%AA"
+ },
+ {
+ "name": "Israel",
+ "code": "IL",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%B1"
+ },
+ {
+ "name": "Isle of Man",
+ "code": "IM",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%B2"
+ },
+ {
+ "name": "India",
+ "code": "IN",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%B3"
+ },
+ {
+ "name": "British Indian Ocean Territory",
+ "code": "IO",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%B4"
+ },
+ {
+ "name": "Iraq",
+ "code": "IQ",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%B6"
+ },
+ {
+ "name": "Iran",
+ "code": "IR",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%B7"
+ },
+ {
+ "name": "Iceland",
+ "code": "IS",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%B8"
+ },
+ {
+ "name": "Italy",
+ "code": "IT",
+ "emoji": "%F0%9F%87%AE%F0%9F%87%B9"
+ },
+ {
+ "name": "Jersey",
+ "code": "JE",
+ "emoji": "%F0%9F%87%AF%F0%9F%87%AA"
+ },
+ {
+ "name": "Jamaica",
+ "code": "JM",
+ "emoji": "%F0%9F%87%AF%F0%9F%87%B2"
+ },
+ {
+ "name": "Jordan",
+ "code": "JO",
+ "emoji": "%F0%9F%87%AF%F0%9F%87%B4"
+ },
+ {
+ "name": "Japan",
+ "code": "JP",
+ "emoji": "%F0%9F%87%AF%F0%9F%87%B5"
+ },
+ {
+ "name": "Kenya",
+ "code": "KE",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%AA"
+ },
+ {
+ "name": "Kyrgyzstan",
+ "code": "KG",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%AC"
+ },
+ {
+ "name": "Cambodia",
+ "code": "KH",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%AD"
+ },
+ {
+ "name": "Kiribati",
+ "code": "KI",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%AE"
+ },
+ {
+ "name": "Comoros",
+ "code": "KM",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%B2"
+ },
+ {
+ "name": "St. Kitts & Nevis",
+ "code": "KN",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%B3"
+ },
+ {
+ "name": "North Korea",
+ "code": "KP",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%B5"
+ },
+ {
+ "name": "South Korea",
+ "code": "KR",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%B7"
+ },
+ {
+ "name": "Kuwait",
+ "code": "KW",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%BC"
+ },
+ {
+ "name": "Cayman Islands",
+ "code": "KY",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%BE"
+ },
+ {
+ "name": "Kazakhstan",
+ "code": "KZ",
+ "emoji": "%F0%9F%87%B0%F0%9F%87%BF"
+ },
+ {
+ "name": "Laos",
+ "code": "LA",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%A6"
+ },
+ {
+ "name": "Lebanon",
+ "code": "LB",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%A7"
+ },
+ {
+ "name": "St. Lucia",
+ "code": "LC",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%A8"
+ },
+ {
+ "name": "Liechtenstein",
+ "code": "LI",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%AE"
+ },
+ {
+ "name": "Sri Lanka",
+ "code": "LK",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%B0"
+ },
+ {
+ "name": "Liberia",
+ "code": "LR",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%B7"
+ },
+ {
+ "name": "Lesotho",
+ "code": "LS",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%B8"
+ },
+ {
+ "name": "Lithuania",
+ "code": "LT",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%B9"
+ },
+ {
+ "name": "Luxembourg",
+ "code": "LU",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%BA"
+ },
+ {
+ "name": "Latvia",
+ "code": "LV",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%BB"
+ },
+ {
+ "name": "Libya",
+ "code": "LY",
+ "emoji": "%F0%9F%87%B1%F0%9F%87%BE"
+ },
+ {
+ "name": "Morocco",
+ "code": "MA",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%A6"
+ },
+ {
+ "name": "Monaco",
+ "code": "MC",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%A8"
+ },
+ {
+ "name": "Moldova",
+ "code": "MD",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%A9"
+ },
+ {
+ "name": "Montenegro",
+ "code": "ME",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%AA"
+ },
+ {
+ "name": "St. Martin",
+ "code": "MF",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%AB"
+ },
+ {
+ "name": "Madagascar",
+ "code": "MG",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%AC"
+ },
+ {
+ "name": "Marshall Islands",
+ "code": "MH",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%AD"
+ },
+ {
+ "name": "North Macedonia",
+ "code": "MK",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B0"
+ },
+ {
+ "name": "Mali",
+ "code": "ML",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B1"
+ },
+ {
+ "name": "Myanmar (Burma)",
+ "code": "MM",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B2"
+ },
+ {
+ "name": "Mongolia",
+ "code": "MN",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B3"
+ },
+ {
+ "name": "Macao SAR China",
+ "code": "MO",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B4"
+ },
+ {
+ "name": "Northern Mariana Islands",
+ "code": "MP",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B5"
+ },
+ {
+ "name": "Martinique",
+ "code": "MQ",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B6"
+ },
+ {
+ "name": "Mauritania",
+ "code": "MR",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B7"
+ },
+ {
+ "name": "Montserrat",
+ "code": "MS",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B8"
+ },
+ {
+ "name": "Malta",
+ "code": "MT",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%B9"
+ },
+ {
+ "name": "Mauritius",
+ "code": "MU",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%BA"
+ },
+ {
+ "name": "Maldives",
+ "code": "MV",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%BB"
+ },
+ {
+ "name": "Malawi",
+ "code": "MW",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%BC"
+ },
+ {
+ "name": "Mexico",
+ "code": "MX",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%BD"
+ },
+ {
+ "name": "Malaysia",
+ "code": "MY",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%BE"
+ },
+ {
+ "name": "Mozambique",
+ "code": "MZ",
+ "emoji": "%F0%9F%87%B2%F0%9F%87%BF"
+ },
+ {
+ "name": "Namibia",
+ "code": "NA",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%A6"
+ },
+ {
+ "name": "New Caledonia",
+ "code": "NC",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%A8"
+ },
+ {
+ "name": "Niger",
+ "code": "NE",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%AA"
+ },
+ {
+ "name": "Norfolk Island",
+ "code": "NF",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%AB"
+ },
+ {
+ "name": "Nigeria",
+ "code": "NG",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%AC"
+ },
+ {
+ "name": "Nicaragua",
+ "code": "NI",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%AE"
+ },
+ {
+ "name": "Netherlands",
+ "code": "NL",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%B1"
+ },
+ {
+ "name": "Norway",
+ "code": "NO",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%B4"
+ },
+ {
+ "name": "Nepal",
+ "code": "NP",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%B5"
+ },
+ {
+ "name": "Nauru",
+ "code": "NR",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%B7"
+ },
+ {
+ "name": "Niue",
+ "code": "NU",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%BA"
+ },
+ {
+ "name": "New Zealand",
+ "code": "NZ",
+ "emoji": "%F0%9F%87%B3%F0%9F%87%BF"
+ },
+ {
+ "name": "Oman",
+ "code": "OM",
+ "emoji": "%F0%9F%87%B4%F0%9F%87%B2"
+ },
+ {
+ "name": "Panama",
+ "code": "PA",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%A6"
+ },
+ {
+ "name": "Peru",
+ "code": "PE",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%AA"
+ },
+ {
+ "name": "French Polynesia",
+ "code": "PF",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%AB"
+ },
+ {
+ "name": "Papua New Guinea",
+ "code": "PG",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%AC"
+ },
+ {
+ "name": "Philippines",
+ "code": "PH",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%AD"
+ },
+ {
+ "name": "Pakistan",
+ "code": "PK",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%B0"
+ },
+ {
+ "name": "Poland",
+ "code": "PL",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%B1"
+ },
+ {
+ "name": "St. Pierre & Miquelon",
+ "code": "PM",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%B2"
+ },
+ {
+ "name": "Pitcairn Islands",
+ "code": "PN",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%B3"
+ },
+ {
+ "name": "Puerto Rico",
+ "code": "PR",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%B7"
+ },
+ {
+ "name": "Palestinian Territories",
+ "code": "PS",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%B8"
+ },
+ {
+ "name": "Portugal",
+ "code": "PT",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%B9"
+ },
+ {
+ "name": "Palau",
+ "code": "PW",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%BC"
+ },
+ {
+ "name": "Paraguay",
+ "code": "PY",
+ "emoji": "%F0%9F%87%B5%F0%9F%87%BE"
+ },
+ {
+ "name": "Qatar",
+ "code": "QA",
+ "emoji": "%F0%9F%87%B6%F0%9F%87%A6"
+ },
+ {
+ "name": "Réunion",
+ "code": "RE",
+ "emoji": "%F0%9F%87%B7%F0%9F%87%AA"
+ },
+ {
+ "name": "Romania",
+ "code": "RO",
+ "emoji": "%F0%9F%87%B7%F0%9F%87%B4"
+ },
+ {
+ "name": "Serbia",
+ "code": "RS",
+ "emoji": "%F0%9F%87%B7%F0%9F%87%B8"
+ },
+ {
+ "name": "Russia",
+ "code": "RU",
+ "emoji": "%F0%9F%87%B7%F0%9F%87%BA"
+ },
+ {
+ "name": "Rwanda",
+ "code": "RW",
+ "emoji": "%F0%9F%87%B7%F0%9F%87%BC"
+ },
+ {
+ "name": "Saudi Arabia",
+ "code": "SA",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%A6"
+ },
+ {
+ "name": "Solomon Islands",
+ "code": "SB",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%A7"
+ },
+ {
+ "name": "Seychelles",
+ "code": "SC",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%A8"
+ },
+ {
+ "name": "Sudan",
+ "code": "SD",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%A9"
+ },
+ {
+ "name": "Sweden",
+ "code": "SE",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%AA"
+ },
+ {
+ "name": "Singapore",
+ "code": "SG",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%AC"
+ },
+ {
+ "name": "St. Helena",
+ "code": "SH",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%AD"
+ },
+ {
+ "name": "Slovenia",
+ "code": "SI",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%AE"
+ },
+ {
+ "name": "Svalbard & Jan Mayen",
+ "code": "SJ",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%AF"
+ },
+ {
+ "name": "Slovakia",
+ "code": "SK",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%B0"
+ },
+ {
+ "name": "Sierra Leone",
+ "code": "SL",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%B1"
+ },
+ {
+ "name": "San Marino",
+ "code": "SM",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%B2"
+ },
+ {
+ "name": "Senegal",
+ "code": "SN",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%B3"
+ },
+ {
+ "name": "Somalia",
+ "code": "SO",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%B4"
+ },
+ {
+ "name": "Suriname",
+ "code": "SR",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%B7"
+ },
+ {
+ "name": "South Sudan",
+ "code": "SS",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%B8"
+ },
+ {
+ "name": "São Tomé & Príncipe",
+ "code": "ST",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%B9"
+ },
+ {
+ "name": "El Salvador",
+ "code": "SV",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%BB"
+ },
+ {
+ "name": "Sint Maarten",
+ "code": "SX",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%BD"
+ },
+ {
+ "name": "Syria",
+ "code": "SY",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%BE"
+ },
+ {
+ "name": "Eswatini",
+ "code": "SZ",
+ "emoji": "%F0%9F%87%B8%F0%9F%87%BF"
+ },
+ {
+ "name": "Tristan da Cunha",
+ "code": "TA",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%A6"
+ },
+ {
+ "name": "Turks & Caicos Islands",
+ "code": "TC",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%A8"
+ },
+ {
+ "name": "Chad",
+ "code": "TD",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%A9"
+ },
+ {
+ "name": "French Southern Territories",
+ "code": "TF",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%AB"
+ },
+ {
+ "name": "Togo",
+ "code": "TG",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%AC"
+ },
+ {
+ "name": "Thailand",
+ "code": "TH",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%AD"
+ },
+ {
+ "name": "Tajikistan",
+ "code": "TJ",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%AF"
+ },
+ {
+ "name": "Tokelau",
+ "code": "TK",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%B0"
+ },
+ {
+ "name": "Timor-Leste",
+ "code": "TL",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%B1"
+ },
+ {
+ "name": "Turkmenistan",
+ "code": "TM",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%B2"
+ },
+ {
+ "name": "Tunisia",
+ "code": "TN",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%B3"
+ },
+ {
+ "name": "Tonga",
+ "code": "TO",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%B4"
+ },
+ {
+ "name": "Turkey",
+ "code": "TR",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%B7"
+ },
+ {
+ "name": "Trinidad & Tobago",
+ "code": "TT",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%B9"
+ },
+ {
+ "name": "Tuvalu",
+ "code": "TV",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%BB"
+ },
+ {
+ "name": "Taiwan",
+ "code": "TW",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%BC"
+ },
+ {
+ "name": "Tanzania",
+ "code": "TZ",
+ "emoji": "%F0%9F%87%B9%F0%9F%87%BF"
+ },
+ {
+ "name": "Ukraine",
+ "code": "UA",
+ "emoji": "%F0%9F%87%BA%F0%9F%87%A6"
+ },
+ {
+ "name": "Uganda",
+ "code": "UG",
+ "emoji": "%F0%9F%87%BA%F0%9F%87%AC"
+ },
+ {
+ "name": "U.S. Outlying Islands",
+ "code": "UM",
+ "emoji": "%F0%9F%87%BA%F0%9F%87%B2"
+ },
+ {
+ "name": "United Nations",
+ "code": "UN",
+ "emoji": "%F0%9F%87%BA%F0%9F%87%B3"
+ },
+ {
+ "name": "United States",
+ "code": "US",
+ "emoji": "%F0%9F%87%BA%F0%9F%87%B8"
+ },
+ {
+ "name": "Uruguay",
+ "code": "UY",
+ "emoji": "%F0%9F%87%BA%F0%9F%87%BE"
+ },
+ {
+ "name": "Uzbekistan",
+ "code": "UZ",
+ "emoji": "%F0%9F%87%BA%F0%9F%87%BF"
+ },
+ {
+ "name": "Vatican City",
+ "code": "VA",
+ "emoji": "%F0%9F%87%BB%F0%9F%87%A6"
+ },
+ {
+ "name": "St. Vincent & Grenadines",
+ "code": "VC",
+ "emoji": "%F0%9F%87%BB%F0%9F%87%A8"
+ },
+ {
+ "name": "Venezuela",
+ "code": "VE",
+ "emoji": "%F0%9F%87%BB%F0%9F%87%AA"
+ },
+ {
+ "name": "British Virgin Islands",
+ "code": "VG",
+ "emoji": "%F0%9F%87%BB%F0%9F%87%AC"
+ },
+ {
+ "name": "U.S. Virgin Islands",
+ "code": "VI",
+ "emoji": "%F0%9F%87%BB%F0%9F%87%AE"
+ },
+ {
+ "name": "Vietnam",
+ "code": "VN",
+ "emoji": "%F0%9F%87%BB%F0%9F%87%B3"
+ },
+ {
+ "name": "Vanuatu",
+ "code": "VU",
+ "emoji": "%F0%9F%87%BB%F0%9F%87%BA"
+ },
+ {
+ "name": "Wallis & Futuna",
+ "code": "WF",
+ "emoji": "%F0%9F%87%BC%F0%9F%87%AB"
+ },
+ {
+ "name": "Samoa",
+ "code": "WS",
+ "emoji": "%F0%9F%87%BC%F0%9F%87%B8"
+ },
+ {
+ "name": "Kosovo",
+ "code": "XK",
+ "emoji": "%F0%9F%87%BD%F0%9F%87%B0"
+ },
+ {
+ "name": "Yemen",
+ "code": "YE",
+ "emoji": "%F0%9F%87%BE%F0%9F%87%AA"
+ },
+ {
+ "name": "Mayotte",
+ "code": "YT",
+ "emoji": "%F0%9F%87%BE%F0%9F%87%B9"
+ },
+ {
+ "name": "South Africa",
+ "code": "ZA",
+ "emoji": "%F0%9F%87%BF%F0%9F%87%A6"
+ },
+ {
+ "name": "Zambia",
+ "code": "ZM",
+ "emoji": "%F0%9F%87%BF%F0%9F%87%B2"
+ },
+ {
+ "name": "Zimbabwe",
+ "code": "ZW",
+ "emoji": "%F0%9F%87%BF%F0%9F%87%BC"
+ },
+ {
+ "name": "England",
+ "code": "ENGLAND",
+ "emoji": "%F0%9F%8F%B4%F3%A0%81%A7%F3%A0%81%A2%F3%A0%81%A5%F3%A0%81%AE%F3%A0%81%A7%F3%A0%81%BF"
+ },
+ {
+ "name": "Scotland",
+ "code": "SCOTLAND",
+ "emoji": "%F0%9F%8F%B4%F3%A0%81%A7%F3%A0%81%A2%F3%A0%81%B3%F3%A0%81%A3%F3%A0%81%B4%F3%A0%81%BF"
+ },
+ {
+ "name": "Wales",
+ "code": "WALES",
+ "emoji": "%F0%9F%8F%B4%F3%A0%81%A7%F3%A0%81%A2%F3%A0%81%B7%F3%A0%81%AC%F3%A0%81%B3%F3%A0%81%BF"
+ }
+]
\ No newline at end of file
diff --git a/app/builders/buildInboundLink.js b/app/builders/buildInboundLink.js
index ad5d620..b3f26b0 100644
--- a/app/builders/buildInboundLink.js
+++ b/app/builders/buildInboundLink.js
@@ -1,3 +1,5 @@
+const flag = process.env.COUNTRY_FLAG ?? '%F0%9F%92%AF';
+
export function buildInboundLink(inbound, domain, idOrPass) {
let link = "";
@@ -48,7 +50,7 @@ function buildVlessLink(inbound, domain, uuid) {
params.set("fp", r.settings.fingerprint || "random");
params.set("sni", r.serverNames?.[0] || "");
params.set("sid", r.shortIds?.[0] || "");
- params.set("spx", '%2F');
+ params.set("spx", '/');
// TCP Reality flow
if (network === "tcp") {
@@ -88,7 +90,7 @@ function buildVlessLink(inbound, domain, uuid) {
return (
`vless://${uuid}@${domain}:${inbound.port}` +
`?${params.toString()}` +
- `#${encodeURIComponent(inbound.remark)}`
+ `#${flag}%20${encodeURIComponent(inbound.remark)}`
);
}
@@ -105,7 +107,7 @@ function buildVmessLink(inbound, domain, uuid) {
net: stream.network || "tcp",
path: "/",
port: inbound.port,
- ps: inbound.remark,
+ ps: decodeURIComponent(flag) + ' ' + inbound.remark,
scy: "",
sni: "",
tls: stream.security || "none",
@@ -134,7 +136,7 @@ function buildSsLink(inbound, domain) {
.from(userInfo, "utf8")
.toString("base64");
- return `ss://${base64}@${domain}:${inbound.port}?type=tcp#${inbound.remark}`;
+ return `ss://${base64}@${domain}:${inbound.port}?type=tcp#${flag}%20${inbound.remark}`;
}
function buildTrojanLink(inbound, domain, password) {
@@ -155,6 +157,6 @@ function buildTrojanLink(inbound, domain, password) {
`&sni=${sni}` +
`&sid=${sid}` +
`&spx=${spx}` +
- `#${inbound.remark}-${password}`
+ `#${flag}%20${inbound.remark}`
);
}
\ No newline at end of file
diff --git a/app/builders/buildVlessWs.js b/app/builders/buildVlessWs.js
index 2886130..4178cd6 100644
--- a/app/builders/buildVlessWs.js
+++ b/app/builders/buildVlessWs.js
@@ -1,5 +1,3 @@
-import crypto from "crypto";
-
export function buildVlessWs({ port, uuid, domain }) {
return {
enable: true,
diff --git a/app/builders/buildVmessTcp.js b/app/builders/buildVmessTcp.js
index 0b71e8d..c138586 100644
--- a/app/builders/buildVmessTcp.js
+++ b/app/builders/buildVmessTcp.js
@@ -14,7 +14,8 @@ export function buildVmessTcp({ port, uuid }) {
totalGB: 0,
expiryTime: 0,
tgId: "",
- subId: "",
+ subId: "0",
+ alterId: "0",
reset: 0
}],
}),
diff --git a/app/docker-compose.example.yml b/app/docker-compose.example.yml
index f33bc59..89f7014 100644
--- a/app/docker-compose.example.yml
+++ b/app/docker-compose.example.yml
@@ -9,6 +9,7 @@ services:
nginx:
image: nginx:alpine
+ restart: unless-stopped
depends_on: [node]
ports:
- "80:80"
diff --git a/app/index.js b/app/index.js
index 9e15455..56afa2e 100644
--- a/app/index.js
+++ b/app/index.js
@@ -13,6 +13,7 @@ import { buildInboundLink } from "./builders/buildInboundLink.js";
const { UI_URL, UI_LOGIN, UI_PASSWORD, UI_HOST } = process.env;
const SUB_FILE = "/subscriptions/list.txt";
+const LAST_INBOUNDS = '/subscriptions/latestInbounds.json';
const cookieJar = {};
const api = axios.create({ baseURL: UI_URL, timeout: 15000, withCredentials: true });
@@ -25,7 +26,7 @@ api.interceptors.request.use(config => {
const WHITELIST_FILE = "/app/whitelist.txt";
const WHITELIST_REPO_URL = "https://raw.githubusercontent.com/denpiligrim/3dp-manager/main/whitelist.txt";
-// Вспомогательные функции
+// Helper functions
async function updateWhitelist() {
try {
const res = await axios.get(WHITELIST_REPO_URL, { timeout: 10000 });
@@ -124,6 +125,26 @@ async function getInbounds() {
} catch (e) { console.error("Get inbounds failed:", e.message); return []; }
}
+async function deleteInbounds() {
+ try {
+ const data = fs.readFileSync(LAST_INBOUNDS, 'utf-8');
+ const oldIds = JSON.parse(data);
+
+ if (Array.isArray(oldIds) && oldIds.length > 0) {
+ console.log(`Удаляем старые инбаунды: ${oldIds.length} шт.`);
+ for (const id of oldIds) {
+ try {
+ await deleteInbound(id);
+ } catch (err) {
+ console.error(`Ошибка при удалении инбаунда ${id}:`, err.message);
+ }
+ }
+ }
+ } catch (error) {
+ console.log("Старых ID не найдено, пропускаем удаление.");
+ }
+}
+
async function deleteInbound(id) {
try { await api.post(`/panel/api/inbounds/del/${id}`); } catch { }
}
@@ -131,14 +152,14 @@ async function deleteInbound(id) {
async function addInbound(config) {
try {
const res = await api.post("/panel/api/inbounds/add", config);
- return res.data?.id || null;
+ return res.data?.obj?.id || null;
} catch (e) {
console.error("Add inbound failed:", e.message);
return null;
}
}
-// Основная ротация inbound'ов
+// Main rotation of inbound entries
async function rotate() {
await login();
@@ -147,8 +168,7 @@ async function rotate() {
const usedPorts = new Set();
const subs = [];
- const existing = await getInbounds();
- for (const i of existing) await deleteInbound(i.id);
+ await deleteInbounds();
const builders = [
async (d) => buildVlessRealityTcp({ port: await isPortFree(8443) ? 8443 : await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
@@ -163,23 +183,31 @@ async function rotate() {
async (d) => buildTrojanRealityTcp({ port: await getFreePort(usedPorts), uuid: await uuid(), domain: d, keys: await generateRealityKeys() }),
];
+ const inboundIds = [];
+
for (const b of builders) {
const domain = pickDomain(whitelist);
const inbound = await b(domain);
usedPorts.add(inbound.port);
const idOrPass = inbound.settings ? JSON.parse(inbound.settings).clients?.[0]?.id || JSON.parse(inbound.settings).clients?.[0]?.password : "";
- // Формируем ссылку в зависимости от протокола
+ // Build the link depending on the protocol
const link = buildInboundLink(inbound, UI_HOST, idOrPass);
if (link) subs.push(link);
- await addInbound(inbound);
+ const id = await addInbound(inbound);
+ inboundIds.push(id);
+ }
+
+ if (inboundIds.length > 0) {
+ fs.writeFileSync(LAST_INBOUNDS, JSON.stringify(inboundIds, null, 2));
+ console.log(`Новые ID (${inboundIds.length} шт.) сохранены в ${LAST_INBOUNDS}`);
}
fs.writeFileSync(SUB_FILE, subs.join("\n") + "\n", "utf8");
console.log("✔ 10 inbound created, подписка обновлена");
}
-// Первый запуск
+// Initial run
rotate();
let interval = parseInt(process.env.ROTATE_INTERVAL) || 30;
diff --git a/app/nginx.example.conf b/app/nginx.example.conf
index fcdc8a1..fd75cc9 100644
--- a/app/nginx.example.conf
+++ b/app/nginx.example.conf
@@ -7,6 +7,8 @@ http {
location = /bus/$SUB_TOKEN {
alias /subscriptions/list.txt;
default_type text/plain;
+ add_header Subscription-Userinfo "upload=0; download=0; total=109951162777600; expire=0" always;
+ add_header Access-Control-Allow-Origin *;
}
location / {
diff --git a/delete.sh b/delete.sh
index 14ab825..c3212b3 100644
--- a/delete.sh
+++ b/delete.sh
@@ -9,7 +9,6 @@ trap 'echo -e "\033[1;31m[ERROR]\033[0m Ошибка в строке $LINENO"; e
#################################
# HELPERS
#################################
-
log() { echo -e "\033[1;32m[INFO]\033[0m $1"; }
warn() { echo -e "\033[1;33m[WARN]\033[0m $1"; }
die() { echo -e "\033[1;31m[ERROR]\033[0m $1"; exit 1; }
@@ -18,10 +17,21 @@ need_root() {
[[ $EUID -eq 0 ]] || die "Запускать только от root"
}
+read -r -p "Вы уверены, что хотите удалить? (y/N): " answer
+
+case "$answer" in
+ y|Y)
+ echo "Начинаю удаление..."
+ ;;
+ *)
+ echo "Удаление отменено"
+ exit 1
+ ;;
+esac
+
#################################
# START
#################################
-
need_root
PROJECT_DIR="/opt/3dp-manager"
@@ -38,7 +48,6 @@ cd "$PROJECT_DIR"
#################################
# DOCKER COMPOSE DOWN
#################################
-
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
if [[ -f docker-compose.yml ]]; then
log "Останавливаем контейнеры 3dp-manager"
@@ -51,9 +60,8 @@ else
fi
#################################
-# CLEAN IMAGES (ТОЛЬКО НАШИ)
+# CLEAN IMAGES
#################################
-
log "Удаляем образы 3dp-manager (если есть)"
docker images --format '{{.Repository}} {{.ID}}' \
@@ -64,12 +72,10 @@ docker images --format '{{.Repository}} {{.ID}}' \
#################################
# REMOVE DIRECTORY
#################################
-
log "Удаляем $PROJECT_DIR"
rm -rf "$PROJECT_DIR"
#################################
# DONE
#################################
-
log "✔ 3dp-manager полностью удалён"
\ No newline at end of file
diff --git a/forwarding_delete.sh b/forwarding_delete.sh
index 9c6fb42..1854467 100644
--- a/forwarding_delete.sh
+++ b/forwarding_delete.sh
@@ -14,6 +14,18 @@ warn() { echo -e "\033[1;33m[WARN]\033[0m $1"; }
[[ $EUID -eq 0 ]] || { echo "Запускать нужно от root"; exit 1; }
+read -r -p "Вы уверены, что хотите удалить? (y/N): " answer
+
+case "$answer" in
+ y|Y)
+ echo "Начинаю удаление..."
+ ;;
+ *)
+ echo "Удаление отменено"
+ exit 1
+ ;;
+esac
+
#################################
# PATHS
#################################
@@ -42,14 +54,11 @@ rm -rf "$BASE_DIR"
#################################
# UFW NAT CLEAN
#################################
-echo "--- 1. Удаление оптимизаций ядра ---"
+echo "--- Удаление перенаправления ---"
rm -f /etc/sysctl.d/99-relay-optimization.conf
-# Применяем стандартные настройки (отключаем BBR и forward, если они не были включены до этого)
sysctl net.ipv4.ip_forward=0
-sysctl net.core.default_qdisc=pfifo_fast
-sysctl net.ipv4.tcp_congestion_control=cubic
-echo "--- 2. Восстановление правил UFW из бэкапа ---"
+echo "--- Восстановление правил UFW из бэкапа ---"
if [ -f /etc/ufw/before.rules.bak ]; then
mv /etc/ufw/before.rules.bak /etc/ufw/before.rules
echo "Файл before.rules восстановлен из бэкапа."
@@ -57,17 +66,19 @@ else
echo "ВНИМАНИЕ: Бэкап before.rules.bak не найден. Правила NAT придется удалять вручную."
fi
-echo "--- 3. Удаление разрешающих правил портов ---"
+echo "--- Удаление разрешающих правил портов ---"
ufw delete allow 443/tcp
+ufw delete allow 443/udp
ufw delete allow 8443/tcp
+ufw delete allow 8443/udp
ufw delete allow "$NGINX_PORT"/tcp
ufw delete allow 10000:60000/tcp
ufw delete allow 10000:60000/udp
-echo "--- 4. Возврат политики FORWARD по умолчанию (DROP) ---"
+echo "--- Возврат политики FORWARD по умолчанию (DROP) ---"
sed -i 's/DEFAULT_FORWARD_POLICY="ACCEPT"/DEFAULT_FORWARD_POLICY="DROP"/' /etc/default/ufw
-echo "--- 5. Перезапуск фаервола ---"
+echo "--- Перезапуск фаервола ---"
ufw reload
#################################
@@ -77,4 +88,4 @@ ufw reload
echo "Docker контейнеры удалены"
echo "UFW NAT очищен"
echo "ip_forward отключён"
-log "Откат завершён. Для применения изменений перезагрузите систему!"
\ No newline at end of file
+log "Откат завершён. Для окончательного применения изменений перезагрузите систему!"
\ No newline at end of file
diff --git a/forwarding_install.sh b/forwarding_install.sh
index be6dd2b..debc6fd 100644
--- a/forwarding_install.sh
+++ b/forwarding_install.sh
@@ -15,7 +15,9 @@ die() { echo -e "\033[1;31m[ERROR]\033[0m $1"; exit 1; }
[[ $EUID -eq 0 ]] || die "Запускать нужно от root"
-# --- ASCII-баннер ---
+#################################
+# ASCII-баннер
+#################################
echo "==================================================="
echo " ____ ____ _ ___ _ "
echo " / __ \___ ____ / __ \(_) (_)___ ______(_)___ ___ "
@@ -27,7 +29,6 @@ echo ""
echo " 3DP-MANAGER SUBSCRIPTION FORWARDER "
echo "==================================================="
echo ""
-# --------------------
#################################
# INPUT
@@ -51,22 +52,16 @@ SUB_PATH="/$(echo "$ORIGIN_SUB_URL" | cut -d/ -f4-)"
#################################
# Определяем ORIGIN_IP
#################################
-
if [[ "$ORIGIN_HOST" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
- # Это уже IP-адрес
ORIGIN_IP="$ORIGIN_HOST"
log "Используем прямой IP из URL: $ORIGIN_IP"
else
- # Это домен — разрешаем в IP
log "Разрешаем домен $ORIGIN_HOST в IP..."
- # Пробуем getent ahosts (IPv4 приоритет)
ORIGIN_IP=$(getent ahosts "$ORIGIN_HOST" | awk '/^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ {print $1; exit}')
- # Если не вышло — fallback на hosts
[[ -z "$ORIGIN_IP" ]] && ORIGIN_IP=$(getent hosts "$ORIGIN_HOST" | awk '{print $1; exit}')
- # Последний шанс — dig (если установлен)
if [[ -z "$ORIGIN_IP" ]] && command -v dig >/dev/null; then
ORIGIN_IP=$(dig +short "$ORIGIN_HOST" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1)
fi
@@ -76,7 +71,6 @@ else
log "Домен $ORIGIN_HOST разрешён в IP: $ORIGIN_IP"
fi
-# Финальная проверка
[[ -n "$ORIGIN_IP" && -n "$ORIGIN_PORT" && -n "$SUB_PATH" ]] || die "Ошибка парсинга URL подписки"
#################################
@@ -117,7 +111,12 @@ cd "$BASE_DIR"
#################################
REPO="https://raw.githubusercontent.com/denpiligrim/3dp-manager/main"
NGINX_PORT=$ORIGIN_PORT
-SUB_URL="$ORIGIN_PROTO://$LOCAL_HOST:$NGINX_PORT$SUB_PATH"
+if [[ "$USE_HTTPS" == "true" ]]; then
+ NGINX_PROTO="https"
+else
+ NGINX_PROTO="http"
+fi
+SUB_URL="$NGINX_PROTO://$LOCAL_HOST:$NGINX_PORT$SUB_PATH"
#################################
# ENV
@@ -194,7 +193,6 @@ EOF
#################################
# NGINX & DOCKER COMPOSE
#################################
-
if $USE_HTTPS; then
cat > nginx.conf </dev/null 2>&1; then
log "docker compose v2 доступен"
else
log "Устанавливаем docker-compose-v2"
- # решаем конфликт с docker-compose-plugin
apt install -y --allow-downgrades --allow-remove-essential --allow-change-held-packages \
docker-compose-v2 || warn "docker-compose-v2 не установлен, возможно уже есть плагин"
fi
@@ -313,27 +317,20 @@ docker compose up -d --build
#################################
LOCAL_IP=$(hostname -I | awk '{print $1}')
-# Проверка и установка UFW, если не установлен
if ! command -v ufw >/dev/null 2>&1; then
echo "UFW не установлен. Устанавливаю..."
apt update -qq && apt install -y ufw
fi
-# Проверка статуса UFW (независимо от локали, на английском)
if LC_ALL=C ufw status 2>/dev/null | grep -q "Status: active"; then
echo "UFW уже активен."
else
echo "ВНИМАНИЕ: UFW выключен или не настроен. Включаю..."
- # Разрешаем SSH (или другой порт, если используете не 22)
- # OpenSSH — стандартный профиль, обычно порт 22
ufw allow OpenSSH >/dev/null 2>&1 || true
- # Если SSH на другом порту, добавьте: ufw allow 2222/tcp
- # Включаем UFW без подтверждения
ufw --force enable >/dev/null 2>&1
- # Проверяем, успешно ли включён
if LC_ALL=C ufw status 2>/dev/null | grep -q "Status: active"; then
echo "UFW успешно включён."
else
@@ -342,7 +339,7 @@ else
fi
fi
-echo "--- 1. Оптимизация сетевого стека ядра ---"
+echo "--- Оптимизация сетевого стека ядра ---"
cat < /etc/sysctl.d/99-relay-optimization.conf
net.ipv4.ip_forward = 1
net.core.default_qdisc = fq
@@ -361,10 +358,9 @@ net.ipv4.tcp_tw_reuse=1
EOF
sysctl --system
-echo "--- 2. Настройка правил перенаправления (before.rules) ---"
+echo "--- Настройка правил перенаправления (before.rules) ---"
cp /etc/ufw/before.rules /etc/ufw/before.rules.bak
-# Создаем временный файл с вашими правилами
cat < /tmp/ufw_nat_rules
*nat
:PREROUTING ACCEPT [0:0]
@@ -400,28 +396,24 @@ COMMIT
EOF
-# Удаляем старые блоки *nat и *mangle из оригинала, если они там были (от прошлых запусков)
-# И создаем чистую копию без них
sed -i '/\*nat/,/COMMIT/d' /etc/ufw/before.rules
sed -i '/\*mangle/,/COMMIT/d' /etc/ufw/before.rules
-# Склеиваем: Сначала NAT/Mangle, потом остаток оригинального before.rules
cat /tmp/ufw_nat_rules /etc/ufw/before.rules > /etc/ufw/before.rules.new
mv /etc/ufw/before.rules.new /etc/ufw/before.rules
-echo "--- 3. Открытие портов в самом фаерволе ---"
-# Разрешаем входящий трафик на эти порты
+echo "--- Открытие портов в самом фаерволе ---"
ufw allow 443/tcp
+ufw allow 443/udp
ufw allow 8443/tcp
+ufw allow 8443/udp
ufw allow "$NGINX_PORT"/tcp
ufw allow 10000:60000/tcp
ufw allow 10000:60000/udp
-# Разрешаем FORWARD (пересылку)
-# Это критически важный шаг для корректной работы DNAT в UFW
sed -i 's/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/' /etc/default/ufw
-echo "--- 4. Перезапуск ---"
+echo "--- Перезапуск ---"
ufw reload
echo "Готово! Система оптимизирована, порты открыты, трафик перенаправлен."
diff --git a/get_domains.js b/get_domains.js
index 7f64e84..9fd5e8e 100644
--- a/get_domains.js
+++ b/get_domains.js
@@ -48,7 +48,7 @@ async function processSubscription() {
const result = Array.from(domains).sort(); // Сортируем для удобства
if (result.length > 0) {
- fs.writeFileSync('domains.txt', result.join('\n'), 'utf8');
+ fs.writeFileSync('my_whitelist.txt', result.join('\n'), 'utf8');
console.log(`Успешно! Сохранено уникальных доменов: ${result.length}`);
} else {
console.log('Валидные домены не найдены.');
diff --git a/install.sh b/install.sh
index 01a67c2..686b977 100644
--- a/install.sh
+++ b/install.sh
@@ -2,14 +2,13 @@
set -euo pipefail
#################################
-# TRAP ДЛЯ ОТЛАДКИ
+# DEBUG TRAP
#################################
trap 'echo -e "\033[1;31m[ERROR]\033[0m Ошибка в строке $LINENO"; exit 1' ERR
#################################
-# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
+# HELPER FUNCTIONS
#################################
-
log() { echo -e "\033[1;32m[INFO]\033[0m $1"; }
die() { echo -e "\033[1;31m[ERROR]\033[0m $1"; exit 1; }
@@ -22,12 +21,13 @@ validate_url() {
}
#################################
-# ПРОВЕРКИ
+# CHECKS
#################################
-
need_root
-# --- ASCII-баннер ---
+#################################
+# ASCII-баннер
+#################################
echo "==================================================="
echo " ____ ____ _ ___ _ "
echo " / __ \___ ____ / __ \(_) (_)___ ______(_)___ ___ "
@@ -39,22 +39,39 @@ echo ""
echo " 3DP-MANAGER FOR 3X-UI "
echo "==================================================="
echo ""
-# --------------------
#################################
-# ВВОД ДАННЫХ
+# INPUT / USER DATA
#################################
+# Function to get panel URL from 3x-ui
+get_xui_url() {
+ local output=$(x-ui settings 2>/dev/null)
-read -rp "URL панели 3x-ui (с портом и путем): " UI_URL
-UI_URL="${UI_URL%/}"
-validate_url "$UI_URL" || die "Некорректный URL 3x-ui"
+ echo "$output" | sed 's/\x1b\[[0-9;]*m//g' | grep "Access URL:" | grep -oE 'https?://[^[:space:]]+' | head -n1
+}
+
+echo "Определяем URL панели 3x-ui..."
+
+UI_URL=$(get_xui_url)
+
+if [[ -z "$UI_URL" ]]; then
+ echo "Не удалось автоматически получить URL"
+ read -rp "Введите URL панели 3x-ui вручную: " UI_URL
+fi
+
+UI_URL=$(echo "$UI_URL" | sed -E 's/[[:space:]]*$//; s|/*$||')
+
+echo "URL панели 3x-ui: $UI_URL"
+
+# Validate URL correctness
+validate_url "$UI_URL" || die "Некорректный URL панели 3x-ui: $UI_URL"
read -rp "Логин 3x-ui: " UI_LOGIN
read -rsp "Пароль 3x-ui: " UI_PASSWORD
echo
[[ -z "$UI_LOGIN" || -z "$UI_PASSWORD" ]] && die "Логин/пароль обязательны"
-# Проверка логина
+# Check login
LOGIN_RESPONSE=$(curl -s -X POST "$UI_URL/login" -H "Content-Type: application/json" -d "{\"username\":\"$UI_LOGIN\",\"password\":\"$UI_PASSWORD\"}")
# Проверка успешного логина по ключевому полю "success" или наличию куки
@@ -65,9 +82,9 @@ fi
echo "✔ Успешный логин в 3x-ui"
-# Разбор UI_URL
-UI_HOST=$(echo "$UI_URL" | awk -F[/:] '{print $4}') # домен или IP
-UI_PROTO=$(echo "$UI_URL" | awk -F: '{print $1}') # http или https
+# Parse UI_URL
+UI_HOST=$(echo "$UI_URL" | awk -F[/:] '{print $4}') # domain or IP
+UI_PROTO=$(echo "$UI_URL" | awk -F: '{print $1}') # http or https
if [[ "$UI_PROTO" == "https" ]]; then
log "HTTPS панель обнаружена, проверяем SSL сертификаты"
@@ -90,11 +107,11 @@ if [[ "$UI_PROTO" == "https" ]]; then
fi
-# Ввод интервала ротации
+# Input rotation interval
read -rp "Интервал генерации инбаундов в минутах (от 10, по умолчанию 30): " ROTATE_INTERVAL
ROTATE_INTERVAL="${ROTATE_INTERVAL:-30}" # по умолчанию 30
-# Проверка, что это число и ≥10
+# Check that it's a number and ≥10
if ! [[ "$ROTATE_INTERVAL" =~ ^[0-9]+$ ]] || [ "$ROTATE_INTERVAL" -lt 10 ]; then
echo "Неверное значение. Используется значение по умолчанию 30 минут."
ROTATE_INTERVAL=30
@@ -109,25 +126,42 @@ mkdir -p "$PROJECT_DIR"
cd "$PROJECT_DIR"
#################################
-# Белый лист
+# Get country flag
#################################
-
REPO_BASE="https://raw.githubusercontent.com/denpiligrim/3dp-manager/main"
+COUNTRY_FLAG=""
+# Get countryCode
+IP_JSON=$(curl -s --fail http://ip-api.com/json/ || true)
+[ -z "$IP_JSON" ] && exit 0
+
+COUNTRY_CODE=$(echo "$IP_JSON" | jq -r '.countryCode // empty' 2>/dev/null)
+[ -z "$COUNTRY_CODE" ] && exit 0
+
+# Flags JSON URL
+FLAGS_JSON_URL="$REPO_BASE/app/assets/flags.json"
+
+FLAGS_JSON=$(curl -s "$FLAGS_JSON_URL" || true)
+[ -z "$FLAGS_JSON" ] && exit 0
+
+# Ищем emoji
+COUNTRY_FLAG=$(echo "$FLAGS_JSON" | jq -r --arg code "$COUNTRY_CODE" '.[] | select(.code == $code) | .emoji // empty' 2>/dev/null | head -n1)
+
+#################################
+# Whitelist
+#################################
curl -fsSL "$REPO_BASE/whitelist.txt" -o whitelist.txt
log "whitelist.txt скопирован"
#################################
-# ГЕНЕРАЦИЯ ТОКЕНА
+# TOKEN GENERATION
#################################
-
SUB_TOKEN="$(openssl rand -hex 24)"
log "Сгенерирован токен подписки"
#################################
# DOCKER
#################################
-
log "Проверка Docker"
if command -v docker >/dev/null 2>&1; then
@@ -140,7 +174,7 @@ else
systemctl restart docker
fi
-# Проверка docker compose v2
+# Check docker compose v2
if docker compose version >/dev/null 2>&1; then
log "docker compose v2 доступен"
else
@@ -151,15 +185,14 @@ else
fi
#################################
-# СТРУКТУРА
+# STRUCTURE
#################################
-
mkdir -p app app/builders subscriptions
-# Генерация случайного свободного порта для подписки/Nginx
+# Generate a random free port for subscription/Nginx
get_random_port() {
while :; do
- PORT=$((RANDOM % 50000 + 10000)) # диапазон 10000-60000
+ PORT=$((RANDOM % 50000 + 10000)) # range 10000-60000
if ! ss -ltn | awk '{print $4}' | grep -q ":$PORT\$"; then
echo "$PORT"
return
@@ -168,18 +201,18 @@ get_random_port() {
}
NGINX_PORT=$(get_random_port)
-# Генерация URL подписки
+# Generate subscription URL
SUB_URL="$UI_PROTO://$UI_HOST:$NGINX_PORT/bus/$SUB_TOKEN"
#################################
# ENV
#################################
-
cat > .env < docker-compose.yml </dev/null || die "Backend не запущен"
docker compose ps | grep nginx >/dev/null || die "Nginx не запущен"
#################################
-# РЕЗУЛЬТАТ
+# RESULT
#################################
-
log "✔ Установка завершена"
echo
echo "URL подписки:"
diff --git a/update.sh b/update.sh
index 865c835..e44211e 100644
--- a/update.sh
+++ b/update.sh
@@ -9,7 +9,6 @@ trap 'echo -e "\033[1;31m[ERROR]\033[0m Ошибка в строке $LINENO"; e
#################################
# HELPERS
#################################
-
log() { echo -e "\033[1;32m[INFO]\033[0m $1"; }
warn() { echo -e "\033[1;33m[WARN]\033[0m $1"; }
die() { echo -e "\033[1;31m[ERROR]\033[0m $1"; exit 1; }
@@ -21,14 +20,12 @@ need_root() {
#################################
# CONFIG
#################################
-
PROJECT_DIR="/opt/3dp-manager"
REPO_RAW="https://raw.githubusercontent.com/denpiligrim/3dp-manager/main"
#################################
# START
#################################
-
need_root
log "Обновление 3dp-manager"
@@ -40,14 +37,12 @@ cd "$PROJECT_DIR"
#################################
# CHECK DOCKER
#################################
-
command -v docker >/dev/null 2>&1 || die "Docker не установлен"
docker compose version >/dev/null 2>&1 || die "docker compose v2 недоступен"
#################################
# DOWNLOAD FILES
#################################
-
log "Загружаем обновлённые файлы из репозитория"
mkdir -p app
@@ -70,21 +65,18 @@ log "Файлы обновлены"
#################################
# REBUILD BACKEND
#################################
-
log "Пересобираем backend"
docker compose build node
#################################
# RESTART CONTAINERS
#################################
-
log "Перезапускаем контейнеры"
docker compose up -d
#################################
# HEALTH CHECK
#################################
-
sleep 2
docker compose ps | grep node >/dev/null || die "Backend не запущен"
@@ -93,5 +85,4 @@ docker compose ps | grep nginx >/dev/null || die "Nginx не запущен"
#################################
# DONE
#################################
-
-log "3dp-manager успешно обновлён ✅"
+log "3dp-manager успешно обновлён ✅"
\ No newline at end of file