From 9d9d12e15042ee4384d0f0bdfe9696afbb1ceb30 Mon Sep 17 00:00:00 2001 From: anten-ka Date: Sat, 11 Apr 2026 23:48:53 +0300 Subject: [PATCH] v2.4.8: preflight port-conflict check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлена предустановочная проверка свободы портов перед установкой telemt. На вход приходит режим (lite/pro) и выбранный порт, проверяются 443 (lite: выбранный), 80 и 8443 (pro). Известный proxy/VPN/веб-софт (xray, sing-box, v2ray, trojan, hysteria, mtg, shadowsocks, x-ui, marzban, amneziawg, caddy, apache, haproxy, wireguard, openvpn) распознаётся по имени процесса и показывается отдельным блоком. При конфликте пользователь видит список занятых портов, получает рекомендацию удалить конфликтующий софт или взять чистый VPS, и может либо форсировать установку, либо отказаться — тогда показывается 15-секундный промо-блок с QR для донатов и возврат в меню. Override для автоматизированных сценариев: GOTELEGRAM_SKIP_PREFLIGHT=1. Files: lib/common.sh — get_port_process, match_known_conflict, preflight_check lib/lang/ru.sh — preflight_* i18n keys (ru) lib/lang/en.sh — preflight_* i18n keys (en) install.sh — preflight_check вызов в install_lite_mode / install_pro_mode; show_promo_with_qr теперь принимает countdown arg gotelegram-bot/bot.py — version bump 2.4.7 → 2.4.8 --- gotelegram-bot/bot.py | 2 +- install.sh | 18 ++++- lib/common.sh | 163 +++++++++++++++++++++++++++++++++++++++++- lib/lang/en.sh | 14 ++++ lib/lang/ru.sh | 14 ++++ 5 files changed, 207 insertions(+), 4 deletions(-) mode change 100755 => 100644 install.sh mode change 100755 => 100644 lib/lang/en.sh mode change 100755 => 100644 lib/lang/ru.sh diff --git a/gotelegram-bot/bot.py b/gotelegram-bot/bot.py index 8138b13..8f71c03 100644 --- a/gotelegram-bot/bot.py +++ b/gotelegram-bot/bot.py @@ -100,7 +100,7 @@ logger = logging.getLogger(__name__) # CONFIGURATION # ============================================================================ -GOTELEGRAM_VERSION = "2.4.7" +GOTELEGRAM_VERSION = "2.4.8" GOTELEGRAM_CONFIG = "/opt/gotelegram/config.json" TELEMT_CONFIG = "/etc/telemt/config.toml" TELEMT_SERVICE = "telemt" diff --git a/install.sh b/install.sh old mode 100755 new mode 100644 index 7204897..28dfa31 --- a/install.sh +++ b/install.sh @@ -295,6 +295,12 @@ install_lite_mode() { port=$(select_port) [ $? -ne 0 ] && return + # Preflight: port conflict check (checks the external port only for lite) + if ! preflight_check "lite" "$port"; then + show_promo_with_qr 15 + return + fi + # Generate secret local secret secret=$(generate_hex 32) @@ -342,6 +348,12 @@ install_lite_mode() { install_pro_mode() { log_step "$(t install_pro_step)" + # Preflight: pro mode needs 443, 80 and 8443 (internal nginx mask) + if ! preflight_check "pro"; then + show_promo_with_qr 15 + return + fi + # Enter domain echo "" echo -ne " ${WHITE}$(t install_enter_domain)${NC} " @@ -1142,6 +1154,7 @@ mark_promo_shown() { # текстовые ссылки и промокоды (см. _promo_block) — QR-коды хостеров # визуально конкурировали с чаевыми и перегружали экран. show_promo_with_qr() { + local countdown="${1:-5}" _promo_block # QR только для чаевых @@ -1154,8 +1167,9 @@ show_promo_with_qr() { mark_promo_shown - # 5-second countdown - for i in 5 4 3 2 1; do + # Countdown (default 5s, caller may pass longer for preflight abort) + local i + for ((i=countdown; i>0; i--)); do echo -ne "\r ${DIM}$(tf promo_menu_in "$i")${NC} " sleep 1 done diff --git a/lib/common.sh b/lib/common.sh index 591fd22..21cc9bc 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -3,7 +3,7 @@ # Colors, logging, spinner, system helpers, v1 compat, i18n-aware # ── Version ─────────────────────────────────────────────────────────────────── -GOTELEGRAM_VERSION="2.4.7" +GOTELEGRAM_VERSION="2.4.8" GOTELEGRAM_NAME="GoTelegram" # ── Пути ────────────────────────────────────────────────────────────────────── @@ -463,6 +463,167 @@ check_port() { return 1 # свободен } +# ── Preflight: port conflict detection ─────────────────────────────────────── +# Проверяет, что нужные для установки порты свободны. Если порт занят — +# определяет процесс и сопоставляет с известным списком proxy/VPN софта +# (xray, sing-box, v2ray, trojan, hysteria, mtg, shadowsocks, x-ui/3x-ui, +# marzban, amneziawg, caddy, apache, haproxy). Пользователь видит явное +# предупреждение и может либо прервать установку, либо продолжить на свой +# страх и риск (GOTELEGRAM_SKIP_PREFLIGHT=1 — полностью отключить проверку). +# +# Используемые порты GoTelegram: +# 443 — telemt (внешний, MTProxy + fake-TLS) — lite и pro +# 80 — nginx redirect + certbot ACME HTTP-01 — только pro +# 8443 — nginx internal mask (127.0.0.1:8443) — только pro + +# get_port_process → "|" если занят, иначе пусто +get_port_process() { + local port="$1" + local line="" pid="" proc="" + line=$(ss -tlnp 2>/dev/null | grep -E ":${port}[[:space:]]" | head -1) + if [ -z "$line" ]; then + line=$(netstat -tlnp 2>/dev/null | grep -E ":${port}[[:space:]]" | head -1) + fi + if [ -n "$line" ]; then + pid=$(echo "$line" | grep -oE 'pid=[0-9]+' | head -1 | cut -d= -f2) + if [ -z "$pid" ]; then + # netstat format: "12345/procname" + pid=$(echo "$line" | grep -oE '[0-9]+/[^ ]+' | head -1 | cut -d/ -f1) + fi + fi + if [ -z "$pid" ]; then + pid=$(fuser -n tcp "$port" 2>/dev/null | tr -s ' ' | awk '{print $1}' | head -1) + pid="${pid:-}" + fi + if [ -n "$pid" ] && [ "$pid" -gt 0 ] 2>/dev/null; then + proc=$(ps -p "$pid" -o comm= 2>/dev/null | tr -d ' \n') + [ -z "$proc" ] && proc="unknown" + echo "${pid}|${proc}" + return 0 + fi + if [ -n "$line" ]; then + # Port is occupied but process cannot be identified (kernel socket / no root) + echo "0|unknown" + return 0 + fi + return 1 +} + +# match_known_conflict → печатает человекочитаемое имя если это +# известный proxy/VPN/web софт. Возвращает 0 если нашли, 1 иначе. +match_known_conflict() { + local proc="$1" + case "$proc" in + *xray*|*Xray*) echo "Xray"; return 0 ;; + *sing-box*|*sing_box*|*singbox*) echo "sing-box"; return 0 ;; + *v2ray*|*V2Ray*) echo "V2Ray"; return 0 ;; + *trojan*) echo "Trojan"; return 0 ;; + *hysteria*) echo "Hysteria"; return 0 ;; + *mtg*) echo "mtg (old MTProxy)"; return 0 ;; + *ss-server*|*ss-local*|*shadowsocks*|*ssserver*) echo "Shadowsocks"; return 0 ;; + *x-ui*|*3x-ui*|*xui*) echo "x-ui / 3x-ui panel"; return 0 ;; + *marzban*) echo "Marzban panel"; return 0 ;; + *amneziawg*|*awg-go*|*awg*) echo "AmneziaWG"; return 0 ;; + *caddy*) echo "Caddy web server"; return 0 ;; + *apache2*|*httpd*) echo "Apache httpd"; return 0 ;; + *haproxy*) echo "HAProxy"; return 0 ;; + *nginx*) echo "nginx (already running)"; return 0 ;; + *tgproxy*|*mtproxy*|*mtproto*) echo "MTProto Proxy (other impl)"; return 0 ;; + *wireguard*|*wg-quick*) echo "WireGuard"; return 0 ;; + *openvpn*) echo "OpenVPN"; return 0 ;; + esac + return 1 +} + +# preflight_check [port] +# mode = "lite" | "pro" +# port = selected port for lite mode (default 443) +# Returns: +# 0 — OK to proceed (no conflicts, or user confirmed to force) +# 1 — user aborted (caller should show promo and return) +preflight_check() { + local mode="${1:-lite}" + local lite_port="${2:-443}" + + # Escape hatch + if [ "${GOTELEGRAM_SKIP_PREFLIGHT:-0}" = "1" ]; then + log_dim "preflight: skipped (GOTELEGRAM_SKIP_PREFLIGHT=1)" + return 0 + fi + + local required_ports=() + if [ "$mode" = "pro" ]; then + required_ports=(443 80 8443) + else + # lite: проверяем только выбранный внешний порт + required_ports=("$lite_port") + fi + + local known_conflicts=() unknown_conflicts=() info port pid proc label + for port in "${required_ports[@]}"; do + info=$(get_port_process "$port") + if [ -n "$info" ]; then + pid="${info%%|*}" + proc="${info##*|}" + if label=$(match_known_conflict "$proc"); then + known_conflicts+=("${port}|${label}|${pid}|${proc}") + else + unknown_conflicts+=("${port}|${pid}|${proc}") + fi + fi + done + + if [ ${#known_conflicts[@]} -eq 0 ] && [ ${#unknown_conflicts[@]} -eq 0 ]; then + log_dim "preflight: ports ${required_ports[*]} свободны" + return 0 + fi + + # Показываем баннер конфликта + echo "" >&2 + echo -e " ${BOLD}${YELLOW}⚠ $(_t_or preflight_title 'Предустановочная проверка: обнаружены конфликты портов')${NC}" >&2 + echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" >&2 + + local item p label2 pid2 proc2 rest + if [ ${#known_conflicts[@]} -gt 0 ]; then + echo -e " ${RED}$(_t_or preflight_known 'Известный proxy/VPN/веб-софт занимает нужные порты:')${NC}" >&2 + for item in "${known_conflicts[@]}"; do + p="${item%%|*}" + rest="${item#*|}" + label2="${rest%%|*}" + rest="${rest#*|}" + pid2="${rest%%|*}" + proc2="${rest##*|}" + echo -e " ${RED}✗${NC} ${BOLD}:${p}${NC} → ${BOLD}${label2}${NC} ${DIM}(pid=${pid2}, cmd=${proc2})${NC}" >&2 + done + fi + if [ ${#unknown_conflicts[@]} -gt 0 ]; then + echo -e " ${YELLOW}$(_t_or preflight_unknown 'Порты заняты неизвестными процессами:')${NC}" >&2 + for item in "${unknown_conflicts[@]}"; do + p="${item%%|*}" + rest="${item#*|}" + pid2="${rest%%|*}" + proc2="${rest##*|}" + echo -e " ${YELLOW}⚠${NC} ${BOLD}:${p}${NC} ${DIM}(pid=${pid2}, cmd=${proc2})${NC}" >&2 + done + fi + + echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" >&2 + echo -e " ${WHITE}$(_t_or preflight_needed 'GoTelegram нужны порты:')${NC} ${CYAN}${required_ports[*]}${NC}" >&2 + echo -e " ${WHITE}$(_t_or preflight_hint_header 'Рекомендации:')${NC}" >&2 + echo -e " ${DIM}• $(_t_or preflight_hint1 'Остановите и удалите конфликтующие сервисы (systemctl stop ...)')${NC}" >&2 + echo -e " ${DIM}• $(_t_or preflight_hint2 'Либо возьмите чистый VPS без других прокси')${NC}" >&2 + echo -e " ${DIM}• $(_t_or preflight_hint3 'Установка поверх, скорее всего, завершится некорректно')${NC}" >&2 + echo -e " ${DIM}$(_t_or preflight_skip_hint 'Override: GOTELEGRAM_SKIP_PREFLIGHT=1 gotelegram')${NC}" >&2 + echo "" >&2 + + if confirm "$(_t_or preflight_proceed 'Продолжить установку всё равно (скорее всего не заработает)?')"; then + log_warning "$(_t_or preflight_forced 'Установка продолжена вопреки конфликтам — возможны ошибки')" + return 0 + fi + log_info "$(_t_or preflight_aborted 'Установка отменена из-за конфликтов портов')" + return 1 +} + check_disk_space() { local min_mb="${1:-500}" local avail_mb diff --git a/lib/lang/en.sh b/lib/lang/en.sh old mode 100755 new mode 100644 index fd3046e..8163c20 --- a/lib/lang/en.sh +++ b/lib/lang/en.sh @@ -347,6 +347,20 @@ I18N[backup_pass_short]="Password too short (minimum 6 characters)" I18N[backup_pick_prompt]="Backup number (or path to file)" I18N[backup_not_found]="Backup not found" +# ── Preflight (v2.4.8) ────────────────────────────────────────────────── +I18N[preflight_title]="Preflight: port conflicts detected" +I18N[preflight_known]="Known proxy/VPN/web software is using required ports:" +I18N[preflight_unknown]="Required ports are held by unknown processes:" +I18N[preflight_needed]="GoTelegram requires ports:" +I18N[preflight_hint_header]="Recommended actions:" +I18N[preflight_hint1]="Stop and remove the conflicting services (systemctl stop ...)" +I18N[preflight_hint2]="Or use a clean VPS without other proxies" +I18N[preflight_hint3]="Installing on top will most likely fail" +I18N[preflight_skip_hint]="Bypass: GOTELEGRAM_SKIP_PREFLIGHT=1 gotelegram" +I18N[preflight_proceed]="Continue installation anyway (likely to fail)?" +I18N[preflight_forced]="Installation continued despite conflicts — errors likely" +I18N[preflight_aborted]="Installation aborted due to port conflicts" + # ── Errors / misc ─────────────────────────────────────────────────────── I18N[err_need_root]="Run the script with sudo / as root" I18N[err_os_unknown]="Failed to detect OS. Linux is required." diff --git a/lib/lang/ru.sh b/lib/lang/ru.sh old mode 100755 new mode 100644 index 6101c23..8e8b3c4 --- a/lib/lang/ru.sh +++ b/lib/lang/ru.sh @@ -347,6 +347,20 @@ I18N[backup_pass_short]="Пароль слишком короткий (мини I18N[backup_pick_prompt]="Номер бекапа (или путь к файлу)" I18N[backup_not_found]="Бекап не найден" +# ── Preflight (v2.4.8) ────────────────────────────────────────────────── +I18N[preflight_title]="Предустановочная проверка: обнаружены конфликты портов" +I18N[preflight_known]="Известный proxy/VPN/веб-софт занимает нужные порты:" +I18N[preflight_unknown]="Порты заняты неизвестными процессами:" +I18N[preflight_needed]="GoTelegram нужны порты:" +I18N[preflight_hint_header]="Рекомендации:" +I18N[preflight_hint1]="Остановите и удалите конфликтующие сервисы (systemctl stop ...)" +I18N[preflight_hint2]="Либо возьмите чистый VPS без других прокси" +I18N[preflight_hint3]="Установка поверх, скорее всего, завершится некорректно" +I18N[preflight_skip_hint]="Обойти проверку: GOTELEGRAM_SKIP_PREFLIGHT=1 gotelegram" +I18N[preflight_proceed]="Продолжить установку всё равно (скорее всего не заработает)?" +I18N[preflight_forced]="Установка продолжена вопреки конфликтам — возможны ошибки" +I18N[preflight_aborted]="Установка отменена из-за конфликтов портов" + # ── Errors / misc ─────────────────────────────────────────────────────── I18N[err_need_root]="Запустите скрипт с sudo / от root" I18N[err_os_unknown]="Не удалось определить ОС. Требуется Linux."