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" <${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 <