mirror of
https://github.com/anten-ka/gotelegram_pro.git
synced 2026-05-19 21:56:05 +00:00
- All frame boxes: correct 54-char width with emoji compensation - Lite mode links now include ee-prefix + mask_host hex (fake-TLS) - Added qrencode to ensure_deps() so QR codes work after fresh install - Centralized link generation in generate_proxy_link() - Fixed bot.py get_proxy_link() for lite mode ee-prefix
1213 lines
51 KiB
Bash
Executable File
1213 lines
51 KiB
Bash
Executable File
#!/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 ──
|
||
local mask_host
|
||
mask_host=$(config_get mask_host 2>/dev/null || echo "")
|
||
if [ -n "$secret" ] && [ "$proxy_status" = "running" ]; then
|
||
if [ "$mode" = "pro" ] && [ -n "$domain" ]; then
|
||
link=$(generate_proxy_link "$domain" "$port" "$secret" "$domain")
|
||
else
|
||
link=$(generate_proxy_link "$ip" "$port" "$secret" "$mask_host")
|
||
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 mask_host
|
||
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 "")
|
||
mask_host=$(config_get mask_host 2>/dev/null || echo "")
|
||
|
||
if [ "$mode" = "pro" ] && [ -n "$domain" ]; then
|
||
link=$(generate_proxy_link "$domain" "$port" "$secret" "$domain")
|
||
else
|
||
link=$(generate_proxy_link "$ip" "$port" "$secret" "$mask_host")
|
||
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 mask_host 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 "")
|
||
mask_host=$(config_get mask_host 2>/dev/null || echo "")
|
||
|
||
if [ "$mode" = "pro" ] && [ -n "$domain" ]; then
|
||
link=$(generate_proxy_link "$domain" "$port" "$secret" "$domain")
|
||
server_display="$domain"
|
||
else
|
||
link=$(generate_proxy_link "$ip" "$port" "$secret" "$mask_host")
|
||
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 ""
|
||
echo -e " ${WHITE}Как добавить администратора?${NC}"
|
||
echo -e " ${CYAN}1${NC}) Автоматически — бот определит ID при первом /start"
|
||
echo -e " ${CYAN}2${NC}) Вручную — ввести ID сейчас"
|
||
echo -ne " ${WHITE}Выбор [1]:${NC} "
|
||
read -r admin_mode
|
||
admin_mode="${admin_mode:-1}"
|
||
|
||
local admin_ids=""
|
||
if [ "$admin_mode" = "2" ]; then
|
||
echo -ne " ${WHITE}ID администраторов (через пробел/запятую):${NC} "
|
||
read -r admin_ids
|
||
admin_ids=$(echo "$admin_ids" | tr ' ' ',' | sed 's/,,*/,/g; s/^,//; s/,$//')
|
||
fi
|
||
|
||
{
|
||
echo "BOT_TOKEN=$token"
|
||
[ -n "$admin_ids" ] && echo "ALLOWED_IDS=$admin_ids"
|
||
} > "$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"
|
||
|
||
# Если авто-режим — ждём пока бот словит первого админа
|
||
local has_ids
|
||
has_ids=$(grep "^ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null | cut -d= -f2)
|
||
if [ -z "$has_ids" ]; then
|
||
echo ""
|
||
echo -e " ${YELLOW}╔══════════════════════════════════════════════════════╗${NC}"
|
||
echo -e " ${YELLOW}║${NC} ${BOLD}Ожидание администратора${NC} ${YELLOW}║${NC}"
|
||
echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}"
|
||
echo -e " ${YELLOW}║${NC} Откройте бота в Telegram и отправьте ${CYAN}/start${NC} ${YELLOW}║${NC}"
|
||
echo -e " ${YELLOW}║${NC} Бот автоматически назначит вас администратором ${YELLOW}║${NC}"
|
||
echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}"
|
||
echo -e " ${YELLOW}║${NC} ${DIM}Нажмите Ctrl+C чтобы пропустить${NC} ${YELLOW}║${NC}"
|
||
echo -e " ${YELLOW}╚══════════════════════════════════════════════════════╝${NC}"
|
||
echo ""
|
||
|
||
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
||
local i=0
|
||
local waited=0
|
||
local max_wait=300 # 5 минут максимум
|
||
|
||
# Ловим Ctrl+C чтобы выйти из ожидания без убийства скрипта
|
||
local interrupted=0
|
||
trap 'interrupted=1' INT
|
||
|
||
while [ $waited -lt $max_wait ] && [ $interrupted -eq 0 ]; do
|
||
printf "\r ${CYAN}${frames[$i]}${NC} Ожидание... напишите /start боту (%d сек) " "$waited" >&2
|
||
i=$(( (i+1) % ${#frames[@]} ))
|
||
sleep 1
|
||
waited=$((waited + 1))
|
||
|
||
# Проверяем появился ли ALLOWED_IDS
|
||
has_ids=$(grep "^ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null | cut -d= -f2)
|
||
if [ -n "$has_ids" ]; then
|
||
break
|
||
fi
|
||
done
|
||
|
||
trap - INT
|
||
printf "\r\033[K" >&2 # очистить строку со спиннером
|
||
|
||
if [ -n "$has_ids" ]; then
|
||
echo ""
|
||
log_success "Администратор назначен!"
|
||
echo -e " ${WHITE}ID:${NC} ${GREEN}${has_ids}${NC}"
|
||
elif [ $interrupted -eq 1 ]; then
|
||
echo ""
|
||
log_warning "Пропущено. Добавить админа позже: меню → Telegram-бот → Настройки"
|
||
else
|
||
echo ""
|
||
log_warning "Таймаут (5 мин). Добавить админа: меню → Telegram-бот → Настройки"
|
||
fi
|
||
fi
|
||
|
||
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 ' ' ',' | sed 's/,,*/,/g; s/^,//; s/,$//')
|
||
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 "$@"
|