#!/bin/bash # ══════════════════════════════════════════════════════════════════════════════ # GoTelegram v2.3.0 — MTProxy на ядре telemt (Rust + Tokio) # Anti-DPI • Fake TLS • TCP Splice • JA3/JA4 Resistance # # Установка: # curl -sL URL/install.sh | sudo bash # ══════════════════════════════════════════════════════════════════════════════ set -uo pipefail # Путь к скрипту и библиотекам SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" LIB_DIR="$SCRIPT_DIR/lib" # Загружаем библиотеки source "$LIB_DIR/common.sh" source "$LIB_DIR/telemt.sh" source "$LIB_DIR/telemt_config.sh" source "$LIB_DIR/website.sh" source "$LIB_DIR/templates_catalog.sh" source "$LIB_DIR/backup.sh" [ -f "$LIB_DIR/stats.sh" ] && source "$LIB_DIR/stats.sh" # ── Главное меню (Compact Dashboard + 5 Top-Level Items) ────────────────────── show_main_menu() { local proxy_status bot_status nginx_st mode domain secret port ip link ssl_expiry proxy_status=$(telemt_status) bot_status=$(bot_service_status) nginx_st=$(nginx_status 2>/dev/null || echo "stopped") mode=$(config_get mode 2>/dev/null || echo "—") domain=$(config_get domain 2>/dev/null || echo "") secret=$(get_config_value secret 2>/dev/null || echo "") port=$(get_config_value port 2>/dev/null || echo "443") ip=$(get_server_ip 2>/dev/null || echo "N/A") local W=54 local line; line=$(printf '━%.0s' $(seq 1 $W)) local line2; line2=$(printf '─%.0s' $(seq 1 $W)) # ── Заголовок (без правого бордера — ANSI ломает выравнивание) ── echo "" echo -e " ${BOLD}${CYAN}━${line}━${NC}" echo -e " ${BOLD}${WHITE} GoTelegram v${GOTELEGRAM_VERSION}${NC} ${DIM}— Панель управления${NC}" echo -e " ${BOLD}${CYAN}━${line}━${NC}" # ── Здоровье сервисов ── echo "" echo -e " ${DIM}${line2}${NC}" # Proxy local proxy_icon proxy_color case "$proxy_status" in running) proxy_icon="●"; proxy_color="${GREEN}" ;; stopped) proxy_icon="○"; proxy_color="${YELLOW}" ;; *) proxy_icon="✗"; proxy_color="${RED}" ;; esac echo -e " ${proxy_color}${proxy_icon}${NC} Прокси ${proxy_color}${proxy_status}${NC} ${DIM}(telemt ${mode})${NC}" # nginx local nginx_icon nginx_color case "$nginx_st" in running) nginx_icon="●"; nginx_color="${GREEN}" ;; *) nginx_icon="✗"; nginx_color="${RED}" ;; esac echo -e " ${nginx_icon}${nginx_color}${NC} nginx ${nginx_color}${nginx_st}${NC} ${DIM}(127.0.0.1:8443)${NC}" # Site (pro) if [ "$mode" = "pro" ] && [ -n "$domain" ]; then local site_icon site_color if curl -sk --max-time 3 "https://${domain}/" -o /dev/null 2>/dev/null; then site_icon="●"; site_color="${GREEN}" else site_icon="✗"; site_color="${RED}" fi echo -e " ${site_color}${site_icon}${NC} Сайт ${site_color}https://${domain}${NC}" ssl_expiry=$(get_ssl_expiry "$domain" 2>/dev/null || echo "N/A") echo -e " ${GREEN}●${NC} SSL ${DIM}до ${ssl_expiry}${NC}" fi # Bot case "$bot_status" in running) echo -e " ${GREEN}●${NC} Бот ${GREEN}running${NC}" ;; stopped) echo -e " ${YELLOW}○${NC} Бот ${YELLOW}stopped${NC}" ;; esac echo -e " ${DIM}${line2}${NC}" # ── Сетевые параметры ── echo -e " ${WHITE}IP:${NC} ${CYAN}${ip}${NC} ${WHITE}Порт:${NC} ${CYAN}${port}${NC} ${WHITE}Режим:${NC} ${CYAN}${mode}${NC}" if [ -n "$domain" ]; then echo -e " ${WHITE}Домен:${NC} ${CYAN}${domain}${NC}" fi echo -e " ${DIM}${line2}${NC}" # ── Прокси-ссылка + QR ── if [ -n "$secret" ] && [ "$proxy_status" = "running" ]; then if [ "$mode" = "pro" ] && [ -n "$domain" ]; then local raw_secret faketls_secret domain_hex raw_secret="$secret" domain_hex=$(printf '%s' "$domain" | xxd -p | tr -d '\n') faketls_secret="ee${raw_secret}${domain_hex}" link="tg://proxy?server=${domain}&port=${port}&secret=${faketls_secret}" else link="tg://proxy?server=${ip}&port=${port}&secret=${secret}" fi echo -e " ${BOLD}${WHITE}Ссылка для Telegram:${NC}" echo -e " ${GREEN}${link}${NC}" if command -v qrencode &>/dev/null; then echo "" qrencode -t UTF8 -m 2 "$link" 2>/dev/null | while IFS= read -r qr_line; do echo " ${qr_line}" done echo "" fi else echo -e " ${DIM}Прокси не настроен. Выберите пункт 1.${NC}" echo "" fi # ── Меню ── echo -e " ${DIM}${line2}${NC}" echo -e " ${CYAN}1${NC}) Прокси ▸" echo -e " ${CYAN}2${NC}) Статистика ▸" echo -e " ${CYAN}3${NC}) Управление ▸" echo -e " ${CYAN}4${NC}) Telegram-бот ▸" echo -e " ${CYAN}5${NC}) О программе ▸" echo -e " ${CYAN}0${NC}) ${DIM}Выход${NC}" echo -e " ${DIM}${line2}${NC}" echo -e " ${DIM}Обновление через 30 сек${NC}" echo -ne " ${WHITE}▸ ${NC}" } # ── Подменю: Прокси ────────────────────────────────────────────────────────── submenu_proxy() { while true; do echo "" echo -e " ${BOLD}${WHITE}🚀 ПРОКСИ${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}" echo -e " ${CYAN}1${NC}) Установить / Обновить" echo -e " ${CYAN}2${NC}) Статус подробно" echo -e " ${CYAN}3${NC}) Скопировать ссылку" echo -e " ${CYAN}4${NC}) Поделиться ключом" echo -e " ${CYAN}5${NC}) Перезапуск" echo -e " ${CYAN}6${NC}) Логи" echo -e " ${CYAN}7${NC}) Сменить режим / шаблон" echo -e " ${CYAN}0${NC}) « Назад" echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}" echo -ne " ${WHITE}Выбор:${NC} " read -r ch case "$ch" in 1) menu_install ;; 2) menu_status ;; 3) menu_link ;; 4) menu_share ;; 5) menu_restart ;; 6) menu_logs ;; 7) menu_change_mode ;; 0) break ;; *) log_error "Неверный выбор" ;; esac echo "" echo -ne " ${DIM}Нажмите Enter...${NC}" read -r done } # ── Подменю: Управление ────────────────────────────────────────────────────── submenu_manage() { while true; do echo "" echo -e " ${BOLD}${WHITE}⚙️ УПРАВЛЕНИЕ${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}" echo -e " ${CYAN}1${NC}) Бекап" echo -e " ${CYAN}2${NC}) Восстановить" echo -e " ${CYAN}3${NC}) Обновить telemt" echo -e " ${CYAN}4${NC}) Сайт / SSL" echo -e " ${CYAN}5${NC}) Удалить" echo -e " ${CYAN}0${NC}) « Назад" echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}" echo -ne " ${WHITE}Выбор:${NC} " read -r ch case "$ch" in 1) interactive_backup ;; 2) interactive_restore ;; 3) update_telemt ;; 4) menu_website ;; 5) menu_remove ;; 0) break ;; *) log_error "Неверный выбор" ;; esac echo "" echo -ne " ${DIM}Нажмите Enter...${NC}" read -r done } # ── Подменю: О программе ───────────────────────────────────────────────────── submenu_about() { while true; do echo "" echo -e " ${BOLD}${WHITE}ℹ️ О ПРОГРАММЕ${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}" echo -e " ${CYAN}1${NC}) Информация о версии" echo -e " ${CYAN}2${NC}) Промо / Донат" echo -e " ${CYAN}0${NC}) « Назад" echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}" echo -ne " ${WHITE}Выбор:${NC} " read -r ch case "$ch" in 1) menu_version ;; 2) menu_promo ;; 0) break ;; *) log_error "Неверный выбор" ;; esac echo "" echo -ne " ${DIM}Нажмите Enter...${NC}" read -r done } # ── Информация о версии ────────────────────────────────────────────────────── menu_version() { echo "" echo -e " ${BOLD}${WHITE}🔍 Информация${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}" echo -e " ${WHITE}GoTelegram:${NC} v${GOTELEGRAM_VERSION}" echo -e " ${WHITE}Ядро:${NC} telemt (Rust + Tokio)" echo -e " ${WHITE}Технология:${NC} Anti-DPI, Fake TLS, TCP Splice" echo -e " ${WHITE}Лицензия:${NC} MIT" echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}" } # ── Установка: выбор режима ────────────────────────────────────────────────── menu_install() { # Проверяем v1 if detect_v1_installation; then echo "" echo -e " ${YELLOW}⚠️ Обнаружена установка GoTelegram v1 (mtg)${NC}" echo -e " ${DIM}Контейнер: ${V1_CONTAINER_NAME}${NC}" echo "" if ! migrate_v1_to_v2; then return fi fi echo "" echo -e " ${BOLD}${WHITE}🎭 Выберите режим маскировки:${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" echo -e " ${CYAN}1)${NC} ${GREEN}⚡ Lite${NC} — маскировка под популярный сайт" echo -e " ${DIM}Быстро, без домена. telemt маскирует трафик${NC}" echo -e " ${DIM}под выбранный сайт (google.com и т.д.)${NC}" echo "" echo -e " ${CYAN}2)${NC} ${MAGENTA}🛡 Pro${NC} — свой сайт + полная маскировка" echo -e " ${DIM}nginx + SSL + HTML-шаблон + telemt.${NC}" echo -e " ${DIM}DPI видит реальный сайт с реальным сертификатом.${NC}" echo -e " ${DIM}Требует: домен, направленный на этот сервер.{{NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" echo -ne " ${WHITE}Выбор (1/2):${NC} " read -r mode_choice mode_choice="${mode_choice:-}" case "$mode_choice" in 1) install_lite_mode ;; 2) install_pro_mode ;; *) log_error "Неверный выбор: ${mode_choice:-<пусто>}" ;; esac } # ── Lite-режим ─────────────────────────────────────────────────────────────── install_lite_mode() { log_step "Установка Lite-режима" # Выбор домена local domain domain=$(select_quick_domain) [ $? -ne 0 ] && return # Выбор порта local port port=$(select_port) [ $? -ne 0 ] && return # Генерация секрета local secret secret=$(generate_hex 32) # Подтверждение local ip ip=$(get_server_ip) echo "" echo -e " ${BOLD}${WHITE}📋 Конфигурация:${NC}" echo -e " IP: ${CYAN}${ip}${NC}" echo -e " Порт: ${CYAN}${port}${NC}" echo -e " Маскировка: ${CYAN}${domain}${NC}" echo -e " Режим: ${GREEN}Lite${NC}" echo "" if ! confirm "Установить прокси?"; then return fi # Установка ensure_deps install_telemt_full || return # Генерируем конфиг telemt generate_telemt_toml "$secret" "$port" "lite" "$domain" "443" # Валидация validate_telemt_config || return # Запуск start_telemt || return # Сохраняем GoTelegram конфиг save_gotelegram_config "telemt" "lite" "$port" "$secret" "$domain" "" "" # Благодарности show_credits # Результат show_proxy_info log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Lite-режим)" } # ── Pro-режим ──────────────────────────────────────────────────────────────── install_pro_mode() { log_step "Установка Pro-режима" # Ввод домена echo "" echo -ne " ${WHITE}Введите ваш домен (например, example.com):${NC} " read -r user_domain if [ -z "$user_domain" ] || ! validate_domain "$user_domain"; then log_error "Некорректный домен: ${user_domain:-<пусто>}" return fi # Проверяем DNS local resolved_ip server_ip resolved_ip=$(dig +short "$user_domain" A 2>/dev/null | head -1) server_ip=$(get_server_ip) if [ -n "$resolved_ip" ] && [ "$resolved_ip" != "$server_ip" ]; then log_warning "Домен $user_domain указывает на $resolved_ip, а не на $server_ip" if ! confirm "Продолжить всё равно?"; then return fi fi # Email для Let's Encrypt echo -ne " ${WHITE}Email для SSL (Enter = без email):${NC} " read -r ssl_email # Выбор шаблона local template_dir template_dir=$(interactive_template_selection) [ $? -ne 0 ] && return # Архитектура Pro: # telemt слушает на 0.0.0.0:443 (принимает ВСЕ подключения) # nginx слушает на 127.0.0.1:8443 с SSL (обслуживает сайт) # MTProxy клиент → :443 → telemt (проксирует) # Обычный браузер → :443 → telemt → 127.0.0.1:8443 → nginx (сайт) # Провайдер видит только HTTPS на 443 к домену local nginx_internal_port=8443 echo "" echo -e " ${DIM}telemt принимает весь трафик на 443 (маскировка под HTTPS)${NC}" echo -e " ${DIM}nginx обслуживает сайт на внутреннем порту $nginx_internal_port${NC}" echo -e " ${DIM}Провайдер видит только HTTPS-трафик к ${user_domain}:443${NC}" # Генерация fake-TLS секрета (ee + secret + hex domain) # Префикс ee говорит Telegram-клиенту маскировать трафик под TLS к домену local raw_secret raw_secret=$(generate_hex 32) local domain_hex domain_hex=$(printf '%s' "$user_domain" | xxd -p | tr -d '\n') local faketls_secret="ee${raw_secret}${domain_hex}" # Подтверждение echo "" echo -e " ${BOLD}${WHITE}📋 Конфигурация:${NC}" echo -e " Домен: ${CYAN}${user_domain}${NC}" echo -e " Порт: ${CYAN}443 (telemt + nginx внутри)${NC}" echo -e " Режим: ${MAGENTA}Pro (fake-TLS)${NC}" echo "" if ! confirm "Установить прокси + сайт?"; then return fi # Установка ensure_deps install_telemt_full || return # Конфиг telemt: слушает 443, маскировка на локальный nginx через dns_override generate_telemt_toml "$raw_secret" "443" "pro" "$user_domain" "$nginx_internal_port" # Настройка сайта (nginx на внутреннем порту + certbot + шаблон) setup_pro_mode "$user_domain" "$template_dir" "$nginx_internal_port" "$ssl_email" || return # Останавливаем nginx на 443 перед запуском telemt (telemt займёт 443) # nginx уже перенастроен на внутренний порт systemctl restart nginx 2>/dev/null # Запуск telemt start_telemt || return # Сохраняем конфиг local tpl_id tpl_id=$(basename "$template_dir") save_gotelegram_config "telemt" "pro" "443" "$raw_secret" "$user_domain" "$user_domain" "$tpl_id" # Результат — используем домен и fake-TLS ссылку show_proxy_info_pro "$user_domain" "$faketls_secret" echo -e " ${WHITE}Сайт:${NC} ${GREEN}https://${user_domain}${NC}" log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Pro-режим)" } # ── Статус ─────────────────────────────────────────────────────────────────── menu_status() { show_proxy_info # Дополнительно для pro local mode mode=$(config_get mode 2>/dev/null) if [ "$mode" = "pro" ]; then local domain domain=$(config_get domain 2>/dev/null) if [ -n "$domain" ]; then local ssl_expiry ssl_expiry=$(get_ssl_expiry "$domain") local nginx_st nginx_st=$(nginx_status) echo -e " ${WHITE}nginx:${NC} ${nginx_st}" echo -e " ${WHITE}SSL до:${NC} ${ssl_expiry}" echo -e " ${WHITE}Сайт:${NC} https://${domain}" echo "" fi fi } # ── Ссылка ─────────────────────────────────────────────────────────────────── menu_link() { local secret port ip link mode domain secret=$(get_config_value secret) port=$(get_config_value port) ip=$(get_server_ip) mode=$(config_get mode 2>/dev/null || echo "lite") domain=$(config_get domain 2>/dev/null || echo "") if [ "$mode" = "pro" ] && [ -n "$domain" ]; then local domain_hex faketls_secret domain_hex=$(printf '%s' "$domain" | xxd -p | tr -d '\n') faketls_secret="ee${secret}${domain_hex}" link="tg://proxy?server=${domain}&port=${port}&secret=${faketls_secret}" else link=$(generate_proxy_link "$ip" "$port" "$secret") fi echo "" echo -e " ${BOLD}${WHITE}🔗 Ссылка для подключения:${NC}" echo "" echo -e " ${GREEN}${link}${NC}" echo "" if command -v qrencode &>/dev/null; then qrencode -t UTF8 -m 2 "$link" 2>/dev/null fi } # ── Поделиться ─────────────────────────────────────────────────────────────── menu_share() { local secret port ip link mode domain server_display secret=$(get_config_value secret) port=$(get_config_value port) ip=$(get_server_ip) mode=$(config_get mode 2>/dev/null || echo "lite") domain=$(config_get domain 2>/dev/null || echo "") if [ "$mode" = "pro" ] && [ -n "$domain" ]; then local domain_hex faketls_secret domain_hex=$(printf '%s' "$domain" | xxd -p | tr -d '\n') faketls_secret="ee${secret}${domain_hex}" link="tg://proxy?server=${domain}&port=${port}&secret=${faketls_secret}" server_display="$domain" else link=$(generate_proxy_link "$ip" "$port" "$secret") server_display="$ip" fi echo "" echo -e " ${BOLD}📤 Перешлите это сообщение:${NC}" echo "" echo "🔐 MTProxy для Telegram (GoTelegram v${GOTELEGRAM_VERSION})" echo "" echo "🌍 Сервер: $server_display" echo "🔌 Порт: $port" echo "" echo "👉 Подключиться одним нажатием:" echo "$link" echo "" echo "Просто нажмите на ссылку или настройте вручную." echo "" } # ── Перезапуск ─────────────────────────────────────────────────────────────── menu_restart() { restart_telemt local mode mode=$(config_get mode 2>/dev/null) if [ "$mode" = "pro" ]; then restart_nginx fi } # ── Логи ───────────────────────────────────────────────────────────────────── menu_logs() { echo "" echo -e " ${BOLD}${WHITE}📋 Логи telemt (последние 40 строк):${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" telemt_logs 40 echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" } # ── Смена режима / шаблона ─────────────────────────────────────────────────── menu_change_mode() { local current_mode current_mode=$(config_get mode 2>/dev/null) echo "" echo -e " ${WHITE}Текущий режим:${NC} ${CYAN}${current_mode}${NC}" echo "" echo -e " ${CYAN}1${NC}) Сменить шаблон сайта (только pro)" echo -e " ${CYAN}2${NC}) Переключить режим (lite ↔ pro)" echo -e " ${CYAN}0${NC}) Назад" echo -ne " ${WHITE}Выбор:${NC} " read -r ch case "$ch" in 1) if [ "$current_mode" != "pro" ]; then log_error "Смена шаблона доступна только в pro-режиме" return fi local template_dir template_dir=$(interactive_template_selection) [ $? -ne 0 ] && return switch_template "$template_dir" ;; 2) log_warning "Переключение режима требует переустановки." if confirm "Переустановить прокси?"; then menu_install fi ;; esac } # ── Управление сайтом ──────────────────────────────────────────────────────── menu_website() { local mode mode=$(config_get mode 2>/dev/null) if [ "$mode" != "pro" ]; then log_info "Управление сайтом доступно только в pro-режиме" return fi local domain domain=$(config_get domain 2>/dev/null) echo "" echo -e " ${BOLD}${WHITE}🌐 Управление сайтом${NC}" echo -e " Домен: ${CYAN}${domain}${NC}" echo -e " SSL до: $(get_ssl_expiry "$domain")" echo "" echo -e " ${CYAN}1${NC}) Обновить SSL сертификат" echo -e " ${CYAN}2${NC}) Перезапустить nginx" echo -e " ${CYAN}3${NC}) Сменить шаблон" echo -e " ${CYAN}0${NC}) Назад" echo -ne " ${WHITE}Выбор:${NC} " read -r ch case "$ch" in 1) renew_ssl_certificate ;; 2) restart_nginx ;; 3) local template_dir template_dir=$(interactive_template_selection) [ $? -ne 0 ] && return switch_template "$template_dir" ;; esac } # ── Удаление ───────────────────────────────────────────────────────────────── menu_remove() { echo "" echo -e " ${BOLD}${RED}🗑 Удаление GoTelegram${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" echo -e " ${CYAN}1${NC}) Удалить только прокси (telemt)" echo -e " ${CYAN}2${NC}) Удалить только Telegram-бота" echo -e " ${CYAN}3${NC}) Удалить всё (прокси + бот + настройки)" echo -e " ${CYAN}0${NC}) Назад" echo -ne " ${WHITE}Выбор:${NC} " read -r rm_choice case "$rm_choice" in 1) log_warning "Это удалит прокси и все его настройки." if ! confirm "Удалить прокси?"; then return; fi if confirm "Сделать бекап перед удалением?"; then interactive_backup fi remove_telemt local mode mode=$(config_get mode 2>/dev/null) if [ "$mode" = "pro" ]; then remove_pro_mode fi rm -f "$GOTELEGRAM_CONFIG" log_success "Прокси удалён" ;; 2) bot_remove ;; 3) log_warning "Это удалит ВСЁ: прокси, бот, сайт, настройки." if ! confirm "Вы точно уверены?"; then return; fi if confirm "Сделать бекап перед удалением?"; then interactive_backup fi # Прокси remove_telemt local mode mode=$(config_get mode 2>/dev/null) if [ "$mode" = "pro" ]; then remove_pro_mode fi rm -f "$GOTELEGRAM_CONFIG" # Бот if [ "$(bot_service_status)" != "not_installed" ]; then systemctl stop "$BOT_SERVICE" 2>/dev/null systemctl disable "$BOT_SERVICE" 2>/dev/null rm -f "/etc/systemd/system/${BOT_SERVICE}.service" systemctl daemon-reload rm -rf "$BOT_DIR" fi log_success "GoTelegram полностью удалён (прокси + бот)" ;; esac } # ── Telegram-бот ──────────────────────────────────────────────────────────── BOT_DIR="/opt/gotelegram-bot" BOT_SERVICE="gotelegram-bot" bot_service_status() { if ! systemctl list-unit-files "$BOT_SERVICE.service" &>/dev/null 2>&1; then echo "not_installed" elif systemctl is-active "$BOT_SERVICE" &>/dev/null 2>&1; then echo "running" else echo "stopped" fi } menu_bot() { local st st=$(bot_service_status) echo "" echo -e " ${BOLD}${WHITE}🤖 Telegram-бот${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" case "$st" in running) echo -e " Статус: ${GREEN}● Работает${NC}" echo "" echo -e " ${CYAN}1${NC}) 📊 Статус бота" echo -e " ${CYAN}2${NC}) 📋 Логи бота" echo -e " ${CYAN}3${NC}) 🔄 Перезапустить бота" echo -e " ${CYAN}4${NC}) ⏹ Остановить бота" echo -e " ${CYAN}5${NC}) ⚙️ Настройки (.env)" echo -e " ${CYAN}6${NC}) 🗑 Удалить бота" ;; stopped) echo -e " Статус: ${YELLOW}○ Остановлен${NC}" echo "" echo -e " ${CYAN}1${NC}) 📊 Статус бота" echo -e " ${CYAN}2${NC}) 📋 Логи бота" echo -e " ${CYAN}3${NC}) ▶️ Запустить бота" echo -e " ${CYAN}5${NC}) ⚙️ Настройки (.env)" echo -e " ${CYAN}6${NC}) 🗑 Удалить бота" ;; *) echo -e " Статус: ${RED}✗ Не установлен${NC}" echo "" echo -e " ${DIM}Бот позволяет управлять прокси прямо из Telegram:${NC}" echo -e " ${DIM}статус, перезапуск, смена режима, бекап, QR-код.${NC}" echo "" echo -e " ${CYAN}1${NC}) 🔧 Установить бота" ;; esac echo -e " ${CYAN}0${NC}) « Назад" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" echo -ne " ${WHITE}Выбор:${NC} " read -r ch case "$st" in running) case "$ch" in 1) bot_show_status ;; 2) bot_show_logs ;; 3) systemctl restart "$BOT_SERVICE" && log_success "Бот перезапущен" ;; 4) systemctl stop "$BOT_SERVICE" && log_info "Бот остановлен" ;; 5) bot_edit_config ;; 6) bot_remove ;; esac ;; stopped) case "$ch" in 1) bot_show_status ;; 2) bot_show_logs ;; 3) systemctl start "$BOT_SERVICE" && log_success "Бот запущен" ;; 5) bot_edit_config ;; 6) bot_remove ;; esac ;; *) case "$ch" in 1) bot_install ;; esac ;; esac } bot_install() { log_step "Установка Telegram-бота" # Python if ! command -v python3 &>/dev/null; then log_info "Установка Python3..." if command -v apt-get &>/dev/null; then apt-get update -qq && apt-get install -y -qq python3 python3-pip python3-venv elif command -v dnf &>/dev/null; then dnf install -y -q python3 python3-pip elif command -v yum &>/dev/null; then yum install -y -q python3 python3-pip fi fi # Копируем файлы бота mkdir -p "$BOT_DIR" if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/" cp "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$BOT_DIR/" [ -f "$SCRIPT_DIR/gotelegram-bot/config.example.env" ] && \ cp "$SCRIPT_DIR/gotelegram-bot/config.example.env" "$BOT_DIR/" else log_error "Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/" return 1 fi # Каталог шаблонов [ -f "$SCRIPT_DIR/templates_catalog.json" ] && \ cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/" # Venv if [ ! -d "$BOT_DIR/venv" ]; then log_info "Создание виртуального окружения..." python3 -m venv "$BOT_DIR/venv" fi log_info "Установка зависимостей..." "$BOT_DIR/venv/bin/pip" install -r "$BOT_DIR/requirements.txt" -q # Конфигурация if [ ! -f "$BOT_DIR/.env" ]; then echo "" echo -e " ${YELLOW}Введите BOT_TOKEN от @BotFather:${NC}" local token="" while [ -z "$token" ]; do echo -ne " ${WHITE}Token:${NC} " read -r token token=$(echo "$token" | tr -d '[:space:]') [ -z "$token" ] && log_error "Токен не может быть пустым" done echo -ne " ${WHITE}ID администратора (Enter = доступ для всех):${NC} " read -r admin_id { echo "BOT_TOKEN=$token" [ -n "$admin_id" ] && echo "ALLOWED_IDS=$admin_id" } > "$BOT_DIR/.env" chmod 600 "$BOT_DIR/.env" log_success ".env создан" else log_info ".env уже существует, настройки сохранены" fi # Systemd cat > "/etc/systemd/system/${BOT_SERVICE}.service" << SVCEOF [Unit] Description=GoTelegram v${GOTELEGRAM_VERSION} Telegram Bot After=network.target [Service] Type=simple WorkingDirectory=$BOT_DIR ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py Restart=always RestartSec=5 Environment=PATH=$BOT_DIR/venv/bin:/usr/bin [Install] WantedBy=multi-user.target SVCEOF systemctl daemon-reload systemctl enable "$BOT_SERVICE" &>/dev/null systemctl restart "$BOT_SERVICE" 2>/dev/null || systemctl start "$BOT_SERVICE" echo "" log_success "Бот установлен и запущен!" echo -e " ${DIM}Проверка: systemctl status $BOT_SERVICE${NC}" echo -e " ${DIM}Логи: journalctl -u $BOT_SERVICE -f${NC}" } bot_show_status() { echo "" echo -e " ${BOLD}${WHITE}📊 Статус Telegram-бота${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" systemctl status "$BOT_SERVICE" --no-pager -l 2>/dev/null | head -15 | while IFS= read -r line; do echo " $line" done echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" if [ -f "$BOT_DIR/.env" ]; then local has_token has_ids has_token=$(grep -c "BOT_TOKEN=" "$BOT_DIR/.env" 2>/dev/null || echo 0) has_ids=$(grep "ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null | cut -d= -f2) echo -e " Token: ${has_token:+${GREEN}✓ настроен${NC}}" echo -e " Доступ: ${has_ids:+ID: $has_ids}${has_ids:-${YELLOW}все пользователи${NC}}" fi } bot_show_logs() { echo "" echo -e " ${BOLD}${WHITE}📋 Логи бота (последние 30 строк):${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" journalctl -u "$BOT_SERVICE" --no-pager -n 30 2>/dev/null | while IFS= read -r line; do echo " $line" done echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" } bot_edit_config() { echo "" echo -e " ${BOLD}${WHITE}⚙️ Настройки бота${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" if [ -f "$BOT_DIR/.env" ]; then echo -e " ${DIM}Текущий .env:${NC}" while IFS= read -r line; do # Маскируем токен для безопасности if [[ "$line" == BOT_TOKEN=* ]]; then local tok="${line#BOT_TOKEN=}" echo -e " BOT_TOKEN=${tok:0:10}...${tok: -5}" else echo " $line" fi done < "$BOT_DIR/.env" fi echo "" echo -e " ${CYAN}1${NC}) Сменить BOT_TOKEN" echo -e " ${CYAN}2${NC}) Изменить ALLOWED_IDS" echo -e " ${CYAN}0${NC}) Назад" echo -ne " ${WHITE}Выбор:${NC} " read -r ch case "$ch" in 1) echo -ne " ${WHITE}Новый BOT_TOKEN:${NC} " read -r new_token new_token=$(echo "$new_token" | tr -d '[:space:]') if [ -n "$new_token" ]; then sed -i "s|^BOT_TOKEN=.*|BOT_TOKEN=$new_token|" "$BOT_DIR/.env" systemctl restart "$BOT_SERVICE" log_success "Токен обновлён, бот перезапущен" else log_error "Пустой токен" fi ;; 2) echo -ne " ${WHITE}ALLOWED_IDS (через запятую, пусто = все):${NC} " read -r new_ids new_ids=$(echo "$new_ids" | tr -d '[:space:]') if grep -q "^ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null; then if [ -n "$new_ids" ]; then sed -i "s|^ALLOWED_IDS=.*|ALLOWED_IDS=$new_ids|" "$BOT_DIR/.env" else sed -i '/^ALLOWED_IDS=/d' "$BOT_DIR/.env" fi else [ -n "$new_ids" ] && echo "ALLOWED_IDS=$new_ids" >> "$BOT_DIR/.env" fi systemctl restart "$BOT_SERVICE" log_success "Доступ обновлён, бот перезапущен" ;; esac } bot_remove() { echo "" log_warning "Это удалит Telegram-бота и все его настройки." if ! confirm "Удалить бота?"; then return fi systemctl stop "$BOT_SERVICE" 2>/dev/null systemctl disable "$BOT_SERVICE" 2>/dev/null rm -f "/etc/systemd/system/${BOT_SERVICE}.service" systemctl daemon-reload rm -rf "$BOT_DIR" log_success "Бот полностью удалён" } # ── Промо ──────────────────────────────────────────────────────────────────── menu_promo() { echo "" echo -e " ${YELLOW}╔══════════════════════════════════════════════════════╗${NC}" echo -e " ${YELLOW}║${NC} ${BOLD}💰 ХОСТИНГ #1 — СКИДКА ДО 60%${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} Ссылка: ${CYAN}https://vk.cc/ct29NQ${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${WHITE}OFF60${NC} — 60% скидки на первый месяц ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${WHITE}antenka20${NC} — 20% + 3% при оплате за 3 месяца ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${WHITE}antenka6${NC} — 15% + 5% при оплате за 6 месяцев ${YELLOW}║${NC}" echo -e " ${YELLOW}╟──────────────────────────────────────────────────────╢${NC}" echo -e " ${YELLOW}║${NC} ${BOLD}💰 ХОСТИНГ #2 — СКИДКА ДО 60%${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} Ссылка: ${CYAN}https://vk.cc/cUxAhj${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${WHITE}OFF60${NC} — 60% скидки на первый месяц ${YELLOW}║${NC}" echo -e " ${YELLOW}╟──────────────────────────────────────────────────────╢${NC}" echo -e " ${YELLOW}║${NC} ${BOLD}☕ Донат / Чаевые${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${CYAN}https://pay.cloudtips.ru/p/7410814f${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}╚══════════════════════════════════════════════════════╝${NC}" echo "" } # ── Проверка: показывать ли промо (раз в сутки) ──────────────────────────── should_show_promo() { local stamp_file="$GOTELEGRAM_DIR/.promo_last_shown" if [ ! -f "$stamp_file" ]; then return 0 # никогда не показывали fi local last_shown now diff last_shown=$(cat "$stamp_file" 2>/dev/null || echo "0") last_shown="${last_shown//[^0-9]/}" last_shown="${last_shown:-0}" now=$(date +%s) diff=$(( now - last_shown )) # 86400 = 24 часа [ "$diff" -ge 86400 ] } mark_promo_shown() { mkdir -p "$GOTELEGRAM_DIR" date +%s > "$GOTELEGRAM_DIR/.promo_last_shown" } # ── Промо с QR и задержкой (при установке + раз в сутки) ─────────────────── show_promo_with_qr() { echo "" echo -e " ${YELLOW}╔══════════════════════════════════════════════════════╗${NC}" echo -e " ${YELLOW}║${NC} ${BOLD}💰 ХОСТИНГ #1 — СКИДКА ДО 60%${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} Ссылка: ${CYAN}https://vk.cc/ct29NQ${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${WHITE}OFF60${NC} — 60% скидки на первый месяц ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${WHITE}antenka20${NC} — 20% + 3% при оплате за 3 месяца ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${WHITE}antenka6${NC} — 15% + 5% при оплате за 6 месяцев ${YELLOW}║${NC}" echo -e " ${YELLOW}╟──────────────────────────────────────────────────────╢${NC}" echo -e " ${YELLOW}║${NC} ${BOLD}💰 ХОСТИНГ #2 — СКИДКА ДО 60%${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} Ссылка: ${CYAN}https://vk.cc/cUxAhj${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}" echo -e " ${YELLOW}║${NC} ${WHITE}OFF60${NC} — 60% скидки на первый месяц ${YELLOW}║${NC}" echo -e " ${YELLOW}╚══════════════════════════════════════════════════════╝${NC}" echo "" # QR-коды if command -v qrencode &>/dev/null; then echo -e " ${DIM}── QR: Хостинг #1 ──${NC}" qrencode -t UTF8 -m 1 "https://vk.cc/ct29NQ" 2>/dev/null | while IFS= read -r qr_line; do echo " $qr_line" done echo "" echo -e " ${DIM}── QR: Хостинг #2 ──${NC}" qrencode -t UTF8 -m 1 "https://vk.cc/cUxAhj" 2>/dev/null | while IFS= read -r qr_line; do echo " $qr_line" done echo "" echo -e " ${DIM}── QR: Чаевые / Донат ──${NC}" qrencode -t UTF8 -m 1 "https://pay.cloudtips.ru/p/7410814f" 2>/dev/null | while IFS= read -r qr_line; do echo " $qr_line" done fi mark_promo_shown # 5-секундная задержка с обратным отсчётом for i in 5 4 3 2 1; do echo -ne "\r ${DIM}Меню через ${i} сек...${NC} " sleep 1 done echo -ne "\r \r" } # ── Точка входа ────────────────────────────────────────────────────────────── main() { check_root init_dirs show_banner # Pre-flight check_os check_disk_space 500 # Промо раз в сутки if should_show_promo; then show_promo_with_qr fi while true; do clear show_main_menu # Auto-refresh: 30 sec timeout if read -t 30 -r choice; then case "$choice" in 1) submenu_proxy ;; 2) submenu_stats ;; 3) submenu_manage ;; 4) menu_bot ;; 5) submenu_about ;; 0|q|exit) echo ""; log_info "До встречи! 👋"; exit 0 ;; *) log_error "Неверный выбор" ;; esac # Пауза после подменю (кроме статистики — у неё свой цикл) if [ "$choice" != "2" ]; then echo "" echo -ne " ${DIM}Нажмите Enter для возврата в меню...${NC}" read -r fi fi # If read timed out, loop refreshes the dashboard done } # ── Статистика (авто-обновление 1 сек, без мерцания) ─────────────────────── submenu_stats() { # Инициализируем статистику при первом входе if type stats_init &>/dev/null; then stats_init 2>/dev/null fi local line2; line2=$(printf '─%.0s' {1..54}) local first_draw=1 # Скрываем курсор для плавного обновления tput civis 2>/dev/null # Восстанавливаем курсор при выходе из функции trap 'tput cnorm 2>/dev/null; trap - RETURN' RETURN while true; do if [ "$first_draw" -eq 1 ]; then clear first_draw=0 else # Перемещаем курсор в начало экрана вместо clear — нет мерцания tput cup 0 0 2>/dev/null || printf '\033[H' fi # Рисуем весь экран поверх старого содержимого echo -e "\033[J" # очищаем от курсора до конца (убирает хвосты) echo -e " ${BOLD}${WHITE}📊 Статистика трафика${NC}" echo -e " ${DIM}${line2}${NC}" if type show_traffic_stats &>/dev/null; then show_traffic_stats else echo -e " ${DIM}Модуль статистики не загружен.${NC}" echo -e " ${DIM}Файл lib/stats.sh не найден.${NC}" echo "" fi echo -e " ${DIM}${line2}${NC}" local stats_on="вкл" if type toggle_stats &>/dev/null; then local cfg_val cfg_val=$(config_get stats_enabled 2>/dev/null || echo "true") [ "$cfg_val" = "false" ] && stats_on="выкл" fi echo -e " ${CYAN}1${NC}) Вкл/Выкл подсчёт (сейчас: ${stats_on})" echo -e " ${CYAN}2${NC}) Установить/обновить сборщик статистики" echo -e " ${CYAN}0${NC}) ${DIM}← Назад${NC}" echo -e " ${DIM}${line2}${NC}" echo -e " ${DIM}Обновление каждые 3 сек${NC}" # Показываем курсор для ввода, потом снова скрываем tput cnorm 2>/dev/null echo -ne " ${WHITE}▸ ${NC}" if read -t 3 -r ch; then tput civis 2>/dev/null case "$ch" in 1) if type toggle_stats &>/dev/null; then toggle_stats echo -ne " ${DIM}Нажмите Enter...${NC}"; read -r first_draw=1 # полная перерисовка после действия fi ;; 2) if type install_stats_collector &>/dev/null; then install_stats_collector echo -ne " ${DIM}Нажмите Enter...${NC}"; read -r first_draw=1 fi ;; 0|"") return ;; esac fi tput civis 2>/dev/null done } main "$@"