diff --git a/install.sh b/install.sh index 429e5ab..0ae88bd 100644 --- a/install.sh +++ b/install.sh @@ -2,16 +2,17 @@ set -o pipefail # ══════════════════════════════════════════════════════════════ -# KASKAD v2.0 — Cascading VPN / Proxy Manager -# Telegram Bot · Live Ping · Monitoring · Alerts +# KASKAD PRO v2.1 — Cascading VPN / Proxy Manager +# Telegram Bot · Live Ping · Monitoring · Alerts · System Stats # Channel: https://www.youtube.com/@antenkaru # ══════════════════════════════════════════════════════════════ -KASKAD_VERSION="2.0" +KASKAD_VERSION="2.1" KASKAD_DIR="/etc/kaskad" KASKAD_CONF="$KASKAD_DIR/config" KASKAD_LOG="/var/log/kaskad.log" MONITOR_DIR="$KASKAD_DIR/monitors" +ALIASES_FILE="$KASKAD_DIR/aliases" BOT_STATE_DIR="$KASKAD_DIR/bot_state" BOT_PID_FILE="/var/run/kaskad_bot.pid" MONITOR_PID_FILE="/var/run/kaskad_monitor.pid" @@ -24,15 +25,18 @@ IFACE="" MY_IP="" BOT_TOKEN="" BOT_CHAT_ID="" +MENU_STYLE="" # ─── Config ─────────────────────────────────────────────────── init_config() { mkdir -p "$KASKAD_DIR" "$MONITOR_DIR" "$BOT_STATE_DIR" + touch "$ALIASES_FILE" if [ ! -f "$KASKAD_CONF" ]; then cat > "$KASKAD_CONF" <<'CONF' BOT_TOKEN="" BOT_CHAT_ID="" +MENU_STYLE="compact" CONF fi source "$KASKAD_CONF" @@ -48,6 +52,33 @@ save_config_val() { source "$KASKAD_CONF" } +# ─── Aliases (server names) ────────────────────────────────── + +set_alias() { + local ip="$1" name="$2" + if grep -q "^${ip}=" "$ALIASES_FILE" 2>/dev/null; then + sed -i "s|^${ip}=.*|${ip}=${name}|" "$ALIASES_FILE" + else + echo "${ip}=${name}" >> "$ALIASES_FILE" + fi +} + +get_alias() { + local ip="$1" + grep "^${ip}=" "$ALIASES_FILE" 2>/dev/null | head -1 | cut -d= -f2- +} + +fmt_ip() { + local ip="$1" + local alias + alias=$(get_alias "$ip") + if [ -n "$alias" ]; then + echo "${alias} (${ip})" + else + echo "$ip" + fi +} + # ─── Logging ────────────────────────────────────────────────── log_action() { @@ -92,6 +123,20 @@ read_validated_port() { done } +read_server_name() { + local ip="$1" + local existing + existing=$(get_alias "$ip") + if [ -n "$existing" ]; then + echo -e "Текущее имя для $ip: ${GREEN}$existing${NC}" + fi + echo -e "Введите имя сервера (или Enter — пропустить):" + read -p "> " _RET_NAME + if [ -n "$_RET_NAME" ]; then + set_alias "$ip" "$_RET_NAME" + fi +} + # ─── System ─────────────────────────────────────────────────── check_root() { @@ -147,11 +192,11 @@ prepare_system() { if [ "$need_install" -eq 1 ]; then if command -v apt-get &>/dev/null; then apt-get update -y > /dev/null 2>&1 - apt-get install -y iptables-persistent netfilter-persistent qrencode jq curl > /dev/null 2>&1 + apt-get install -y iptables-persistent netfilter-persistent qrencode jq curl procps > /dev/null 2>&1 elif command -v dnf &>/dev/null; then - dnf install -y iptables-services jq qrencode curl > /dev/null 2>&1 + dnf install -y iptables-services jq qrencode curl procps-ng > /dev/null 2>&1 elif command -v yum &>/dev/null; then - yum install -y iptables-services jq qrencode curl > /dev/null 2>&1 + yum install -y iptables-services jq qrencode curl procps-ng > /dev/null 2>&1 else echo -e "${RED}[ERROR] Неподдерживаемый пакетный менеджер!${NC}" exit 1 @@ -159,6 +204,36 @@ prepare_system() { fi } +# ─── System Stats ───────────────────────────────────────────── + +get_system_stats() { + local cpu_line load_avg mem_info disk_info uptime_str top_procs + cpu_line=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || echo "?") + load_avg=$(cat /proc/loadavg 2>/dev/null | awk '{print $1, $2, $3}') + mem_info=$(free -m 2>/dev/null | awk '/^Mem:/ {printf "%d/%dMB (%.1f%%)", $3, $2, $3/$2*100}') + local swap_info + swap_info=$(free -m 2>/dev/null | awk '/^Swap:/ {if($2>0) printf "%d/%dMB", $3, $2; else print "N/A"}') + disk_info=$(df -h / 2>/dev/null | awk 'NR==2 {printf "%s/%s (%s)", $3, $2, $5}') + uptime_str=$(uptime -p 2>/dev/null || uptime | sed 's/.*up /up /' | sed 's/,.*load.*//') + top_procs=$(ps aux --sort=-%cpu 2>/dev/null | head -8 | awk 'NR>1 {printf "%-6s %-4s%% %-4s%% %s\n", $2, $3, $4, $11}') + + local cpu_usage + cpu_usage=$(awk '/^cpu / {u=$2+$4; t=$2+$3+$4+$5+$6+$7+$8; if(t>0) printf "%.1f", u/t*100; else print "0"}' /proc/stat 2>/dev/null) + + local result="" + result+="📊 Системная информация\n\n" + result+="Uptime: ${uptime_str}\n" + result+="CPU: ${cpu_line} ядер | загрузка: ${cpu_usage}%\n" + result+="Load Avg: ${load_avg}\n" + result+="RAM: ${mem_info}\n" + result+="Swap: ${swap_info}\n" + result+="Disk /: ${disk_info}\n\n" + result+="Топ процессов (CPU):\n" + result+="
PID    CPU%  MEM%  CMD\n"
+    result+="${top_procs}
" + echo "$result" +} + # ─── iptables helpers ───────────────────────────────────────── get_rules_list() { @@ -234,7 +309,7 @@ apply_iptables_rules() { save_iptables echo -e "${GREEN}[SUCCESS] $name настроен!${NC}" - echo -e "$proto: Вход :$in_port -> Выход $target_ip:$out_port" + echo -e "$proto: ${MY_IP:-*}:$in_port -> $target_ip:$out_port" } # ─── Interactive rule configuration ────────────────────────── @@ -246,13 +321,14 @@ configure_rule() { read_validated_ip "Введите IP адрес назначения:" local target_ip="$_RET_IP" + read_server_name "$target_ip" check_target_reachable "$target_ip" || return read_validated_port "Введите Порт (одинаковый для входа и выхода):" local port="$_RET_PORT" echo -e "\n${YELLOW}Будет создано правило:${NC}" - echo -e " $proto: :$port -> $target_ip:$port" + echo -e " $proto: ${MY_IP:-*}:$port -> $(fmt_ip "$target_ip"):$port" read -p "Применить? (y/n): " confirm [[ "$confirm" != "y" ]] && return @@ -275,6 +351,7 @@ configure_custom_rule() { read_validated_ip "Введите IP адрес назначения (куда отправляем трафик):" local target_ip="$_RET_IP" + read_server_name "$target_ip" check_target_reachable "$target_ip" || return read_validated_port "Введите ${YELLOW}ВХОДЯЩИЙ Порт${NC} (на этом сервере):" @@ -284,7 +361,7 @@ configure_custom_rule() { local out_port="$_RET_PORT" echo -e "\n${YELLOW}Будет создано правило:${NC}" - echo -e " $proto: :$in_port -> $target_ip:$out_port" + echo -e " $proto: ${MY_IP:-*}:$in_port -> $(fmt_ip "$target_ip"):$out_port" read -p "Применить? (y/n): " confirm [[ "$confirm" != "y" ]] && return @@ -296,24 +373,23 @@ configure_custom_rule() { list_active_rules() { echo -e "\n${CYAN}--- Активные переадресации ---${NC}" - echo -e "${MAGENTA}ПОРТ (ВХОД)\tПРОТОКОЛ\tЦЕЛЬ (IP:ВЫХОД)${NC}" + echo -e "${WHITE}Сервер каскада: ${GREEN}${MY_IP:-N/A}${NC}\n" + echo -e "${MAGENTA}ВХОД (IP:ПОРТ)\t\tПРОТОКОЛ\tЦЕЛЬ${NC}" local rules rules=$(get_rules_list) if [ -z "$rules" ]; then echo -e "${YELLOW}Нет активных правил.${NC}" else echo "$rules" | while IFS='|' read -r proto port dest; do - echo -e "$port\t\t$proto\t\t$dest" + local dest_ip="${dest%:*}" + local alias_str + alias_str=$(get_alias "$dest_ip") + local label="$dest" + [ -n "$alias_str" ] && label="$dest [$alias_str]" + echo -e "${MY_IP:-*}:$port\t\t$proto\t\t$label" done fi echo "" - echo -e "${GREEN}Задонатить каналу и автору:${NC}" - if command -v qrencode &>/dev/null; then - qrencode -t ANSIUTF8 "https://pay.cloudtips.ru/p/7410814f" - else - echo "https://pay.cloudtips.ru/p/7410814f" - fi - echo "" read -p "Нажмите Enter..." } @@ -323,7 +399,10 @@ delete_single_rule() { local i=1 while IFS='|' read -r proto port dest; do rules_arr[$i]="$proto|$port|$dest" - echo -e "${YELLOW}[$i]${NC} Вход: $port ($proto) -> Выход: $dest" + local dest_ip="${dest%:*}" + local label + label=$(fmt_ip "$dest_ip") + echo -e "${YELLOW}[$i]${NC} ${MY_IP:-*}:$port ($proto) -> ${dest#*:} @ $label" ((i++)) done <<< "$(get_rules_list)" @@ -339,7 +418,6 @@ delete_single_rule() { IFS='|' read -r d_proto d_port d_dest <<< "${rules_arr[$rule_num]}" local target_ip="${d_dest%:*}" - local target_port="${d_dest#*:}" iptables -t nat -D PREROUTING -p "$d_proto" --dport "$d_port" -j DNAT --to-destination "$d_dest" 2>/dev/null iptables -S INPUT 2>/dev/null | grep "kaskad:${d_port}:${d_proto}" | while read -r rule; do @@ -379,6 +457,33 @@ flush_rules() { read -p "Нажмите Enter..." } +manage_aliases_menu() { + while true; do + clear + echo -e "${CYAN}━━━ Имена серверов ━━━${NC}" + local -a ips=() + while read -r ip; do [ -n "$ip" ] && ips+=("$ip"); done <<< "$(get_target_ips)" + if [ ${#ips[@]} -eq 0 ]; then + echo -e "${YELLOW}Нет целевых серверов.${NC}" + read -p "Enter..." + return + fi + for i in "${!ips[@]}"; do + echo -e " ${YELLOW}[$((i+1))]${NC} ${ips[$i]} — ${GREEN}$(get_alias "${ips[$i]}" || echo "без имени")${NC}" + done + echo -e " ${YELLOW}[0]${NC} Назад" + read -p "Выберите сервер для переименования: " choice + [[ "$choice" == "0" || -z "$choice" ]] && return + local idx=$((choice - 1)) + [ -z "${ips[$idx]:-}" ] && continue + echo -e "Новое имя для ${ips[$idx]}:" + read -p "> " new_name + [ -n "$new_name" ] && set_alias "${ips[$idx]}" "$new_name" + echo -e "${GREEN}[OK] Сохранено.${NC}" + read -p "Enter..." + done +} + # ─── Auto-update ────────────────────────────────────────────── self_update() { @@ -403,29 +508,25 @@ self_update() { cp -f /tmp/kaskad_update.sh /usr/local/bin/gokaskad chmod +x /usr/local/bin/gokaskad rm -f /tmp/kaskad_update.sh - systemctl restart kaskad-bot 2>/dev/null systemctl restart kaskad-monitor 2>/dev/null - echo -e "${GREEN}[OK] Скрипт обновлён! Службы перезапущены.${NC}" echo -e "${GREEN}Перезапустите меню: gokaskad${NC}" log_action "Self-update completed" else echo -e "${RED}[ERROR] Не удалось скачать обновление.${NC}" - echo -e "${YELLOW}Если репо приватный, задайте токен: меню 8 → доп. опции${NC}" rm -f /tmp/kaskad_update.sh fi read -p "Нажмите Enter..." } # ═══════════════════════════════════════════════════════════════ -# LIVE PING (Terminal — refreshes every ~1 second) +# LIVE PING # ═══════════════════════════════════════════════════════════════ ping_live() { local ip="$1" - local -a results=() - local -a lines=() + local -a results=() lines=() local count=0 lost=0 running=1 trap 'running=0' INT @@ -445,7 +546,7 @@ ping_live() { clear echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" - echo -e "${CYAN} Live Ping: ${WHITE}$ip${CYAN} [Ctrl+C — стоп]${NC}" + echo -e "${CYAN} Live Ping: ${WHITE}$(fmt_ip "$ip")${CYAN} [Ctrl+C — стоп]${NC}" echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}" local show=20 @@ -485,14 +586,14 @@ ping_menu() { done <<< "$(get_target_ips)" if [ ${#ips[@]} -eq 0 ]; then - echo -e "${YELLOW}Нет активных целевых серверов. Сначала добавьте правило.${NC}" + echo -e "${YELLOW}Нет активных целевых серверов.${NC}" read -p "Нажмите Enter..." return fi echo -e "Активные целевые серверы:" for i in "${!ips[@]}"; do - echo -e " ${YELLOW}[$((i+1))]${NC} ${ips[$i]}" + echo -e " ${YELLOW}[$((i+1))]${NC} $(fmt_ip "${ips[$i]}")" done echo -e " ${YELLOW}[0]${NC} Отмена" @@ -505,105 +606,49 @@ ping_menu() { } # ═══════════════════════════════════════════════════════════════ -# MONITORING +# MONITORING (with configurable alert cooldown + auto start/stop) # ═══════════════════════════════════════════════════════════════ add_monitor() { - local ip="$1" interval="$2" threshold="$3" + local ip="$1" interval="$2" threshold="$3" cooldown="${4:-300}" cat > "$MONITOR_DIR/${ip}.conf" < "$alert_file" - log_action "ALERT: $ip ping=${ping_ms}ms threshold=${threshold}ms" - - source "$KASKAD_CONF" 2>/dev/null - if [ -n "${BOT_TOKEN:-}" ] && [ -n "${BOT_CHAT_ID:-}" ]; then - local text - if [ "$ping_ms" = "TIMEOUT" ]; then - text="⚠️ ALERT: ${ip}\nPing: TIMEOUT (порог: ${threshold}ms)" - else - text="⚠️ ALERT: ${ip}\nPing: ${ping_ms}ms (порог: ${threshold}ms)" +sync_monitoring_service() { + if has_monitors; then + if ! systemctl is-active kaskad-monitor &>/dev/null 2>&1; then + start_monitoring_silent + fi + else + if systemctl is-active kaskad-monitor &>/dev/null 2>&1; then + stop_monitoring_silent fi - tg_send "$BOT_CHAT_ID" "$text" "" > /dev/null 2>&1 fi } -monitor_daemon() { - log_action "Monitor daemon started (PID $$)" - echo $$ > "$MONITOR_PID_FILE" - - while true; do - local now - now=$(date +%s) - for conf_file in "$MONITOR_DIR"/*.conf; do - [ -f "$conf_file" ] || continue - local MON_IP="" MON_INTERVAL="" MON_THRESHOLD="" - source "$conf_file" - - local check_file="$MONITOR_DIR/.last_check_${MON_IP}" - local last_check=0 - [ -f "$check_file" ] && last_check=$(cat "$check_file") - - if (( now - last_check >= MON_INTERVAL )); then - echo "$now" > "$check_file" - local ping_result - ping_result=$(ping -c 1 -W 3 "$MON_IP" 2>/dev/null | grep -oP 'time=\K[\d.]+') - - if [ -z "$ping_result" ]; then - monitor_alert "$MON_IP" "TIMEOUT" "$MON_THRESHOLD" - else - local ping_int - ping_int=$(awk "BEGIN {printf \"%d\", $ping_result + 0.5}") - if (( ping_int > MON_THRESHOLD )); then - monitor_alert "$MON_IP" "$ping_result" "$MON_THRESHOLD" - fi - fi - fi - done - sleep 1 - done -} - -start_monitoring() { - if [ -f "$MONITOR_PID_FILE" ] && kill -0 "$(cat "$MONITOR_PID_FILE")" 2>/dev/null; then - echo -e "${YELLOW}Мониторинг уже запущен (PID $(cat "$MONITOR_PID_FILE")).${NC}" - return - fi - +start_monitoring_silent() { cat > /etc/systemd/system/kaskad-monitor.service < /dev/null 2>&1 - systemctl start kaskad-monitor - echo -e "${GREEN}[OK] Мониторинг запущен.${NC}" - log_action "Monitoring service started" + systemctl start kaskad-monitor 2>/dev/null + log_action "Monitoring auto-started" } -stop_monitoring() { +stop_monitoring_silent() { systemctl stop kaskad-monitor 2>/dev/null systemctl disable kaskad-monitor 2>/dev/null rm -f "$MONITOR_PID_FILE" - echo -e "${GREEN}[OK] Мониторинг остановлен.${NC}" - log_action "Monitoring service stopped" + log_action "Monitoring auto-stopped (no monitors)" +} + +list_monitors() { + local found=0 + for conf in "$MONITOR_DIR"/*.conf; do + [ -f "$conf" ] || continue + found=1 + local MON_IP="" MON_INTERVAL="" MON_THRESHOLD="" MON_COOLDOWN=300 + source "$conf" + local label + label=$(fmt_ip "$MON_IP") + echo -e " ${WHITE}$label${NC} инт: ${MON_INTERVAL}s порог: ${MON_THRESHOLD}ms уведомл: ${MON_COOLDOWN}s" + done + if [ "$found" -eq 0 ]; then + echo -e " ${YELLOW}Нет настроенных мониторов.${NC}" + fi +} + +monitor_alert() { + local ip="$1" ping_ms="$2" threshold="$3" cooldown="${4:-300}" + local alert_file="$MONITOR_DIR/.last_alert_${ip}" + local last_alert=0 + [ -f "$alert_file" ] && last_alert=$(cat "$alert_file") + local now + now=$(date +%s) + + if (( now - last_alert < cooldown )); then return; fi + echo "$now" > "$alert_file" + log_action "ALERT: $ip ping=${ping_ms}ms threshold=${threshold}ms" + + source "$KASKAD_CONF" 2>/dev/null + if [ -n "${BOT_TOKEN:-}" ] && [ -n "${BOT_CHAT_ID:-}" ]; then + local label + label=$(get_alias "$ip") + local header="$ip" + [ -n "$label" ] && header="$label ($ip)" + local text + if [ "$ping_ms" = "TIMEOUT" ]; then + text="⚠️ ALERT: ${header}\nPing: TIMEOUT (порог: ${threshold}ms)" + else + text="⚠️ ALERT: ${header}\nPing: ${ping_ms}ms (порог: ${threshold}ms)" + fi + tg_send "$BOT_CHAT_ID" "$text" "" > /dev/null 2>&1 + fi +} + +monitor_daemon() { + log_action "Monitor daemon started (PID $$)" + echo $$ > "$MONITOR_PID_FILE" + + while true; do + local now + now=$(date +%s) + for conf_file in "$MONITOR_DIR"/*.conf; do + [ -f "$conf_file" ] || continue + local MON_IP="" MON_INTERVAL="" MON_THRESHOLD="" MON_COOLDOWN=300 + source "$conf_file" + + local check_file="$MONITOR_DIR/.last_check_${MON_IP}" + local last_check=0 + [ -f "$check_file" ] && last_check=$(cat "$check_file") + + if (( now - last_check >= MON_INTERVAL )); then + echo "$now" > "$check_file" + local ping_result + ping_result=$(ping -c 1 -W 3 "$MON_IP" 2>/dev/null | grep -oP 'time=\K[\d.]+') + + if [ -z "$ping_result" ]; then + monitor_alert "$MON_IP" "TIMEOUT" "$MON_THRESHOLD" "$MON_COOLDOWN" + else + local ping_int + ping_int=$(awk "BEGIN {printf \"%d\", $ping_result + 0.5}") + if (( ping_int > MON_THRESHOLD )); then + monitor_alert "$MON_IP" "$ping_result" "$MON_THRESHOLD" "$MON_COOLDOWN" + fi + fi + fi + done + sleep 1 + done } monitoring_menu() { while true; do clear local mon_status="${RED}Остановлен${NC}" - if [ -f "$MONITOR_PID_FILE" ] && kill -0 "$(cat "$MONITOR_PID_FILE" 2>/dev/null)" 2>/dev/null; then - mon_status="${GREEN}Работает (PID $(cat "$MONITOR_PID_FILE"))${NC}" + if systemctl is-active kaskad-monitor &>/dev/null 2>&1; then + mon_status="${GREEN}Работает${NC}" fi - echo -e "${CYAN}━━━ Мониторинг ━━━${NC}" + echo -e "${CYAN}━━━ Мониторинг (авто) ━━━${NC}" echo -e "Статус: $mon_status" + echo -e "${YELLOW}Служба запускается/останавливается автоматически.${NC}" echo "" list_monitors echo "" echo -e "1) Добавить мониторинг" echo -e "2) Удалить мониторинг" - echo -e "3) Запустить службу мониторинга" - echo -e "4) Остановить службу мониторинга" echo -e "0) Назад" read -p "Выбор: " choice @@ -663,7 +785,7 @@ monitoring_menu() { continue fi echo "Серверы:" - for i in "${!ips[@]}"; do echo -e " ${YELLOW}[$((i+1))]${NC} ${ips[$i]}"; done + for i in "${!ips[@]}"; do echo -e " ${YELLOW}[$((i+1))]${NC} $(fmt_ip "${ips[$i]}")"; done read -p "Сервер: " s_choice local s_idx=$((s_choice - 1)) [ -z "${ips[$s_idx]:-}" ] && continue @@ -679,11 +801,22 @@ monitoring_menu() { 1) interval=10 ;; 2) interval=60 ;; 3) interval=300 ;; esac - read_validated_port "Порог уведомления (мс, 1-65535):" + read_validated_port "Порог уведомления (мс):" local threshold="$_RET_PORT" - add_monitor "$sel_ip" "$interval" "$threshold" - echo -e "${GREEN}[OK] Мониторинг для $sel_ip добавлен.${NC}" + echo -e "Частота уведомлений (не чаще чем):" + echo -e " 1) Каждые 10 сек" + echo -e " 2) Каждые 60 сек" + echo -e " 3) Каждые 5 мин" + echo -e " 4) Каждые 15 мин" + read -p "Выбор: " cd_ch + local cooldown=300 + case $cd_ch in + 1) cooldown=10 ;; 2) cooldown=60 ;; 3) cooldown=300 ;; 4) cooldown=900 ;; + esac + + add_monitor "$sel_ip" "$interval" "$threshold" "$cooldown" + echo -e "${GREEN}[OK] Мониторинг для $(fmt_ip "$sel_ip") добавлен.${NC}" read -p "Enter..." ;; 2) @@ -699,14 +832,12 @@ monitoring_menu() { read -p "Enter..." continue fi - for i in "${!mon_ips[@]}"; do echo -e " ${YELLOW}[$((i+1))]${NC} ${mon_ips[$i]}"; done + for i in "${!mon_ips[@]}"; do echo -e " ${YELLOW}[$((i+1))]${NC} $(fmt_ip "${mon_ips[$i]}")"; done read -p "Номер для удаления: " d_ch local d_idx=$((d_ch - 1)) [ -n "${mon_ips[$d_idx]:-}" ] && remove_monitor "${mon_ips[$d_idx]}" && echo -e "${GREEN}[OK] Удалено.${NC}" read -p "Enter..." ;; - 3) start_monitoring; read -p "Enter..." ;; - 4) stop_monitoring; read -p "Enter..." ;; 0) return ;; esac done @@ -774,19 +905,44 @@ bot_clear_state() { # ─── Bot keyboards ─────────────────────────────────────────── +get_menu_style() { + source "$KASKAD_CONF" 2>/dev/null + echo "${MENU_STYLE:-compact}" +} + kbd_main() { - cat <<'JSON' + local style + style=$(get_menu_style) + if [ "$style" = "large" ]; then + cat <<'JSON' [ - [{"text":"🔀 AWG/WireGuard (UDP)","callback_data":"a_u"}], - [{"text":"🔀 VLESS/XRay (TCP)","callback_data":"a_t"}], - [{"text":"🔀 MTProto/TProxy (TCP)","callback_data":"a_mt"}], - [{"text":"🛠 Custom Rule","callback_data":"a_c"}], - [{"text":"📋 Правила","callback_data":"lr"},{"text":"🏓 Ping","callback_data":"pm"}], + [{"text":"🔀 AmneziaWG / WireGuard (UDP)","callback_data":"a_u"}], + [{"text":"🔀 VLESS / XRay (TCP)","callback_data":"a_t"}], + [{"text":"🔀 MTProto / TProxy (TCP)","callback_data":"a_mt"}], + [{"text":"🛠 Кастомное правило (TCP/UDP)","callback_data":"a_c"}], + [{"text":"📋 Активные правила","callback_data":"lr"}], + [{"text":"🏓 Ping серверов","callback_data":"pm"}], [{"text":"📊 Мониторинг","callback_data":"mm"}], + [{"text":"💻 Состояние системы","callback_data":"sys"}], [{"text":"❌ Удалить правило","callback_data":"dr"}], - [{"text":"🗑 Сбросить всё","callback_data":"fa"}] + [{"text":"🗑 Сбросить все правила","callback_data":"fa"}], + [{"text":"🏢 Хостинг, который работает","callback_data":"promo"}], + [{"text":"⚙️ Компактное меню","callback_data":"sw_compact"}] ] JSON + else + cat <<'JSON' +[ + [{"text":"🔀 AWG","callback_data":"a_u"},{"text":"🔀 VLESS","callback_data":"a_t"},{"text":"🔀 MTProto","callback_data":"a_mt"}], + [{"text":"🛠 Custom","callback_data":"a_c"},{"text":"📋 Правила","callback_data":"lr"}], + [{"text":"🏓 Ping","callback_data":"pm"},{"text":"📊 Монитор","callback_data":"mm"}], + [{"text":"💻 Система","callback_data":"sys"}], + [{"text":"❌ Удалить","callback_data":"dr"},{"text":"🗑 Сброс","callback_data":"fa"}], + [{"text":"🏢 Хостинг","callback_data":"promo"}], + [{"text":"⚙️ Большое меню","callback_data":"sw_large"}] +] +JSON + fi } kbd_back() { @@ -828,14 +984,27 @@ kbd_intervals() { ]' } +kbd_cooldowns() { + local ip="$1" interval="$2" threshold="$3" + jq -n --arg ip "$ip" --arg int "$interval" --arg thr "$threshold" '[ + [{"text":"10 сек","callback_data":("mc:" + $ip + ":" + $int + ":" + $thr + ":10")}], + [{"text":"60 сек","callback_data":("mc:" + $ip + ":" + $int + ":" + $thr + ":60")}], + [{"text":"5 мин","callback_data":("mc:" + $ip + ":" + $int + ":" + $thr + ":300")}], + [{"text":"15 мин","callback_data":("mc:" + $ip + ":" + $int + ":" + $thr + ":900")}], + [{"text":"⬅️ Меню","callback_data":"m"}] + ]' +} + build_ip_kbd() { local prefix="$1"; shift local ips=("$@") - local rows="" - local first=1 + local rows="" first=1 for ip in "${ips[@]}"; do + local label + label=$(get_alias "$ip") + [ -z "$label" ] && label="$ip" || label="$label ($ip)" [ "$first" -eq 0 ] && rows+="," - rows+="[{\"text\":\"$ip\",\"callback_data\":\"${prefix}:${ip}\"}]" + rows+="[{\"text\":\"$label\",\"callback_data\":\"${prefix}:${ip}\"}]" first=0 done echo "[${rows},[{\"text\":\"⬅️ Меню\",\"callback_data\":\"m\"}]]" @@ -847,8 +1016,13 @@ build_delete_kbd() { local rows="" i=1 first=1 while IFS='|' read -r proto port dest; do [ -z "$port" ] && continue + local dest_ip="${dest%:*}" + local label + label=$(get_alias "$dest_ip") + local btn_text="❌ ${MY_IP:-*}:$port ($proto) → $dest" + [ -n "$label" ] && btn_text="❌ :$port → $label" [ "$first" -eq 0 ] && rows+="," - rows+="[{\"text\":\"❌ $port ($proto) → $dest\",\"callback_data\":\"dr_${i}\"}]" + rows+="[{\"text\":\"$btn_text\",\"callback_data\":\"dr_${i}\"}]" first=0 ((i++)) done <<< "$rules" @@ -864,7 +1038,7 @@ build_delete_kbd() { bot_main_menu() { local chat_id="$1" msg_id="${2:-}" bot_clear_state "$chat_id" - local text="Kaskad v${KASKAD_VERSION}\nВыберите действие:" + local text="Kaskad PRO v${KASKAD_VERSION}\nIP: ${MY_IP:-N/A}\nВыберите действие:" local kbd kbd=$(kbd_main) if [ -n "$msg_id" ]; then @@ -884,6 +1058,15 @@ bot_handle_callback() { bot_main_menu "$chat_id" "$msg_id" ;; + sw_compact) + save_config_val "MENU_STYLE" "compact" + bot_main_menu "$chat_id" "$msg_id" + ;; + sw_large) + save_config_val "MENU_STYLE" "large" + bot_main_menu "$chat_id" "$msg_id" + ;; + # ── Add rules ── a_u) bot_set_state "$chat_id" "STATE=awaiting_ip" "PROTO=udp" "NAME=AmneziaWG" "CUSTOM=0" @@ -913,9 +1096,16 @@ bot_handle_callback() { if [ -z "$rules" ]; then text="📋 Нет активных правил." else - text="📋 Активные правила:\n" + text="📋 Активные правила:\nСервер: ${MY_IP:-N/A}\n\n" while IFS='|' read -r proto port dest; do - [ -n "$port" ] && text+="$proto :$port → $dest\n" + if [ -n "$port" ]; then + local dest_ip="${dest%:*}" + local label + label=$(get_alias "$dest_ip") + local line="${MY_IP:-*}:$port ($proto) → $dest" + [ -n "$label" ] && line+=" [$label]" + text+="$line\n" + fi done <<< "$rules" fi tg_edit "$chat_id" "$msg_id" "$text" "$(kbd_back)" @@ -931,8 +1121,6 @@ bot_handle_callback() { line=$(get_rules_list | sed -n "${idx}p") if [ -n "$line" ]; then IFS='|' read -r d_proto d_port d_dest <<< "$line" - local target_ip="${d_dest%:*}" - local target_port="${d_dest#*:}" iptables -t nat -D PREROUTING -p "$d_proto" --dport "$d_port" -j DNAT --to-destination "$d_dest" 2>/dev/null iptables -S INPUT 2>/dev/null | grep "kaskad:${d_port}:${d_proto}" | while read -r rule; do eval "iptables -D ${rule#-A }" 2>/dev/null @@ -971,6 +1159,29 @@ bot_handle_callback() { tg_edit "$chat_id" "$msg_id" "✅ Все правила Kaskad удалены." "$(kbd_back)" ;; + # ── System stats ── + sys) + local stats + stats=$(get_system_stats) + tg_edit "$chat_id" "$msg_id" "$stats" "$(kbd_back)" + ;; + + # ── Promo ── + promo) + local promo_text="" + promo_text+="🏢 Хостинг, который работает\n\n" + promo_text+="🌍 Локации: РФ и Европа\n" + promo_text+="👉 https://vk.cc/ct29NQ\n\n" + promo_text+="OFF60 — 60% скидка на 1-й месяц\n" + promo_text+="antenka20 — +20% к балансу (3 мес)\n" + promo_text+="antenka6 — +15% к балансу (6 мес)\n" + promo_text+="antenka12 — +5% к балансу (12 мес)\n\n" + promo_text+="🇧🇾 Локация: Беларусь\n" + promo_text+="👉 https://vk.cc/cUxAhj\n\n" + promo_text+="OFF60 — 60% скидка на 1-й месяц" + tg_edit "$chat_id" "$msg_id" "$promo_text" "$(kbd_back)" + ;; + # ── Ping ── pm) local -a ips=() @@ -983,25 +1194,31 @@ bot_handle_callback() { ;; ps:*) local ip="${data#ps:}" - tg_edit "$chat_id" "$msg_id" "🏓 Ping $ip\nВыберите режим:" "$(kbd_ping_opts "$ip")" + local label + label=$(fmt_ip "$ip") + tg_edit "$chat_id" "$msg_id" "🏓 Ping $label\nВыберите режим:" "$(kbd_ping_opts "$ip")" ;; po:*) local ip="${data#po:}" + local label + label=$(fmt_ip "$ip") ( local ms ms=$(ping -c 1 -W 3 "$ip" 2>/dev/null | grep -oP 'time=\K[\d.]+') if [ -n "$ms" ]; then - tg_send "$chat_id" "🏓 Ping $ip\nОтвет: ${ms} ms" "$(kbd_back)" > /dev/null + tg_send "$chat_id" "🏓 $label\nОтвет: ${ms} ms" "$(kbd_back)" > /dev/null else - tg_send "$chat_id" "🏓 Ping $ip\nОтвет: timeout" "$(kbd_back)" > /dev/null + tg_send "$chat_id" "🏓 $label\nОтвет: timeout" "$(kbd_back)" > /dev/null fi ) & ;; p10:*) local ip="${data#p10:}" + local label + label=$(fmt_ip "$ip") ( local resp - resp=$(tg_send "$chat_id" "🏓 Ping $ip (10 раз)...\nОжидайте." "") + resp=$(tg_send "$chat_id" "🏓 Ping $label (10 раз)...\nОжидайте." "") local mid mid=$(echo "$resp" | jq -r '.result.message_id // empty') @@ -1020,7 +1237,7 @@ bot_handle_callback() { sleep 1 done - local summary="🏓 Ping $ip (10 раз)\n${text}" + local summary="🏓 Ping $label (10 раз)\n${text}" if [ ${#results[@]} -gt 0 ]; then local avg avg=$(printf '%s\n' "${results[@]}" | awk '{s+=$1} END {printf "%.2f", s/NR}') @@ -1037,9 +1254,11 @@ bot_handle_callback() { ;; p60:*) local ip="${data#p60:}" + local label + label=$(fmt_ip "$ip") ( local resp - resp=$(tg_send "$chat_id" "🏓 Ping $ip (60 сек)...\nОбновление каждые 10 сек." "") + resp=$(tg_send "$chat_id" "🏓 Ping $label (60 сек)...\nОбновление каждые 10 сек." "") local mid mid=$(echo "$resp" | jq -r '.result.message_id // empty') @@ -1048,14 +1267,10 @@ bot_handle_callback() { for i in $(seq 1 60); do local ms ms=$(ping -c 1 -W 2 "$ip" 2>/dev/null | grep -oP 'time=\K[\d.]+') - if [ -n "$ms" ]; then - results+=("$ms") - else - ((lost++)) - fi + if [ -n "$ms" ]; then results+=("$ms"); else ((lost++)); fi if (( i % 10 == 0 )) && [ -n "$mid" ]; then - local partial="🏓 Ping $ip: ${i}/60 сек\nУспешно: ${#results[@]} | Потеряно: $lost" + local partial="🏓 $label: ${i}/60 сек\nУспешно: ${#results[@]} | Потеряно: $lost" if [ ${#results[@]} -gt 0 ]; then local pavg pavg=$(printf '%s\n' "${results[@]}" | awk '{s+=$1} END {printf "%.2f", s/NR}') @@ -1066,7 +1281,7 @@ bot_handle_callback() { sleep 1 done - local summary="🏓 Ping $ip (60 сек) — завершён\n" + local summary="🏓 $label (60 сек) — завершён\n" if [ ${#results[@]} -gt 0 ]; then local stats stats=$(printf '%s\n' "${results[@]}" | awk ' @@ -1101,14 +1316,26 @@ bot_handle_callback() { ;; ma:*) local ip="${data#ma:}" - tg_edit "$chat_id" "$msg_id" "📊 Мониторинг $ip\nВыберите интервал:" "$(kbd_intervals "$ip")" + local label + label=$(fmt_ip "$ip") + tg_edit "$chat_id" "$msg_id" "📊 Мониторинг $label\nВыберите интервал проверки:" "$(kbd_intervals "$ip")" ;; mi:*) local rest="${data#mi:}" local ip="${rest%:*}" local interval="${rest##*:}" bot_set_state "$chat_id" "STATE=awaiting_threshold" "MON_IP=$ip" "MON_INTERVAL=$interval" - tg_edit "$chat_id" "$msg_id" "📊 Мониторинг $ip (каждые ${interval}с)\n\nВведите порог уведомления (мс):" "$(kbd_back)" + local label + label=$(fmt_ip "$ip") + tg_edit "$chat_id" "$msg_id" "📊 $label (каждые ${interval}с)\n\nВведите порог уведомления (мс):" "$(kbd_back)" + ;; + mc:*) + local rest="${data#mc:}" + IFS=':' read -r ip interval threshold cooldown <<< "$rest" + add_monitor "$ip" "$interval" "$threshold" "$cooldown" + local label + label=$(fmt_ip "$ip") + tg_edit "$chat_id" "$msg_id" "✅ Мониторинг для $label добавлен.\nИнтервал: ${interval}с | Порог: ${threshold}мс | Уведомл: ${cooldown}с" "$(kbd_back)" ;; ml) local text="📊 Активные мониторы:\n" @@ -1116,9 +1343,11 @@ bot_handle_callback() { for conf in "$MONITOR_DIR"/*.conf; do [ -f "$conf" ] || continue found=1 - local MON_IP="" MON_INTERVAL="" MON_THRESHOLD="" + local MON_IP="" MON_INTERVAL="" MON_THRESHOLD="" MON_COOLDOWN=300 source "$conf" - text+="$MON_IP — каждые ${MON_INTERVAL}с, порог ${MON_THRESHOLD}мс\n" + local label + label=$(fmt_ip "$MON_IP") + text+="$label\n инт: ${MON_INTERVAL}с | порог: ${MON_THRESHOLD}мс | уведомл: ${MON_COOLDOWN}с\n" done [ "$found" -eq 0 ] && text+="Нет настроенных мониторов." tg_edit "$chat_id" "$msg_id" "$text" "$(kbd_monitor)" @@ -1140,7 +1369,9 @@ bot_handle_callback() { md:*) local ip="${data#md:}" remove_monitor "$ip" - tg_edit "$chat_id" "$msg_id" "✅ Мониторинг для $ip удалён." "$(kbd_monitor)" + local label + label=$(fmt_ip "$ip") + tg_edit "$chat_id" "$msg_id" "✅ Мониторинг для $label удалён." "$(kbd_monitor)" ;; esac } @@ -1166,17 +1397,29 @@ bot_handle_message() { proto=$(bot_get_state "$chat_id" "PROTO") name=$(bot_get_state "$chat_id" "NAME") custom=$(bot_get_state "$chat_id" "CUSTOM") + bot_set_state "$chat_id" "STATE=awaiting_name" "PROTO=$proto" "NAME=$name" "CUSTOM=$custom" "TARGET_IP=$text" + tg_send "$chat_id" "IP: $text ✅\n\nВведите имя сервера (или - чтобы пропустить):" "$(kbd_back)" > /dev/null + ;; + awaiting_name) + local proto name custom target_ip + proto=$(bot_get_state "$chat_id" "PROTO") + name=$(bot_get_state "$chat_id" "NAME") + custom=$(bot_get_state "$chat_id" "CUSTOM") + target_ip=$(bot_get_state "$chat_id" "TARGET_IP") + if [ "$text" != "-" ] && [ -n "$text" ]; then + set_alias "$target_ip" "$text" + fi if [ "$custom" = "1" ]; then - bot_set_state "$chat_id" "STATE=awaiting_in_port" "PROTO=$proto" "NAME=$name" "CUSTOM=1" "TARGET_IP=$text" - tg_send "$chat_id" "IP: $text ✅\n\nВведите ВХОДЯЩИЙ порт (на этом сервере):" "$(kbd_back)" > /dev/null + bot_set_state "$chat_id" "STATE=awaiting_in_port" "PROTO=$proto" "NAME=$name" "CUSTOM=1" "TARGET_IP=$target_ip" + tg_send "$chat_id" "Сервер: $(fmt_ip "$target_ip")\n\nВведите ВХОДЯЩИЙ порт:" "$(kbd_back)" > /dev/null else - bot_set_state "$chat_id" "STATE=awaiting_port" "PROTO=$proto" "NAME=$name" "CUSTOM=0" "TARGET_IP=$text" - tg_send "$chat_id" "IP: $text ✅\n\nВведите порт:" "$(kbd_back)" > /dev/null + bot_set_state "$chat_id" "STATE=awaiting_port" "PROTO=$proto" "NAME=$name" "CUSTOM=0" "TARGET_IP=$target_ip" + tg_send "$chat_id" "Сервер: $(fmt_ip "$target_ip")\n\nВведите порт:" "$(kbd_back)" > /dev/null fi ;; awaiting_port) if ! validate_port "$text"; then - tg_send "$chat_id" "❌ Некорректный порт (1-65535). Попробуйте ещё раз:" "" > /dev/null + tg_send "$chat_id" "❌ Некорректный порт (1-65535)." "" > /dev/null return fi local proto name target_ip @@ -1185,7 +1428,9 @@ bot_handle_message() { target_ip=$(bot_get_state "$chat_id" "TARGET_IP") bot_clear_state "$chat_id" apply_iptables_rules "$proto" "$text" "$text" "$target_ip" "$name" - tg_send "$chat_id" "✅ $name настроен!\n$proto :$text → $target_ip:$text" "$(kbd_back)" > /dev/null + local label + label=$(fmt_ip "$target_ip") + tg_send "$chat_id" "✅ $name настроен!\n$proto ${MY_IP:-*}:$text → $target_ip:$text\nСервер: $label" "$(kbd_back)" > /dev/null ;; awaiting_in_port) if ! validate_port "$text"; then @@ -1197,7 +1442,7 @@ bot_handle_message() { name=$(bot_get_state "$chat_id" "NAME") target_ip=$(bot_get_state "$chat_id" "TARGET_IP") bot_set_state "$chat_id" "STATE=awaiting_out_port" "PROTO=$proto" "NAME=$name" "CUSTOM=1" "TARGET_IP=$target_ip" "IN_PORT=$text" - tg_send "$chat_id" "Входящий порт: $text ✅\n\nВведите ИСХОДЯЩИЙ порт (на целевом сервере):" "$(kbd_back)" > /dev/null + tg_send "$chat_id" "Входящий порт: $text ✅\n\nВведите ИСХОДЯЩИЙ порт:" "$(kbd_back)" > /dev/null ;; awaiting_out_port) if ! validate_port "$text"; then @@ -1211,7 +1456,9 @@ bot_handle_message() { in_port=$(bot_get_state "$chat_id" "IN_PORT") bot_clear_state "$chat_id" apply_iptables_rules "$proto" "$in_port" "$text" "$target_ip" "$name" - tg_send "$chat_id" "✅ Custom Rule настроен!\n$proto :$in_port → $target_ip:$text" "$(kbd_back)" > /dev/null + local label + label=$(fmt_ip "$target_ip") + tg_send "$chat_id" "✅ Custom Rule настроен!\n$proto ${MY_IP:-*}:$in_port → $target_ip:$text\nСервер: $label" "$(kbd_back)" > /dev/null ;; awaiting_threshold) if ! validate_port "$text"; then @@ -1222,8 +1469,9 @@ bot_handle_message() { mon_ip=$(bot_get_state "$chat_id" "MON_IP") mon_interval=$(bot_get_state "$chat_id" "MON_INTERVAL") bot_clear_state "$chat_id" - add_monitor "$mon_ip" "$mon_interval" "$text" - tg_send "$chat_id" "✅ Мониторинг для $mon_ip добавлен.\nИнтервал: ${mon_interval}с | Порог: ${text}мс\n\n⚠️ Убедитесь, что служба мониторинга запущена (gokaskad → Мониторинг → Запустить)." "$(kbd_back)" > /dev/null + local label + label=$(fmt_ip "$mon_ip") + tg_send "$chat_id" "📊 $label\nИнтервал: ${mon_interval}с | Порог: ${text}мс\n\nВыберите частоту уведомлений:" "$(kbd_cooldowns "$mon_ip" "$mon_interval" "$text")" > /dev/null ;; *) tg_send "$chat_id" "Используйте /start или /menu для вызова меню." "" > /dev/null @@ -1240,11 +1488,11 @@ bot_daemon() { source "$KASKAD_CONF" if [ -z "$BOT_TOKEN" ]; then log_action "BOT ERROR: BOT_TOKEN is empty" - echo "ERROR: BOT_TOKEN не задан в $KASKAD_CONF" exit 1 fi detect_interface + get_my_ip local offset=0 while true; do @@ -1252,17 +1500,11 @@ bot_daemon() { response=$(curl -s --max-time 35 \ "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?offset=${offset}&timeout=30" 2>/dev/null) - if [ -z "$response" ]; then - sleep 2 - continue - fi + if [ -z "$response" ]; then sleep 2; continue; fi local ok ok=$(echo "$response" | jq -r '.ok // "false"') - if [ "$ok" != "true" ]; then - sleep 5 - continue - fi + if [ "$ok" != "true" ]; then sleep 5; continue; fi local count count=$(echo "$response" | jq '.result | length') @@ -1307,27 +1549,19 @@ bot_daemon() { start_bot() { source "$KASKAD_CONF" - if [ -z "$BOT_TOKEN" ]; then - echo -e "${RED}Сначала задайте BOT_TOKEN!${NC}" - return - fi - + if [ -z "$BOT_TOKEN" ]; then echo -e "${RED}Сначала задайте BOT_TOKEN!${NC}"; return; fi if [ -f "$BOT_PID_FILE" ] && kill -0 "$(cat "$BOT_PID_FILE")" 2>/dev/null; then - echo -e "${YELLOW}Бот уже запущен (PID $(cat "$BOT_PID_FILE")).${NC}" - return + echo -e "${YELLOW}Бот уже запущен.${NC}"; return fi - cat > /etc/systemd/system/kaskad-bot.service </dev/null - local bot_status="${RED}Остановлен${NC}" if [ -f "$BOT_PID_FILE" ] && kill -0 "$(cat "$BOT_PID_FILE" 2>/dev/null)" 2>/dev/null; then bot_status="${GREEN}Работает (PID $(cat "$BOT_PID_FILE"))${NC}" fi - local token_display="не задан" - if [ -n "${BOT_TOKEN:-}" ]; then - token_display="***${BOT_TOKEN: -6}" - fi + [ -n "${BOT_TOKEN:-}" ] && token_display="***${BOT_TOKEN: -6}" + local ut_val + ut_val=$(bot_get_state "system" "UPDATE_TOKEN" 2>/dev/null) + local ut_display="не задан" + [ -n "$ut_val" ] && ut_display="***${ut_val: -6}" echo -e "${CYAN}━━━ Telegram Bot ━━━${NC}" echo -e "Статус: $bot_status" echo -e "Токен: ${YELLOW}$token_display${NC}" echo -e "Chat ID: ${YELLOW}${BOT_CHAT_ID:-не задан}${NC}" - echo "" - local ut_display="не задан" - local ut_val - ut_val=$(bot_get_state "system" "UPDATE_TOKEN" 2>/dev/null) - [ -n "$ut_val" ] && ut_display="***${ut_val: -6}" - echo -e "Update Token: ${YELLOW}$ut_display${NC}" + echo -e "Меню: ${YELLOW}${MENU_STYLE:-compact}${NC}" echo "" echo -e "1) Установить токен бота" echo -e "2) Получить Chat ID (авто)" echo -e "3) Установить Chat ID вручную" echo -e "4) ${GREEN}Запустить бота${NC}" echo -e "5) ${RED}Остановить бота${NC}" - echo -e "6) Установить токен обновления (GitHub PAT)" + echo -e "6) Токен обновления (GitHub PAT)" echo -e "0) Назад" read -p "Выбор: " choice case $choice in - 1) - echo -e "Введите токен бота (от @BotFather):" - read -p "> " new_token - if [ -n "$new_token" ]; then - save_config_val "BOT_TOKEN" "$new_token" - echo -e "${GREEN}[OK] Токен сохранён.${NC}" - fi - read -p "Enter..." - ;; + 1) echo "Введите токен (от @BotFather):"; read -p "> " t; [ -n "$t" ] && save_config_val "BOT_TOKEN" "$t" && echo -e "${GREEN}OK${NC}"; read -p "Enter..." ;; 2) - if [ -z "${BOT_TOKEN:-}" ]; then - echo -e "${RED}Сначала задайте токен!${NC}" - read -p "Enter..." - continue - fi - echo -e "${YELLOW}Отправьте любое сообщение боту в Telegram, затем нажмите Enter.${NC}" - read -p "" - local resp - resp=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?limit=1&offset=-1" 2>/dev/null) - local cid - cid=$(echo "$resp" | jq -r '.result[0].message.chat.id // empty') - if [ -n "$cid" ]; then - save_config_val "BOT_CHAT_ID" "$cid" - echo -e "${GREEN}Chat ID: $cid — сохранён!${NC}" - else - echo -e "${RED}Не удалось определить Chat ID. Повторите.${NC}" - fi - read -p "Enter..." - ;; - 3) - echo -e "Введите Chat ID:" - read -p "> " new_cid - if [ -n "$new_cid" ]; then - save_config_val "BOT_CHAT_ID" "$new_cid" - echo -e "${GREEN}[OK] Chat ID сохранён.${NC}" - fi - read -p "Enter..." - ;; + [ -z "${BOT_TOKEN:-}" ] && echo -e "${RED}Сначала токен!${NC}" && read -p "Enter..." && continue + echo -e "${YELLOW}Отправьте боту сообщение в Telegram, затем Enter.${NC}"; read -p "" + local cid; cid=$(curl -s "https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?limit=1&offset=-1" | jq -r '.result[0].message.chat.id // empty') + [ -n "$cid" ] && save_config_val "BOT_CHAT_ID" "$cid" && echo -e "${GREEN}Chat ID: $cid${NC}" || echo -e "${RED}Не удалось.${NC}" + read -p "Enter..." ;; + 3) echo "Chat ID:"; read -p "> " c; [ -n "$c" ] && save_config_val "BOT_CHAT_ID" "$c" && echo -e "${GREEN}OK${NC}"; read -p "Enter..." ;; 4) start_bot; read -p "Enter..." ;; 5) stop_bot; read -p "Enter..." ;; - 6) - echo -e "Введите GitHub PAT (для скачивания обновлений из приватного репо):" - read -p "> " new_ut - if [ -n "$new_ut" ]; then - bot_set_state "system" "UPDATE_TOKEN=$new_ut" - echo -e "${GREEN}[OK] Токен обновления сохранён.${NC}" - fi - read -p "Enter..." - ;; + 6) echo "GitHub PAT:"; read -p "> " u; [ -n "$u" ] && bot_set_state "system" "UPDATE_TOKEN=$u" && echo -e "${GREEN}OK${NC}"; read -p "Enter..." ;; 0) return ;; esac done } # ═══════════════════════════════════════════════════════════════ -# PROMO & INSTRUCTIONS +# PROMO & INSTRUCTIONS (kept from previous version) # ═══════════════════════════════════════════════════════════════ show_promo() { @@ -1453,302 +1644,50 @@ show_promo() { echo -e "${MAGENTA}║ ХОСТИНГ, КОТОРЫЙ РАБОТАЕТ СО СКИДКОЙ ДО -60% ║${NC}" echo -e "${MAGENTA}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" - echo -e "${CYAN}🌍 ЛОКАЦИИ: РФ И ЕВРОПА${NC}" echo -e "${WHITE} >>> https://vk.cc/ct29NQ${NC}" printf " ${YELLOW}%-12s${NC} : ${WHITE}%s${NC}\n" "OFF60" "60% скидка на первый месяц" printf " ${YELLOW}%-12s${NC} : ${WHITE}%s${NC}\n" "antenka20" "Буст 20% + 3% (при оплате за 3 мес)" printf " ${YELLOW}%-12s${NC} : ${WHITE}%s${NC}\n" "antenka6" "Буст 15% + 5% (при оплате за 6 мес)" printf " ${YELLOW}%-12s${NC} : ${WHITE}%s${NC}\n" "antenka12" "Буст 5% + 5% (при оплате за 12 мес)" - echo -e "\n${CYAN}🇧🇾 ЛОКАЦИЯ: БЕЛАРУСЬ${NC}" echo -e "${WHITE} >>> https://vk.cc/cUxAhj${NC}" printf " ${YELLOW}%-12s${NC} : ${WHITE}%s${NC}\n" "OFF60" "60% скидка на первый месяц" - echo "" - echo -e "\n${YELLOW}Генерация QR-кода основного партнера... (3 сек)${NC}" - for i in {3..1}; do - echo -ne "$i..." - sleep 1 - done - echo "" - + echo -e "\n${YELLOW}Генерация QR-кода... (3 сек)${NC}" + for i in {3..1}; do echo -ne "$i..."; sleep 1; done; echo "" echo -e "\n${WHITE}" - if command -v qrencode &>/dev/null; then - qrencode -t ANSIUTF8 "https://vk.cc/ct29NQ" - else - echo "QR-код не загрузился, используйте ссылки выше." - fi + command -v qrencode &>/dev/null && qrencode -t ANSIUTF8 "https://vk.cc/ct29NQ" || echo "Используйте ссылки выше." echo -e "${NC}" - echo -e "${GREEN}Сканируйте камерой телефона!${NC}" echo "" - read -p "Нажмите Enter для настройки каскадного скрипта..." + read -p "Нажмите Enter..." } show_instructions() { - local page=1 - local total_pages=7 + local page=1 total_pages=7 while true; do clear echo -e "${MAGENTA}╔══════════════════════════════════════════════════════════════╗${NC}" echo -e "${MAGENTA}║ 📚 ИНСТРУКЦИЯ KASKAD PRO v${KASKAD_VERSION} (стр. ${page}/${total_pages}) ║${NC}" echo -e "${MAGENTA}╚══════════════════════════════════════════════════════════════╝${NC}" echo "" - case $page in - 1) - echo -e "${CYAN}═══ ЧТО ТАКОЕ КАСКАД? ═══${NC}" - echo "" - echo -e "Каскад — это 'мост' между вашим устройством и зарубежным" - echo -e "VPN/Proxy сервером. Трафик идёт по цепочке:" - echo "" - echo -e " ${WHITE}Телефон/ПК${NC} → ${GREEN}Этот сервер (РФ)${NC} → ${CYAN}Зарубежный VPN${NC} → Интернет" - echo "" - echo -e "Зачем это нужно:" - echo -e " • Провайдер видит только подключение к серверу в РФ" - echo -e " • Зарубежный сервер не блокируется, т.к. подключение идёт" - echo -e " с российского IP" - echo -e " • Скорость выше за счет BBR и близкого первого хопа" - echo "" - echo -e "${CYAN}═══ ЧТО НУЖНО ДЛЯ НАЧАЛА ═══${NC}" - echo "" - echo -e " ${YELLOW}1.${NC} VPS в РФ (на нём запущен этот скрипт)" - echo -e " ${YELLOW}2.${NC} Зарубежный VPN/Proxy сервер (AWG, VLESS и т.д.)" - echo -e " ${YELLOW}3.${NC} IP-адрес и порт зарубежного сервера" - echo "" - echo -e "${WHITE}Пример:${NC}" - echo -e " Зарубежный сервер: ${GREEN}45.10.20.30${NC}, порт ${GREEN}51820${NC} (WireGuard)" - echo -e " Этот сервер (РФ): ${GREEN}${MY_IP:-185.1.2.3}${NC}" - ;; - 2) - echo -e "${CYAN}═══ ПУНКТ 1: AmneziaWG / WireGuard (UDP) ═══${NC}" - echo "" - echo -e "Создаёт каскад для протоколов на базе UDP:" - echo -e "AmneziaWG, WireGuard, OpenVPN (UDP) и т.п." - echo "" - echo -e "${WHITE}Как использовать:${NC}" - echo -e " 1. В меню выберите ${YELLOW}1${NC}" - echo -e " 2. Введите IP зарубежного сервера: ${GREEN}45.10.20.30${NC}" - echo -e " 3. Введите порт: ${GREEN}51820${NC}" - echo -e " 4. Подтвердите: ${GREEN}y${NC}" - echo "" - echo -e "${WHITE}Что произойдёт:${NC}" - echo -e " Создастся правило: UDP :51820 → 45.10.20.30:51820" - echo -e " Весь UDP-трафик на порт 51820 этого сервера будет" - echo -e " перенаправляться на зарубежный сервер." - echo "" - echo -e "${WHITE}Настройка клиента:${NC}" - echo -e " В приложении AmneziaWG/WireGuard замените Endpoint:" - echo -e " ${RED}Было:${NC} 45.10.20.30:51820" - echo -e " ${GREEN}Стало:${NC} ${MY_IP:-185.1.2.3}:51820" - echo "" - echo -e "${CYAN}═══ ПУНКТ 2: VLESS / XRay (TCP) ═══${NC}" - echo "" - echo -e "Создаёт каскад для TCP-протоколов:" - echo -e "VLESS, VMess, Reality, Trojan и т.п." - echo "" - echo -e "${WHITE}Пример:${NC} Зарубежный VLESS на порту 443" - echo -e " 1. В меню: ${YELLOW}2${NC}" - echo -e " 2. IP: ${GREEN}67.89.100.200${NC}" - echo -e " 3. Порт: ${GREEN}443${NC}" - echo -e " 4. В клиенте (v2rayNG, NekoBox) замените адрес" - echo -e " сервера на IP этого VPS" - ;; - 3) - echo -e "${CYAN}═══ ПУНКТ 3: TProxy / MTProto (TCP) ═══${NC}" - echo "" - echo -e "Каскад для Telegram-прокси (MTProto, TProxy)." - echo "" - echo -e "${WHITE}Пример:${NC} MTProto прокси на порту 8443" - echo -e " 1. В меню: ${YELLOW}3${NC}" - echo -e " 2. IP: ${GREEN}45.10.20.30${NC}" - echo -e " 3. Порт: ${GREEN}8443${NC}" - echo -e " 4. В Telegram: Настройки → Прокси → адрес:" - echo -e " ${GREEN}${MY_IP:-185.1.2.3}${NC}, порт: ${GREEN}8443${NC}" - echo "" - echo -e "${CYAN}═══ ПУНКТ 4: Кастомное правило ═══${NC}" - echo "" - echo -e "Полная гибкость: разные порты входа и выхода," - echo -e "выбор TCP/UDP, проброс SSH, RDP и др." - echo "" - echo -e "${WHITE}Пример 1:${NC} Проброс SSH (разные порты)" - echo -e " Протокол: ${GREEN}tcp${NC}" - echo -e " IP: ${GREEN}45.10.20.30${NC}" - echo -e " Входящий порт: ${GREEN}2222${NC} (на этом сервере)" - echo -e " Исходящий порт: ${GREEN}22${NC} (на целевом сервере)" - echo -e " Подключение: ssh user@${MY_IP:-185.1.2.3} -p 2222" - echo "" - echo -e "${WHITE}Пример 2:${NC} Проброс RDP" - echo -e " Протокол: ${GREEN}tcp${NC}" - echo -e " IP: ${GREEN}10.0.0.5${NC}" - echo -e " Входящий порт: ${GREEN}13389${NC}" - echo -e " Исходящий порт: ${GREEN}3389${NC}" - echo "" - echo -e "${WHITE}Пример 3:${NC} WireGuard на нестандартном порту" - echo -e " Протокол: ${GREEN}udp${NC}" - echo -e " IP: ${GREEN}45.10.20.30${NC}" - echo -e " Входящий порт: ${GREEN}9999${NC}" - echo -e " Исходящий порт: ${GREEN}51820${NC}" - echo -e " Клиент подключается к ${MY_IP:-185.1.2.3}:9999" - ;; - 4) - echo -e "${CYAN}═══ ПУНКТЫ 5-6: ПРАВИЛА И PING ═══${NC}" - echo "" - echo -e "${WHITE}Пункт 5 — Активные правила${NC}" - echo -e " Показывает таблицу всех созданных каскадов:" - echo -e " ┌────────────┬──────────┬───────────────────┐" - echo -e " │ Порт (вход)│ Протокол │ Цель (IP:выход) │" - echo -e " ├────────────┼──────────┼───────────────────┤" - echo -e " │ 51820 │ udp │ 45.10.20.30:51820 │" - echo -e " │ 443 │ tcp │ 67.89.100.200:443 │" - echo -e " └────────────┴──────────┴───────────────────┘" - echo "" - echo -e "${WHITE}Пункт 6 — Ping сервера (live)${NC}" - echo -e " Проверяет связь с целевым сервером в реальном времени." - echo -e " Экран обновляется каждую секунду." - echo "" - echo -e " ${WHITE}Как использовать:${NC}" - echo -e " 1. В меню: ${YELLOW}6${NC}" - echo -e " 2. Выберите сервер из списка (например: ${GREEN}1${NC})" - echo -e " 3. Наблюдайте результаты в реальном времени:" - echo "" - echo -e " ${CYAN}━━━ Live Ping: 45.10.20.30 ━━━${NC}" - echo -e " ${GREEN}#1: 15.3 ms${NC}" - echo -e " ${GREEN}#2: 14.8 ms${NC}" - echo -e " ${GREEN}#3: 16.1 ms${NC}" - echo -e " ${RED}#4: timeout${NC}" - echo -e " ${GREEN}#5: 15.0 ms${NC}" - echo "" - echo -e " ${WHITE}Мин:${NC} 14.8ms ${WHITE}Макс:${NC} 16.1ms ${WHITE}Сред:${NC} 15.3ms" - echo -e " ${WHITE}Потеряно:${NC} 1 / 5" - echo "" - echo -e " ${YELLOW}Ctrl+C${NC} для остановки." - ;; - 5) - echo -e "${CYAN}═══ ПУНКТ 7: МОНИТОРИНГ ═══${NC}" - echo "" - echo -e "Автоматическая проверка доступности целевых серверов" - echo -e "с отправкой уведомлений в Telegram при проблемах." - echo "" - echo -e "${WHITE}Как настроить:${NC}" - echo -e " 1. В меню: ${YELLOW}7${NC} → ${YELLOW}1) Добавить мониторинг${NC}" - echo -e " 2. Выберите сервер из списка" - echo -e " 3. Выберите интервал проверки:" - echo -e " ${YELLOW}1${NC} — каждые 10 секунд (для критичных сервисов)" - echo -e " ${YELLOW}2${NC} — каждую минуту (рекомендуется)" - echo -e " ${YELLOW}3${NC} — каждые 5 минут (для экономии ресурсов)" - echo -e " 4. Укажите порог в мс (например: ${GREEN}100${NC})" - echo "" - echo -e "${WHITE}Что происходит:${NC}" - echo -e " • Каждые N секунд скрипт пингует целевой сервер" - echo -e " • Если пинг > порога или timeout — отправляет алерт" - echo -e " в Telegram (не чаще 1 раза в 5 минут на IP)" - echo -e " • Все события записываются в ${YELLOW}/var/log/kaskad.log${NC}" - echo "" - echo -e "${WHITE}Пример алерта в Telegram:${NC}" - echo -e " ⚠️ ${RED}ALERT${NC}: 45.10.20.30" - echo -e " Ping: 250ms (порог: 100ms)" - echo "" - echo -e "${WHITE}Управление службой:${NC}" - echo -e " ${YELLOW}3${NC} — Запустить (создаёт systemd-сервис, переживёт ребут)" - echo -e " ${YELLOW}4${NC} — Остановить" - echo "" - echo -e "${RED}Важно:${NC} Для Telegram-алертов нужно настроить бота (пункт 8)." - ;; - 6) - echo -e "${CYAN}═══ ПУНКТ 8: TELEGRAM BOT ═══${NC}" - echo "" - echo -e "Полное управление сервером из Telegram через кнопки." - echo "" - echo -e "${WHITE}НАСТРОЙКА (один раз):${NC}" - echo "" - echo -e " ${YELLOW}Шаг A.${NC} Создайте бота:" - echo -e " 1. Откройте Telegram, найдите ${GREEN}@BotFather${NC}" - echo -e " 2. Отправьте ${GREEN}/newbot${NC}" - echo -e " 3. Придумайте имя (например: ${GREEN}Мой Kaskad${NC})" - echo -e " 4. Придумайте username (например: ${GREEN}my_kaskad_bot${NC})" - echo -e " 5. BotFather выдаст токен вида:" - echo -e " ${GREEN}7123456789:AAH1bGz-example-token-here${NC}" - echo "" - echo -e " ${YELLOW}Шаг B.${NC} Введите токен:" - echo -e " Меню: ${YELLOW}8${NC} → ${YELLOW}1) Установить токен бота${NC}" - echo -e " Вставьте токен и нажмите Enter." - echo "" - echo -e " ${YELLOW}Шаг C.${NC} Получите Chat ID:" - echo -e " Меню: ${YELLOW}8${NC} → ${YELLOW}2) Получить Chat ID${NC}" - echo -e " 1. Откройте вашего бота в Telegram" - echo -e " 2. Отправьте ему любое сообщение (например: /start)" - echo -e " 3. Вернитесь в терминал, нажмите Enter" - echo -e " 4. Скрипт автоматически найдёт и сохранит Chat ID" - echo "" - echo -e " ${YELLOW}Шаг D.${NC} Запустите бота:" - echo -e " Меню: ${YELLOW}8${NC} → ${YELLOW}4) Запустить бота${NC}" - echo -e " Бот работает как systemd-сервис (автозапуск при ребуте)." - echo "" - echo -e " ${YELLOW}Шаг E.${NC} В Telegram отправьте ${GREEN}/start${NC} — появится меню." - ;; - 7) - echo -e "${CYAN}═══ TELEGRAM BOT: ВОЗМОЖНОСТИ ═══${NC}" - echo "" - echo -e "${WHITE}Главное меню бота:${NC}" - echo -e " 🔀 AWG/WireGuard — добавить UDP каскад" - echo -e " 🔀 VLESS/XRay — добавить TCP каскад" - echo -e " 🔀 MTProto/TProxy — добавить TCP каскад" - echo -e " 🛠 Custom Rule — кастомное правило (TCP/UDP)" - echo -e " 📋 Правила — показать активные правила" - echo -e " 🏓 Ping — пинг целевых серверов" - echo -e " 📊 Мониторинг — управление мониторами" - echo -e " ❌ Удалить правило — удалить конкретный каскад" - echo -e " 🗑 Сбросить всё — полная очистка" - echo "" - echo -e "${WHITE}Добавление правил (пример VLESS):${NC}" - echo -e " 1. Нажмите ${GREEN}🔀 VLESS/XRay${NC}" - echo -e " 2. Бот: 'Введите IP' → отправьте ${GREEN}45.10.20.30${NC}" - echo -e " 3. Бот: 'Введите порт' → отправьте ${GREEN}443${NC}" - echo -e " 4. Бот: '✅ VLESS настроен! tcp :443 → 45.10.20.30:443'" - echo "" - echo -e "${WHITE}Ping (три режима):${NC}" - echo -e " 1. Нажмите ${GREEN}🏓 Ping${NC} → выберите сервер" - echo -e " 2. ${YELLOW}1 раз${NC} — мгновенный результат: '15.3 ms'" - echo -e " 3. ${YELLOW}10 раз${NC} — пингует 10 раз, считает среднее" - echo -e " Результат: '#1: 15ms #2: 14ms ... Среднее: 14.8ms'" - echo -e " 4. ${YELLOW}60 секунд${NC} — непрерывный пинг, обновление каждые 10с" - echo -e " Итог: 'Мин: 14ms Макс: 22ms Среднее: 16ms'" - echo "" - echo -e "${WHITE}Мониторинг через бота:${NC}" - echo -e " 1. ${GREEN}📊 Мониторинг${NC} → ${GREEN}➕ Добавить${NC} → выберите сервер" - echo -e " 2. Выберите интервал (10с / 1мин / 5мин)" - echo -e " 3. Введите порог: ${GREEN}100${NC}" - echo -e " 4. Алерты будут приходить в этот же чат." - echo "" - echo -e "${CYAN}═══ ПУНКТЫ 9-13 ═══${NC}" - echo "" - echo -e " ${YELLOW}9${NC} — Удалить одно правило (выберите из списка)" - echo -e " ${YELLOW}10${NC} — Сбросить все правила Kaskad (с подтверждением)" - echo -e " ${YELLOW}11${NC} — Обновить скрипт до последней версии" - echo -e " ${YELLOW}12${NC} — Показать PROMO партнёров" - echo -e " ${YELLOW}13${NC} — Эта инструкция" - ;; + 1) echo -e "${CYAN}═══ ЧТО ТАКОЕ КАСКАД? ═══${NC}\n\nКаскад — 'мост' между устройством и зарубежным VPN/Proxy.\n\n ${WHITE}Клиент${NC} → ${GREEN}Этот сервер (РФ)${NC} → ${CYAN}Зарубежный VPN${NC} → Интернет\n\nПровайдер видит только подключение к серверу в РФ.\n\n${CYAN}═══ ЧТО НУЖНО ═══${NC}\n 1. VPS в РФ\n 2. Зарубежный VPN/Proxy\n 3. IP и порт зарубежного сервера" ;; + 2) echo -e "${CYAN}═══ ПУНКТ 1: AWG/WG (UDP) ═══${NC}\n\n Меню → 1 → IP: 45.10.20.30 → Порт: 51820\n В клиенте Endpoint: ${MY_IP:-185.1.2.3}:51820\n\n${CYAN}═══ ПУНКТ 2: VLESS (TCP) ═══${NC}\n\n Меню → 2 → IP: 67.89.100.200 → Порт: 443\n В v2rayNG/NekoBox замените адрес сервера" ;; + 3) echo -e "${CYAN}═══ ПУНКТ 3: MTProto ═══${NC}\n\n Меню → 3 → IP → Порт: 8443\n Telegram → Прокси → ${MY_IP:-*}:8443\n\n${CYAN}═══ ПУНКТ 4: Custom ═══${NC}\n\n Разные порты входа/выхода, SSH, RDP\n Пример SSH: tcp, IP, вход: 2222, выход: 22\n Подключение: ssh user@${MY_IP:-*} -p 2222" ;; + 4) echo -e "${CYAN}═══ ПРАВИЛА И PING ═══${NC}\n\nПункт 5 — таблица правил с IP каскада и именами серверов\nПункт 6 — Live Ping (1 сек обновление, Ctrl+C стоп)\nПункт 14 — управление именами серверов" ;; + 5) echo -e "${CYAN}═══ МОНИТОРИНГ ═══${NC}\n\nАвтопроверка серверов с Telegram-алертами.\n\n Меню → 7 → Добавить → сервер → интервал → порог → частота уведомл.\n\nСлужба запускается/останавливается автоматически.\nЧастота уведомлений: 10с / 60с / 5мин / 15мин" ;; + 6) echo -e "${CYAN}═══ TELEGRAM BOT ═══${NC}\n\n Шаг A. @BotFather → /newbot → получить токен\n Шаг B. Меню → 8 → 1 → вставить токен\n Шаг C. Меню → 8 → 2 → отправить боту сообщение → Enter\n Шаг D. Меню → 8 → 4 → Запустить\n Шаг E. В Telegram: /start\n\nБот имеет 2 стиля меню: компактный и большой (кнопка внизу)" ;; + 7) echo -e "${CYAN}═══ ВОЗМОЖНОСТИ БОТА ═══${NC}\n\n Добавление/удаление правил через кнопки\n Ping: 1x / 10x (среднее) / 60 сек\n Мониторинг: добавить/удалить/список\n 💻 Система: CPU, RAM, Swap, диск, топ процессов\n 🏢 Хостинг: промокоды партнёров\n Имена серверов отображаются везде\n\n Пункты 9-13: удаление, сброс, обновление, промо, инструкция" ;; esac - - echo "" - echo -e "${MAGENTA}──────────────────────────────────────────────────────────────${NC}" - if [ "$page" -eq 1 ]; then - echo -e " ${YELLOW}[N]${NC} Далее ${YELLOW}[0]${NC} Выход в меню" - elif [ "$page" -eq "$total_pages" ]; then - echo -e " ${YELLOW}[P]${NC} Назад ${YELLOW}[0]${NC} Выход в меню" - else - echo -e " ${YELLOW}[P]${NC} Назад ${YELLOW}[N]${NC} Далее ${YELLOW}[0]${NC} Выход в меню" - fi + echo -e "\n${MAGENTA}──────────────────────────────────────────────────────────────${NC}" + if [ "$page" -eq 1 ]; then echo -e " ${YELLOW}[N]${NC} Далее ${YELLOW}[0]${NC} Выход" + elif [ "$page" -eq "$total_pages" ]; then echo -e " ${YELLOW}[P]${NC} Назад ${YELLOW}[0]${NC} Выход" + else echo -e " ${YELLOW}[P]${NC} Назад ${YELLOW}[N]${NC} Далее ${YELLOW}[0]${NC} Выход"; fi read -p " > " nav - case "$nav" in - [nN]) (( page < total_pages )) && ((page++)) ;; - [pP]) (( page > 1 )) && ((page--)) ;; - 0) return ;; - [1-7]) page="$nav" ;; - esac + [nN]) (( page < total_pages )) && ((page++)) ;; [pP]) (( page > 1 )) && ((page--)) ;; 0) return ;; [1-7]) page="$nav" ;; esac done } @@ -1761,19 +1700,14 @@ show_menu() { clear echo -e "${MAGENTA}" echo "******************************************************" - echo " anten-ka канал представляет... Kaskad v${KASKAD_VERSION}" + echo " anten-ka канал представляет... Kaskad PRO v${KASKAD_VERSION}" echo " YouTube: https://www.youtube.com/@antenkaru" echo "******************************************************" echo -e "${NC}" - - echo -e "${WHITE}IP этого сервера: ${GREEN}${MY_IP}${NC}" - echo -e "${WHITE}Интерфейс: ${CYAN}${IFACE}${NC}" - echo "" - echo -e "${YELLOW}Инструкции:${NC}" - echo -e " ${BLUE}https://boosty.to/anten-ka${NC} | ${BLUE}https://antenka.taplink.ws${NC}" + echo -e "${WHITE}IP сервера: ${GREEN}${MY_IP}${NC} ${WHITE}Интерфейс: ${CYAN}${IFACE}${NC}" + echo -e "${YELLOW}Инструкции:${NC} ${BLUE}https://boosty.to/anten-ka${NC}" echo -e "${GREEN}Донат:${NC} https://pay.cloudtips.ru/p/7410814f" echo -e "------------------------------------------------------" - echo -e " 1) Настроить ${CYAN}AmneziaWG / WireGuard${NC} (UDP)" echo -e " 2) Настроить ${CYAN}VLESS / XRay${NC} (TCP)" echo -e " 3) Настроить ${CYAN}TProxy / MTProto${NC} (TCP)" @@ -1787,6 +1721,7 @@ show_menu() { echo -e "11) ${YELLOW}Обновить скрипт${NC}" echo -e "12) ${YELLOW}PROMO${NC}" echo -e "13) ${MAGENTA}📚 Инструкция${NC}" + echo -e "14) ${WHITE}Имена серверов${NC}" echo -e " 0) Выход" echo -e "------------------------------------------------------" read -p "Ваш выбор: " choice @@ -1805,6 +1740,7 @@ show_menu() { 11) self_update ;; 12) show_promo ;; 13) show_instructions ;; + 14) manage_aliases_menu ;; 0) exit 0 ;; *) ;; esac @@ -1824,16 +1760,6 @@ case "${1:-}" in init_config monitor_daemon ;; - --ping-1) - init_config - ip="$2"; chat_id="$3" - ms=$(ping -c 1 -W 3 "$ip" 2>/dev/null | grep -oP 'time=\K[\d.]+') - if [ -n "$ms" ]; then - tg_send "$chat_id" "🏓 Ping $ip: ${ms}ms" "$(kbd_back)" > /dev/null - else - tg_send "$chat_id" "🏓 Ping $ip: timeout" "$(kbd_back)" > /dev/null - fi - ;; *) check_root init_config