diff --git a/README.md b/README.md
index 8fe97f6..5283c19 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,7 @@ WireGuard-интерфейс WARP внутри Docker-контейнера Amnez
| JSON-конфиги для Xray | ✅ | — |
| Маршрутизация по доменам | ✅ | — |
| Изменение порта SOCKS5 | ✅ | — |
-| Управление клиентами (add/remove) | — | ✅ |
+| Управление клиентами (переключатели ✅/☐ в одном меню) | — | ✅ |
| WireGuard внутри Docker | — | ✅ |
| Перезапуск контейнера | — | ✅ |
@@ -249,31 +249,30 @@ curl -s --proxy socks5h://127.0.0.1:40000 ifconfig.me
- Поднимет WG-интерфейс `warp` внутри контейнера
- Покажет WARP IP
-### Шаг 3. Добавление клиентов в WARP
+### Шаг 3. Включение клиентов в WARP
После установки WARP нужно указать, **каких клиентов** пускать через Cloudflare.
**Через SSH-меню:**
-1. Выберите пункт **6** — ➕ Добавить клиентов в WARP
-2. Вы увидите список всех клиентов AmneziaWG с их IP и именами
-3. Введите номер клиента, которого хотите добавить, или `all` для всех
+1. Выберите пункт **6** — переключение клиентов WARP (единое меню)
+2. Вы увидите список всех клиентов AmneziaWG с их IP и именами; напротив каждого — **✅** (уже в WARP) или **☐** (не в WARP)
+3. Введите номер клиента, чтобы переключить состояние (в WARP или вне WARP)
4. Скрипт:
- - Добавит `ip rule` для маршрутизации трафика клиента через WARP
- - Настроит `iptables` NAT для корректного выхода через WG-интерфейс
+ - Добавит или снимет `ip rule` для маршрутизации трафика клиента через WARP
+ - Настроит или уберёт `iptables` NAT для корректного выхода через WG-интерфейс
- Пропишет правила в `start.sh` контейнера (персистентность)
**Через Telegram-бот:**
-1. Нажмите **Клиенты** → **➕ Добавить**
-2. Выберите клиента из списка (✅ = уже в WARP, ➕ = можно добавить)
-3. Или нажмите **✅ Все в WARP** для добавления всех
+1. Нажмите **👥 Клиенты WARP** — откроется тот же список-переключатель
+2. Напротив клиентов отображаются **✅** (в WARP) и **☐** (вне WARP); выбор пункта переключает состояние
### Шаг 4. Проверка
1. Подключитесь к VPN через приложение Amnezia
2. Откройте [https://whoer.net](https://whoer.net) или [https://ifconfig.me](https://ifconfig.me)
-3. Если ваш клиент добавлен в WARP — вы увидите **IP Cloudflare**
+3. Если ваш клиент включён в WARP — вы увидите **IP Cloudflare**
4. Если клиент не в WARP — вы увидите **обычный IP сервера**
Проверка через SSH (внутри контейнера):
@@ -284,17 +283,17 @@ docker exec <имя_контейнера> curl -s --interface warp ifconfig.me
### Управление клиентами
+В SSH и в боте используется **одно меню переключателей**: у каждого клиента видно **✅** (трафик через WARP) или **☐** (напрямую); действие по пункту меняет состояние.
+
| Действие | SSH-меню | Telegram-бот |
|----------|----------|--------------|
-| Добавить в WARP | пункт **6** | Клиенты → ➕ Добавить |
-| Убрать из WARP | пункт **7** | Клиенты → ➖ Убрать |
-| Список клиентов в WARP | пункт **8** | Клиенты |
+| Включить/выключить клиента в WARP (список ✅/☐) | пункт **6** | **👥 Клиенты WARP** |
| Перевыпуск ключа | пункт **5** | 🔑 Перевыпуск ключа |
-| Перезапуск контейнера | пункт **9** | 🔄 Контейнер |
+| Перезапуск контейнера | пункт **7** | 🔄 Контейнер |
-### Что происходит при добавлении клиента
+### Что происходит при включении клиента в WARP
-Когда вы добавляете клиента (например, `10.8.1.2`) в WARP, скрипт:
+Когда вы переводите клиента в WARP (например, `10.8.1.2`), скрипт:
1. Создаёт **ip rule**: `ip rule add from 10.8.1.2 table 51820`
2. Добавляет **iptables NAT**: `iptables -t nat -A POSTROUTING -s 10.8.1.2 -o warp -j MASQUERADE`
@@ -320,7 +319,7 @@ docker exec <имя_контейнера> curl -s --interface warp ifconfig.me
## Меню WARP Manager
-Меню организовано по секциям. Нумерация пунктов 1–5 и 10–13 одинакова в обоих режимах, а секция 6–9 адаптируется.
+Меню организовано по секциям. Пункты **1–5** (WARP) и **8–11** (бот, промо, инструкция, удаление) одинаковы в обоих режимах; пункты **6–7** зависят от режима (3X-UI или AmneziaWG).
### Общая секция — WARP-ключ (оба режима)
@@ -336,26 +335,24 @@ docker exec <имя_контейнера> curl -s --interface warp ifconfig.me
| Пункт | Действие |
|-------|----------|
-| 6 | JSON конфиг для 3X-UI |
+| 6 | Настройки SOCKS5 / JSON для 3X-UI |
| 7 | Изменить порт SOCKS5 |
### Секция AmneziaWG (только в режиме AmneziaWG)
| Пункт | Действие |
|-------|----------|
-| 6 | ➕ Добавить клиентов в WARP |
-| 7 | ➖ Убрать клиентов из WARP |
-| 8 | Показать клиентов в WARP |
-| 9 | Перезапуск контейнера |
+| 6 | Переключение клиентов WARP (единое меню, ✅ / ☐) |
+| 7 | Перезапуск контейнера |
### Общая секция — бот и прочее (оба режима)
| Пункт | Действие |
|-------|----------|
-| 10 | Telegram Bot (настройка и управление) |
-| 11 | PROMO |
-| 12 | Инструкция |
-| 13 | Полное удаление |
+| 8 | Telegram Bot (настройка и управление) |
+| 9 | PROMO |
+| 10 | Инструкция |
+| 11 | Полное удаление |
| 0 | Выход |
---
@@ -370,7 +367,7 @@ WARP Manager включает встроенный Telegram-бот для уда
2. Отправьте `/newbot`
3. Задайте имя и username бота
4. Скопируйте полученный **токен**
-5. На сервере выполните `gowarp` → пункт **10** (Telegram Bot)
+5. На сервере выполните `gowarp` → пункт **8** (Telegram Bot)
6. Выберите **1) Токен бота** — вставьте токен
7. Выберите **2) Chat ID (авто)**:
- Откройте вашего бота в Telegram
@@ -405,9 +402,7 @@ WARP Manager включает встроенный Telegram-бот для уда
| ▶️ Запустить | Поднять WG-интерфейс WARP |
| ⏹ Остановить | Опустить WG-интерфейс WARP |
| 🔑 Перевыпуск ключа | Пересоздать WARP-профиль |
-| 👥 Клиенты | Список клиентов в WARP |
-| ➕ Добавить | Добавить клиента в WARP (выбор из списка) |
-| ➖ Убрать | Убрать клиента из WARP |
+| 👥 Клиенты WARP | Единое меню переключателей: список клиентов с **✅** (в WARP) и **☐** (вне WARP) |
| 🔄 Контейнер | Перезапуск Docker-контейнера AmneziaWG |
| 🖥 Система | CPU, RAM, диск, uptime |
| 🏷 Хостинг | Промо-материалы |
@@ -453,7 +448,7 @@ docker exec <контейнер> cat /opt/warp/warp.conf
docker exec <контейнер> ip link show warp
```
-Попробуйте перевыпустить ключ (пункт 5) или перезапустить контейнер (пункт 9).
+Попробуйте перевыпустить ключ (пункт 5) или перезапустить контейнер (пункт 7).
### Сайт всё ещё заблокирован после настройки (3X-UI)
@@ -464,9 +459,9 @@ docker exec <контейнер> ip link show warp
### Клиент не видит WARP IP (AmneziaWG)
-1. Убедитесь, что клиент **добавлен** в WARP (`gowarp` → пункт 8)
+1. Убедитесь, что клиент **включён** в WARP (`gowarp` → пункт 6, в списке должна быть **✅**)
2. Проверьте, что интерфейс `warp` поднят (`gowarp` → пункт 4)
-3. Перезапустите контейнер (`gowarp` → пункт 9)
+3. Перезапустите контейнер (`gowarp` → пункт 7)
4. Переподключитесь к VPN на клиентском устройстве
### Как сменить WARP IP
@@ -492,7 +487,7 @@ docker exec <контейнер> ip link show warp
gowarp
```
-Выберите пункт **13** — Полное удаление.
+Выберите пункт **11** — Полное удаление.
### Что удаляется (3X-UI)
diff --git a/warp.sh b/warp.sh
index 2c77009..dae5f5c 100644
--- a/warp.sh
+++ b/warp.sh
@@ -283,7 +283,7 @@ install_warp_3xui() {
else
echo -e "${YELLOW} ⚠ Подключение не подтверждено.${NC}"
fi
- echo -e "\n${WHITE}Интеграция с 3X-UI: пункт меню ${YELLOW}5${NC}"
+ echo -e "\n${WHITE}Настройки SOCKS5 и JSON: пункт меню ${YELLOW}6${NC}"
read -p "Enter..."
}
@@ -292,8 +292,11 @@ start_warp_3xui() {
is_warp_running_3xui && { echo -e "\n${YELLOW}Уже подключён.${NC}"; read -p "Enter..."; return; }
echo -e "\n${YELLOW}Подключение...${NC}"
warp-cli --accept-tos connect > /dev/null 2>&1; sleep 3
- is_warp_running_3xui && echo -e "${GREEN}[OK] Подключён.${NC}" && log_action "3XUI START" \
- || echo -e "${RED}Ошибка подключения.${NC}"
+ if is_warp_running_3xui; then
+ echo -e "${GREEN}[OK] Подключён.${NC}"; log_action "3XUI START"
+ else
+ echo -e "${RED}Ошибка подключения.${NC}"
+ fi
read -p "Enter..."
}
@@ -613,7 +616,7 @@ install_warp_awg() {
awg_detect_warp_exit_ip
[ -n "$AWG_WARP_EXIT_IP" ] && echo -e "\n ${WHITE}WARP IP: ${GREEN}${AWG_WARP_EXIT_IP}${NC}"
- echo -e "\n${GREEN}WARP установлен! Добавьте клиентов через п.6.${NC}"
+ echo -e "\n${GREEN}WARP установлен! Управление клиентами — п.6.${NC}"
log_action "AWG INSTALL: endpoint=${ep}, warp_ip=${AWG_WARP_EXIT_IP}"
read -p "Enter..."
}
@@ -622,8 +625,11 @@ start_warp_awg() {
is_warp_installed_awg || { echo -e "\n${RED}WARP не установлен (п.1).${NC}"; read -p "Enter..."; return; }
is_warp_running_awg && { echo -e "\n${YELLOW}Уже работает.${NC}"; read -p "Enter..."; return; }
echo -e "\n${YELLOW}Поднимаю warp...${NC}"
- awg_warp_up && echo -e "${GREEN}[OK] WARP подключён.${NC}" && log_action "AWG START" \
- || echo -e "${RED}Ошибка.${NC}"
+ if awg_warp_up; then
+ echo -e "${GREEN}[OK] WARP подключён.${NC}"; log_action "AWG START"
+ else
+ echo -e "${RED}Ошибка.${NC}"
+ fi
read -p "Enter..."
}
@@ -649,8 +655,8 @@ rekey_warp_awg() {
awg_apply_rules
awg_patch_start_sh
awg_detect_warp_exit_ip
- echo -e "${GREEN} ✓ Готово!"
- [ -n "$AWG_WARP_EXIT_IP" ] && echo -e " Новый WARP IP: ${AWG_WARP_EXIT_IP}${NC}"
+ echo -e "${GREEN} ✓ Готово!${NC}"
+ [ -n "$AWG_WARP_EXIT_IP" ] && echo -e " ${WHITE}Новый WARP IP: ${GREEN}${AWG_WARP_EXIT_IP}${NC}"
log_action "AWG REKEY: warp_ip=${AWG_WARP_EXIT_IP}"
read -p "Enter..."
}
@@ -828,105 +834,72 @@ awg_get_client_ips() {
fi
}
-awg_add_clients_ssh() {
- awg_get_client_ips; awg_parse_clients_table; awg_load_clients
- local warp_set=" ${AWG_SELECTED_IPS[*]+"${AWG_SELECTED_IPS[*]}"} "
- clear; echo -e "\n${CYAN}━━━ Добавить клиентов в WARP ━━━${NC}\n"
- if [ ${#AWG_CLIENT_IPS[@]} -eq 0 ]; then echo -e "${RED}Нет клиентов.${NC}"; read -p "Enter..."; return; fi
- local i=1
- for ip in "${AWG_CLIENT_IPS[@]}"; do
- local label; label=$(awg_format_label "$ip")
- if [[ "$warp_set" == *" $ip "* ]]; then
- echo -e " ${GREEN}$i) $label [WARP]${NC}"
- else
- echo " $i) $label"
+awg_toggle_clients_ssh() {
+ while true; do
+ awg_get_client_ips; awg_parse_clients_table; awg_load_clients
+ local warp_set=" ${AWG_SELECTED_IPS[*]+"${AWG_SELECTED_IPS[*]}"} "
+
+ clear; echo -e "\n${CYAN}━━━ Клиенты WARP ━━━${NC}\n"
+
+ if [ ${#AWG_CLIENT_IPS[@]} -eq 0 ]; then
+ echo -e " ${RED}Нет клиентов в конфиге VPN.${NC}"
+ read -p "Enter..."; return
fi
- ((i++))
- done
- echo " all) все клиенты"
- echo -e " ${DIM}0) Отмена${NC}\n"
- read -p "Номер (через запятую) или all: " answer
- [ -z "$answer" ] || [ "$answer" = "0" ] && return
- local -a new_ips=()
- if [ "$answer" = "all" ]; then
- new_ips=("${AWG_CLIENT_IPS[@]}")
- else
- IFS=',' read -ra parts <<< "$answer"
- for p in "${parts[@]}"; do
- p=$(echo "$p" | xargs)
- [[ "$p" =~ ^[0-9]+$ ]] && (( p >= 1 && p <= ${#AWG_CLIENT_IPS[@]} )) && new_ips+=("${AWG_CLIENT_IPS[$((p-1))]}")
+ local i=1 warp_count=0
+ for ip in "${AWG_CLIENT_IPS[@]}"; do
+ local label; label=$(awg_format_label "$ip")
+ if [[ "$warp_set" == *" $ip "* ]]; then
+ echo -e " ${GREEN}$i) ✅ $label${NC}"
+ ((warp_count++))
+ else
+ echo -e " $i) ☐ $label"
+ fi
+ ((i++))
done
- fi
- echo ""
- for nip in "${new_ips[@]}"; do
- local found=0
- for eip in "${AWG_SELECTED_IPS[@]}"; do [ "$nip" = "$eip" ] && found=1 && break; done
- if [ "$found" -eq 0 ]; then
- AWG_SELECTED_IPS+=("$nip")
- echo -e " ${GREEN}+${NC} $(awg_format_label "$nip")"
+ echo ""
+ echo -e " ${WHITE}В WARP: ${CYAN}${warp_count}${NC} из ${#AWG_CLIENT_IPS[@]}"
+ echo ""
+ echo -e " ${DIM}Введите номер для переключения (через запятую)${NC}"
+ echo -e " ${DIM}all) все в WARP | none) убрать всех | 0) назад${NC}"
+ echo ""
+ read -p " Выбор: " answer
+
+ [ -z "$answer" ] || [ "$answer" = "0" ] && return
+
+ local changed=0
+ if [ "$answer" = "all" ]; then
+ AWG_SELECTED_IPS=("${AWG_CLIENT_IPS[@]}")
+ changed=1
+ elif [ "$answer" = "none" ]; then
+ AWG_SELECTED_IPS=()
+ changed=1
else
- echo -e " ${DIM}Уже в WARP: $(awg_format_label "$nip")${NC}"
+ IFS=',' read -ra parts <<< "$answer"
+ for p in "${parts[@]}"; do
+ p=$(echo "$p" | xargs)
+ if [[ "$p" =~ ^[0-9]+$ ]] && (( p >= 1 && p <= ${#AWG_CLIENT_IPS[@]} )); then
+ local ip="${AWG_CLIENT_IPS[$((p-1))]}"
+ if [[ "$warp_set" == *" $ip "* ]]; then
+ local -a tmp=()
+ for eip in "${AWG_SELECTED_IPS[@]}"; do
+ [ "$eip" != "$ip" ] && tmp+=("$eip")
+ done
+ AWG_SELECTED_IPS=("${tmp[@]+"${tmp[@]}"}")
+ else
+ AWG_SELECTED_IPS+=("$ip")
+ fi
+ changed=1
+ fi
+ done
+ fi
+
+ if [ "$changed" -eq 1 ]; then
+ awg_save_clients; awg_apply_rules; awg_patch_start_sh
+ log_action "AWG TOGGLE: ${#AWG_SELECTED_IPS[@]} clients in WARP"
fi
done
-
- awg_save_clients; awg_apply_rules; awg_patch_start_sh
- echo -e "\n${GREEN}Готово. Правила применены.${NC}"
- log_action "AWG ADD CLIENTS: ${#AWG_SELECTED_IPS[@]} total"
- read -p "Enter..."
-}
-
-awg_remove_clients_ssh() {
- awg_load_clients
- if [ ${#AWG_SELECTED_IPS[@]} -eq 0 ]; then echo -e "\n${YELLOW}Нет клиентов в WARP.${NC}"; read -p "Enter..."; return; fi
- awg_parse_clients_table
- clear; echo -e "\n${CYAN}━━━ Убрать клиентов из WARP ━━━${NC}\n"
- local i=1
- for ip in "${AWG_SELECTED_IPS[@]}"; do echo " $i) $(awg_format_label "$ip")"; ((i++)); done
- echo " all) убрать всех"
- echo -e " ${DIM}0) Отмена${NC}\n"
- read -p "Номер (через запятую) или all: " answer
- [ -z "$answer" ] || [ "$answer" = "0" ] && return
-
- local -a remove_ips=()
- if [ "$answer" = "all" ]; then
- remove_ips=("${AWG_SELECTED_IPS[@]}")
- else
- IFS=',' read -ra parts <<< "$answer"
- for p in "${parts[@]}"; do
- p=$(echo "$p" | xargs)
- [[ "$p" =~ ^[0-9]+$ ]] && (( p >= 1 && p <= ${#AWG_SELECTED_IPS[@]} )) && remove_ips+=("${AWG_SELECTED_IPS[$((p-1))]}")
- done
- fi
-
- echo ""
- local -a new_list=()
- for eip in "${AWG_SELECTED_IPS[@]}"; do
- local removing=0
- for rip in "${remove_ips[@]}"; do [ "$eip" = "$rip" ] && removing=1 && break; done
- if [ "$removing" -eq 0 ]; then new_list+=("$eip")
- else echo -e " ${RED}-${NC} $(awg_format_label "$eip")"; fi
- done
- AWG_SELECTED_IPS=("${new_list[@]+"${new_list[@]}"}")
- awg_save_clients; awg_apply_rules; awg_patch_start_sh
- echo -e "\n${GREEN}Готово.${NC}"
- log_action "AWG REMOVE CLIENTS: ${#AWG_SELECTED_IPS[@]} remaining"
- read -p "Enter..."
-}
-
-awg_show_clients_ssh() {
- awg_load_clients; awg_parse_clients_table
- clear; echo -e "\n${CYAN}━━━ Клиенты в WARP ━━━${NC}\n"
- if [ ${#AWG_SELECTED_IPS[@]} -eq 0 ]; then
- echo -e " ${YELLOW}Нет клиентов в WARP.${NC}"
- else
- echo -e " ${WHITE}Всего: ${CYAN}${#AWG_SELECTED_IPS[@]}${NC}\n"
- for ip in "${AWG_SELECTED_IPS[@]}"; do
- echo -e " ${GREEN}●${NC} $(awg_format_label "$ip")"
- done
- fi
- echo ""; read -p "Enter..."
}
# ═══════════════════════════════════════════════════════════════
@@ -1030,7 +1003,7 @@ awg_remove_from_start_sh() {
# ═══════════════════════════════════════════════════════════════
get_warp_status() {
- [ "$MODE" = "3xui" ] && get_warp_status_3xui || get_warp_status_awg
+ if [ "$MODE" = "3xui" ]; then get_warp_status_3xui; else get_warp_status_awg; fi
}
get_warp_ip() {
@@ -1042,7 +1015,7 @@ get_warp_ip() {
}
is_warp_running() {
- [ "$MODE" = "3xui" ] && is_warp_running_3xui || is_warp_running_awg
+ if [ "$MODE" = "3xui" ]; then is_warp_running_3xui; else is_warp_running_awg; fi
}
# ═══════════════════════════════════════════════════════════════
@@ -1107,15 +1080,15 @@ kbd_main_awg() {
[{"text":"📊 Статус","callback_data":"st"},{"text":"🌐 IP","callback_data":"ip"}],
[{"text":"▶️ Запустить","callback_data":"on"},{"text":"⏹ Остановить","callback_data":"off"}],
[{"text":"🔑 Перевыпуск","callback_data":"rk"}],
- [{"text":"👥 Клиенты WARP","callback_data":"cl"},{"text":"➕ Добавить","callback_data":"cla"}],
- [{"text":"➖ Убрать","callback_data":"clr"},{"text":"🔄 Контейнер","callback_data":"rc"}],
+ [{"text":"👥 Клиенты WARP","callback_data":"cl"}],
+ [{"text":"🔄 Контейнер","callback_data":"rc"}],
[{"text":"💻 Система","callback_data":"sys"}],
[{"text":"🏢 Хостинг","callback_data":"promo"}]
]
JSON
}
-kbd_main() { [ "$MODE" = "3xui" ] && kbd_main_3xui || kbd_main_awg; }
+kbd_main() { if [ "$MODE" = "3xui" ]; then kbd_main_3xui; else kbd_main_awg; fi; }
kbd_back() { echo '[[{"text":"⬅️ Меню","callback_data":"m"}]]'; }
kbd_rekey_confirm() { echo '[[{"text":"✅ Да","callback_data":"rk_y"}],[{"text":"⬅️ Отмена","callback_data":"m"}]]'; }
@@ -1132,7 +1105,11 @@ bot_main_menu() {
local mode_label="3X-UI"; [ "$MODE" = "amnezia" ] && mode_label="AmneziaWG"
local text="WARP Manager v${WARP_VERSION} (${mode_label})\nСервер: ${MY_IP:-N/A}\nСтатус: ${ws}${wip}${extra}\n\nВыберите:"
local kbd; kbd=$(kbd_main)
- [ -n "$msg_id" ] && tg_edit "$chat_id" "$msg_id" "$text" "$kbd" || tg_send "$chat_id" "$text" "$kbd"
+ if [ -n "$msg_id" ]; then
+ tg_edit "$chat_id" "$msg_id" "$text" "$kbd"
+ else
+ tg_send "$chat_id" "$text" "$kbd"
+ fi
}
# ─── Bot handlers ────────────────────────────────────────────
@@ -1171,8 +1148,12 @@ bot_handle_callback() {
is_warp_running_3xui && { tg_edit "$chat_id" "$msg_id" "✅ Уже подключён." "$(kbd_back)"; return; }
tg_edit "$chat_id" "$msg_id" "⏳ Подключение..." ""
warp-cli --accept-tos connect > /dev/null 2>&1; sleep 3
- is_warp_running_3xui && { local w; w=$(get_warp_ip_3xui); tg_edit "$chat_id" "$msg_id" "✅ Подключён\nWARP IP: ${w}" "$(kbd_back)"; log_action "BOT 3XUI ON"; } \
- || tg_edit "$chat_id" "$msg_id" "❌ Ошибка." "$(kbd_back)"
+ if is_warp_running_3xui; then
+ local w; w=$(get_warp_ip_3xui)
+ tg_edit "$chat_id" "$msg_id" "✅ Подключён\nWARP IP: ${w}" "$(kbd_back)"; log_action "BOT 3XUI ON"
+ else
+ tg_edit "$chat_id" "$msg_id" "❌ Ошибка." "$(kbd_back)"
+ fi
else
is_warp_installed_awg || { tg_edit "$chat_id" "$msg_id" "❌ Не установлен." "$(kbd_back)"; return; }
is_warp_running_awg && { tg_edit "$chat_id" "$msg_id" "✅ Уже работает." "$(kbd_back)"; return; }
@@ -1237,93 +1218,64 @@ bot_handle_callback() {
fi ;;
cl)
- awg_load_clients; awg_parse_clients_table
- local t="👥 Клиенты в WARP (${#AWG_SELECTED_IPS[@]})\n\n"
- if [ ${#AWG_SELECTED_IPS[@]} -eq 0 ]; then
- t+="Нет клиентов."
- else
- for ip in "${AWG_SELECTED_IPS[@]}"; do
- local name; name=$(awg_get_name "$ip")
- t+="● ${ip}"
- [ -n "$name" ] && t+=" ($name)"
- t+="\n"
- done
- fi
- tg_edit "$chat_id" "$msg_id" "$t" "$(kbd_back)" ;;
-
- cla)
awg_get_client_ips; awg_parse_clients_table; awg_load_clients
local warp_set=" ${AWG_SELECTED_IPS[*]+"${AWG_SELECTED_IPS[*]}"} "
- local kbd="["
- local first=1
- for i in "${!AWG_CLIENT_IPS[@]}"; do
- local ip="${AWG_CLIENT_IPS[$i]}"
- local name; name=$(awg_get_name "$ip")
- local label="${ip}"
- [ -n "$name" ] && label="$name"
- local in_warp=0
- [[ "$warp_set" == *" $ip "* ]] && in_warp=1
- [ "$in_warp" -eq 1 ] && label="✅ $label" || label="➕ $label"
- [ "$first" -eq 0 ] && kbd+=","
- kbd+="[{\"text\":\"${label}\",\"callback_data\":\"ca:${i}\"}]"
- first=0
- done
- kbd+=",[{\"text\":\"✅ Все в WARP\",\"callback_data\":\"ca:all\"}]"
- kbd+=",[{\"text\":\"⬅️ Меню\",\"callback_data\":\"m\"}]]"
- tg_edit "$chat_id" "$msg_id" "➕ Добавить в WARP:" "$kbd" ;;
+ local t="👥 Клиенты WARP (${#AWG_SELECTED_IPS[@]} из ${#AWG_CLIENT_IPS[@]})\n\n"
+ local kbd=""
+ if [ ${#AWG_CLIENT_IPS[@]} -eq 0 ]; then
+ t+="Нет клиентов в конфиге VPN."
+ kbd='[[{"text":"⬅️ Меню","callback_data":"m"}]]'
+ else
+ kbd="["
+ local first=1
+ for i in "${!AWG_CLIENT_IPS[@]}"; do
+ local ip="${AWG_CLIENT_IPS[$i]}"
+ local name; name=$(awg_get_name "$ip")
+ local label="${ip}"
+ [ -n "$name" ] && label="$name"
+ if [[ "$warp_set" == *" $ip "* ]]; then
+ t+="✅ ${ip}"
+ [ -n "$name" ] && t+=" ($name)"
+ label="✅ ${label}"
+ else
+ t+="☐ ${ip}"
+ [ -n "$name" ] && t+=" ($name)"
+ label="☐ ${label}"
+ fi
+ t+="\n"
+ [ "$first" -eq 0 ] && kbd+=","
+ kbd+="[{\"text\":\"${label}\",\"callback_data\":\"ct:${i}\"}]"
+ first=0
+ done
+ kbd+=",[{\"text\":\"✅ Все\",\"callback_data\":\"ct:all\"},{\"text\":\"☐ Никого\",\"callback_data\":\"ct:none\"}]"
+ kbd+=",[{\"text\":\"⬅️ Меню\",\"callback_data\":\"m\"}]]"
+ fi
+ tg_edit "$chat_id" "$msg_id" "$t" "$kbd" ;;
- ca:*)
- local idx="${data#ca:}"
+ ct:*)
+ local idx="${data#ct:}"
awg_get_client_ips; awg_load_clients
if [ "$idx" = "all" ]; then
AWG_SELECTED_IPS=("${AWG_CLIENT_IPS[@]}")
+ elif [ "$idx" = "none" ]; then
+ AWG_SELECTED_IPS=()
else
[[ "$idx" =~ ^[0-9]+$ ]] && (( idx < ${#AWG_CLIENT_IPS[@]} )) || { tg_edit "$chat_id" "$msg_id" "❌ Ошибка." "$(kbd_back)"; return; }
local ip="${AWG_CLIENT_IPS[$idx]}"
- local found=0
- for e in "${AWG_SELECTED_IPS[@]}"; do [ "$e" = "$ip" ] && found=1 && break; done
- [ "$found" -eq 0 ] && AWG_SELECTED_IPS+=("$ip")
+ local warp_set=" ${AWG_SELECTED_IPS[*]+"${AWG_SELECTED_IPS[*]}"} "
+ if [[ "$warp_set" == *" $ip "* ]]; then
+ local -a tmp=()
+ for eip in "${AWG_SELECTED_IPS[@]}"; do
+ [ "$eip" != "$ip" ] && tmp+=("$eip")
+ done
+ AWG_SELECTED_IPS=("${tmp[@]+"${tmp[@]}"}")
+ else
+ AWG_SELECTED_IPS+=("$ip")
+ fi
fi
awg_save_clients; awg_apply_rules; awg_patch_start_sh
- tg_edit "$chat_id" "$msg_id" "✅ Обновлено. Клиентов: ${#AWG_SELECTED_IPS[@]}" "$(kbd_back)"
- log_action "BOT AWG ADD: ${#AWG_SELECTED_IPS[@]}" ;;
-
- clr)
- awg_load_clients; awg_parse_clients_table
- if [ ${#AWG_SELECTED_IPS[@]} -eq 0 ]; then
- tg_edit "$chat_id" "$msg_id" "ℹ️ Нет клиентов в WARP." "$(kbd_back)"; return
- fi
- local kbd="["
- local first=1
- for i in "${!AWG_SELECTED_IPS[@]}"; do
- local ip="${AWG_SELECTED_IPS[$i]}"
- local name; name=$(awg_get_name "$ip")
- local label="${ip}"
- [ -n "$name" ] && label="$name"
- [ "$first" -eq 0 ] && kbd+=","
- kbd+="[{\"text\":\"❌ ${label}\",\"callback_data\":\"cr:${i}\"}]"
- first=0
- done
- kbd+=",[{\"text\":\"❌ Убрать всех\",\"callback_data\":\"cr:all\"}]"
- kbd+=",[{\"text\":\"⬅️ Меню\",\"callback_data\":\"m\"}]]"
- tg_edit "$chat_id" "$msg_id" "➖ Убрать из WARP:" "$kbd" ;;
-
- cr:*)
- local idx="${data#cr:}"
- awg_load_clients
- if [ "$idx" = "all" ]; then
- AWG_SELECTED_IPS=()
- else
- [[ "$idx" =~ ^[0-9]+$ ]] && (( idx < ${#AWG_SELECTED_IPS[@]} )) || { tg_edit "$chat_id" "$msg_id" "❌ Ошибка." "$(kbd_back)"; return; }
- local -a new=()
- for i in "${!AWG_SELECTED_IPS[@]}"; do
- [ "$i" -ne "$idx" ] && new+=("${AWG_SELECTED_IPS[$i]}")
- done
- AWG_SELECTED_IPS=("${new[@]+"${new[@]}"}")
- fi
- awg_save_clients; awg_apply_rules; awg_patch_start_sh
- tg_edit "$chat_id" "$msg_id" "✅ Обновлено. Клиентов: ${#AWG_SELECTED_IPS[@]}" "$(kbd_back)"
- log_action "BOT AWG REMOVE: ${#AWG_SELECTED_IPS[@]}" ;;
+ log_action "BOT AWG TOGGLE: ${#AWG_SELECTED_IPS[@]}"
+ bot_handle_callback "$chat_id" "$msg_id" "" "cl" ;;
rc)
if [ "$MODE" != "amnezia" ]; then
@@ -1387,7 +1339,11 @@ bot_daemon() {
mtx=$(echo "$upd" | jq -r '.message.text // empty')
if [ -n "$mci" ] && [ -n "$mtx" ]; then
[ -n "$BOT_CHAT_ID" ] && [ "$mci" != "$BOT_CHAT_ID" ] && { tg_send "$mci" "⛔ Нет доступа.\nChat ID: $mci" "" > /dev/null; continue; }
- [[ "$mtx" == "/start" || "$mtx" == "/menu" ]] && bot_main_menu "$mci" || tg_send "$mci" "Используйте /start или /menu" "" > /dev/null
+ if [[ "$mtx" == "/start" || "$mtx" == "/menu" ]]; then
+ bot_main_menu "$mci"
+ else
+ tg_send "$mci" "Используйте /start или /menu" "" > /dev/null
+ fi
fi
fi
done
@@ -1413,8 +1369,11 @@ RestartSec=5
WantedBy=multi-user.target
EOF
systemctl daemon-reload; systemctl enable warp-bot > /dev/null 2>&1; systemctl start warp-bot; sleep 1
- systemctl is-active warp-bot &>/dev/null && echo -e "${GREEN}[OK] Бот запущен.${NC}" && log_action "Bot started" \
- || echo -e "${RED}[ERROR] journalctl -u warp-bot${NC}"
+ if systemctl is-active warp-bot &>/dev/null; then
+ echo -e "${GREEN}[OK] Бот запущен.${NC}"; log_action "Bot started"
+ else
+ echo -e "${RED}[ERROR] journalctl -u warp-bot${NC}"
+ fi
}
stop_bot() {
@@ -1446,8 +1405,11 @@ bot_menu() {
2) [ -z "${BOT_TOKEN:-}" ] && echo -e "${RED}Сначала токен!${NC}" && read -p "" && continue
echo -e "${YELLOW}Отправьте боту сообщение, нажмите Enter.${NC}"; read -p ""
local c; c=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?limit=1&offset=-1" | jq -r '.result[0].message.chat.id // empty')
- [ -n "$c" ] && save_config_val "BOT_CHAT_ID" "$c" && BOT_CHAT_ID="$c" && echo -e "${GREEN}Chat ID: $c${NC}" \
- || echo -e "${RED}Не удалось.${NC}"
+ if [ -n "$c" ]; then
+ save_config_val "BOT_CHAT_ID" "$c"; BOT_CHAT_ID="$c"; echo -e "${GREEN}Chat ID: $c${NC}"
+ else
+ echo -e "${RED}Не удалось.${NC}"
+ fi
read -p "Enter..." ;;
3) echo "Chat ID:"; read -p "> " c
[ -n "$c" ] && save_config_val "BOT_CHAT_ID" "$c" && BOT_CHAT_ID="$c" && echo -e "${GREEN}OK${NC}"
@@ -1518,7 +1480,7 @@ full_uninstall() {
read -p "$(echo -e "${RED}Удалить полностью? (y/n): ${NC}")" c1
[[ "$c1" != "y" ]] && return
- [ "$MODE" = "3xui" ] && uninstall_3xui || uninstall_awg
+ if [ "$MODE" = "3xui" ]; then uninstall_3xui; else uninstall_awg; fi
rm -rf "$WARP_DIR" "$WARP_LOG"
echo -e " ${GREEN}✓${NC} Конфигурация и логи"
@@ -1568,43 +1530,39 @@ show_menu() {
if [ "$MODE" = "3xui" ]; then
echo -e "\n${CYAN}── 3X-UI ──────────────────────────────────────────────${NC}"
- echo -e " 6) 📋 ${CYAN}JSON конфиг для 3X-UI${NC}"
+ echo -e " 6) 📋 ${CYAN}Настройки SOCKS5 / JSON для 3X-UI${NC}"
echo -e " 7) 🔧 ${WHITE}Изменить порт SOCKS5${NC}"
fi
if [ "$MODE" = "amnezia" ]; then
echo -e "\n${CYAN}── AmneziaWG ──────────────────────────────────────────${NC}"
- echo -e " 6) ${GREEN}➕ Добавить клиентов в WARP${NC}"
- echo -e " 7) ${YELLOW}➖ Убрать клиентов из WARP${NC}"
- echo -e " 8) 👥 ${WHITE}Показать клиентов в WARP${NC}"
- echo -e " 9) 🔄 ${CYAN}Перезапуск контейнера${NC}"
+ echo -e " 6) 👥 ${GREEN}Управление клиентами WARP${NC}"
+ echo -e " 7) 🔄 ${CYAN}Перезапуск контейнера${NC}"
fi
echo -e "\n${CYAN}── Telegram-бот ───────────────────────────────────────${NC}"
- echo -e " 10) 🤖 ${CYAN}Настройка и управление ботом${NC}"
+ echo -e " 8) 🤖 ${CYAN}Настройка и управление ботом${NC}"
echo -e "\n${CYAN}── Прочее ─────────────────────────────────────────────${NC}"
- echo -e " 11) ${YELLOW}PROMO${NC}"
- echo -e " 12) ${MAGENTA}📚 Инструкция${NC}"
- echo -e " 13) ${RED}⚠ Полное удаление${NC}"
+ echo -e " 9) ${YELLOW}PROMO${NC}"
+ echo -e " 10) ${MAGENTA}📚 Инструкция${NC}"
+ echo -e " 11) ${RED}⚠ Полное удаление${NC}"
echo -e " 0) Выход"
echo -e "${CYAN}──────────────────────────────────────────────────────${NC}"
read -p " Выбор: " ch
case $ch in
- 1) [ "$MODE" = "3xui" ] && install_warp_3xui || install_warp_awg ;;
- 2) [ "$MODE" = "3xui" ] && start_warp_3xui || start_warp_awg ;;
- 3) [ "$MODE" = "3xui" ] && stop_warp_3xui || stop_warp_awg ;;
- 4) [ "$MODE" = "3xui" ] && show_status_3xui || show_status_awg ;;
- 5) [ "$MODE" = "3xui" ] && rekey_warp_3xui || rekey_warp_awg ;;
- 6) [ "$MODE" = "3xui" ] && show_xui_json || awg_add_clients_ssh ;;
- 7) [ "$MODE" = "3xui" ] && change_port_3xui || awg_remove_clients_ssh ;;
- 8) [ "$MODE" = "amnezia" ] && awg_show_clients_ssh ;;
- 9) [ "$MODE" = "amnezia" ] && awg_restart_container ;;
- 10) bot_menu ;;
- 11) show_promo ;;
- 12) show_info ;;
- 13) full_uninstall ;;
+ 1) if [ "$MODE" = "3xui" ]; then install_warp_3xui; else install_warp_awg; fi ;;
+ 2) if [ "$MODE" = "3xui" ]; then start_warp_3xui; else start_warp_awg; fi ;;
+ 3) if [ "$MODE" = "3xui" ]; then stop_warp_3xui; else stop_warp_awg; fi ;;
+ 4) if [ "$MODE" = "3xui" ]; then show_status_3xui; else show_status_awg; fi ;;
+ 5) if [ "$MODE" = "3xui" ]; then rekey_warp_3xui; else rekey_warp_awg; fi ;;
+ 6) if [ "$MODE" = "3xui" ]; then show_xui_json; else awg_toggle_clients_ssh; fi ;;
+ 7) if [ "$MODE" = "3xui" ]; then change_port_3xui; else awg_restart_container; fi ;;
+ 8) bot_menu ;;
+ 9) show_promo ;;
+ 10) show_info ;;
+ 11) full_uninstall ;;
0) exit 0 ;;
esac
done