mirror of
https://github.com/anten-ka/gotelegram_pro.git
synced 2026-05-19 20:56:14 +00:00
v2.3.0: Lite/Pro rebrand, submenu system, traffic stats, bot stats
This commit is contained in:
63
bootstrap.sh
63
bootstrap.sh
@@ -1,63 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# GoTelegram v2.2.1 — Bootstrap installer (private repo)
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
TOKEN="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"
|
|
||||||
REPO="anten-ka/gotelegram_pro"
|
|
||||||
BRANCH="test"
|
|
||||||
API="https://api.github.com/repos/$REPO"
|
|
||||||
INSTALL_DIR="/opt/gotelegram"
|
|
||||||
|
|
||||||
echo -e "\033[1;36m"
|
|
||||||
echo "╔══════════════════════════════════════════╗"
|
|
||||||
echo "║ GoTelegram v2.2.1 — Установка ║"
|
|
||||||
echo "╚══════════════════════════════════════════╝"
|
|
||||||
echo -e "\033[0m"
|
|
||||||
|
|
||||||
# Функция загрузки файла из приватного репо
|
|
||||||
dl() {
|
|
||||||
local path="$1" dest="$2"
|
|
||||||
mkdir -p "$(dirname "$dest")"
|
|
||||||
curl -sfL -H "Authorization: token $TOKEN" \
|
|
||||||
-H "Accept: application/vnd.github.v3.raw" \
|
|
||||||
"$API/contents/$path?ref=$BRANCH" -o "$dest"
|
|
||||||
sed -i 's/\r$//' "$dest"
|
|
||||||
echo " ✓ $path"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "📦 Скачиваю файлы..."
|
|
||||||
mkdir -p "$INSTALL_DIR/lib" "$INSTALL_DIR/gotelegram-bot"
|
|
||||||
|
|
||||||
# Основные файлы
|
|
||||||
dl "install.sh" "$INSTALL_DIR/install.sh"
|
|
||||||
dl "install_gotelegram_bot.sh" "$INSTALL_DIR/install_gotelegram_bot.sh"
|
|
||||||
dl "templates_catalog.json" "$INSTALL_DIR/templates_catalog.json"
|
|
||||||
|
|
||||||
# Библиотеки
|
|
||||||
dl "lib/common.sh" "$INSTALL_DIR/lib/common.sh"
|
|
||||||
dl "lib/telemt.sh" "$INSTALL_DIR/lib/telemt.sh"
|
|
||||||
dl "lib/telemt_config.sh" "$INSTALL_DIR/lib/telemt_config.sh"
|
|
||||||
dl "lib/backup.sh" "$INSTALL_DIR/lib/backup.sh"
|
|
||||||
dl "lib/website.sh" "$INSTALL_DIR/lib/website.sh"
|
|
||||||
dl "lib/templates_catalog.sh" "$INSTALL_DIR/lib/templates_catalog.sh"
|
|
||||||
|
|
||||||
# Бот
|
|
||||||
dl "gotelegram-bot/bot.py" "$INSTALL_DIR/gotelegram-bot/bot.py"
|
|
||||||
dl "gotelegram-bot/config.example.env" "$INSTALL_DIR/gotelegram-bot/config.example.env"
|
|
||||||
dl "gotelegram-bot/requirements.txt" "$INSTALL_DIR/gotelegram-bot/requirements.txt"
|
|
||||||
dl "gotelegram-bot/README.md" "$INSTALL_DIR/gotelegram-bot/README.md"
|
|
||||||
|
|
||||||
# Права
|
|
||||||
chmod +x "$INSTALL_DIR/install.sh" "$INSTALL_DIR/install_gotelegram_bot.sh"
|
|
||||||
|
|
||||||
# Команда gotelegram
|
|
||||||
ln -sf "$INSTALL_DIR/install.sh" /usr/local/bin/gotelegram
|
|
||||||
chmod +x /usr/local/bin/gotelegram
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "\033[1;32m✅ Все 13 файлов скачаны в $INSTALL_DIR\033[0m"
|
|
||||||
echo -e "\033[1;33m💡 Команда для запуска меню: gotelegram\033[0m"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Запускаем меню
|
|
||||||
exec bash "$INSTALL_DIR/install.sh"
|
|
||||||
@@ -6,14 +6,17 @@ Uses python-telegram-bot v21+
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import csv
|
||||||
import html
|
import html
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
import toml
|
import toml
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from io import StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Tuple, Optional, List, Dict, Any
|
from typing import Tuple, Optional, List, Dict, Any
|
||||||
|
|
||||||
@@ -47,7 +50,7 @@ logger = logging.getLogger(__name__)
|
|||||||
# CONFIGURATION
|
# CONFIGURATION
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
GOTELEGRAM_VERSION = "2.2.0"
|
GOTELEGRAM_VERSION = "2.3.0"
|
||||||
GOTELEGRAM_CONFIG = "/opt/gotelegram/config.json"
|
GOTELEGRAM_CONFIG = "/opt/gotelegram/config.json"
|
||||||
TELEMT_CONFIG = "/etc/telemt/config.toml"
|
TELEMT_CONFIG = "/etc/telemt/config.toml"
|
||||||
TELEMT_SERVICE = "telemt"
|
TELEMT_SERVICE = "telemt"
|
||||||
@@ -69,7 +72,7 @@ for _id_str in ALLOWED_IDS_STR.split(","):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
logging.warning(f"Invalid ALLOWED_IDS entry: {_id_str}")
|
logging.warning(f"Invalid ALLOWED_IDS entry: {_id_str}")
|
||||||
|
|
||||||
QUICK_DOMAINS = [
|
LITE_DOMAINS = [
|
||||||
"google.com",
|
"google.com",
|
||||||
"microsoft.com",
|
"microsoft.com",
|
||||||
"cloudflare.com",
|
"cloudflare.com",
|
||||||
@@ -277,10 +280,13 @@ def get_main_menu() -> InlineKeyboardMarkup:
|
|||||||
InlineKeyboardButton("🎁 Promo", callback_data="menu_promo"),
|
InlineKeyboardButton("🎁 Promo", callback_data="menu_promo"),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
InlineKeyboardButton("📊 Traffic Stats", callback_data="menu_stats"),
|
||||||
InlineKeyboardButton("🗑️ Remove", callback_data="menu_remove"),
|
InlineKeyboardButton("🗑️ Remove", callback_data="menu_remove"),
|
||||||
InlineKeyboardButton("ℹ️ Credits", callback_data="menu_credits"),
|
|
||||||
],
|
],
|
||||||
[InlineKeyboardButton("❌ Close", callback_data="close_menu")],
|
[
|
||||||
|
InlineKeyboardButton("ℹ️ Credits", callback_data="menu_credits"),
|
||||||
|
InlineKeyboardButton("❌ Close", callback_data="close_menu"),
|
||||||
|
],
|
||||||
]
|
]
|
||||||
return InlineKeyboardMarkup(buttons)
|
return InlineKeyboardMarkup(buttons)
|
||||||
|
|
||||||
@@ -403,19 +409,135 @@ async def get_status_text() -> str:
|
|||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
async def cb_menu_status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def get_traffic_stats() -> str:
|
||||||
"""Status callback."""
|
"""Get formatted traffic statistics."""
|
||||||
|
# Read current snapshot
|
||||||
|
current_file = "/run/gotelegram/stats_current.json"
|
||||||
|
history_file = "/opt/gotelegram/stats_history.csv"
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(current_file, "r") as f:
|
||||||
|
current = json.load(f)
|
||||||
|
except Exception:
|
||||||
|
return "📊 <b>Статистика</b>\n\n<i>Данные недоступны. Убедитесь что модуль статистики включён.</i>"
|
||||||
|
|
||||||
|
# Read history
|
||||||
|
history = []
|
||||||
|
try:
|
||||||
|
with open(history_file, "r") as f:
|
||||||
|
reader = csv.reader(f)
|
||||||
|
for row in reader:
|
||||||
|
if len(row) >= 3:
|
||||||
|
history.append({
|
||||||
|
"ts": int(row[0]),
|
||||||
|
"proxy": int(row[1]),
|
||||||
|
"site": int(row[2]),
|
||||||
|
})
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
now = int(time.time())
|
||||||
|
|
||||||
|
def format_bytes(b):
|
||||||
|
if b < 1024:
|
||||||
|
return f"{b} B"
|
||||||
|
if b < 1048576:
|
||||||
|
return f"{b/1024:.1f} KB"
|
||||||
|
if b < 1073741824:
|
||||||
|
return f"{b/1048576:.1f} MB"
|
||||||
|
return f"{b/1073741824:.1f} GB"
|
||||||
|
|
||||||
|
def format_rate(bps):
|
||||||
|
if bps < 1024:
|
||||||
|
return f"{bps:.0f} B/s"
|
||||||
|
if bps < 1048576:
|
||||||
|
return f"{bps/1024:.1f} KB/s"
|
||||||
|
return f"{bps/1048576:.1f} MB/s"
|
||||||
|
|
||||||
|
def calc_for_period(secs, key):
|
||||||
|
target_ts = now - secs
|
||||||
|
# Find closest snapshot to target_ts
|
||||||
|
closest = None
|
||||||
|
for h in history:
|
||||||
|
if h["ts"] <= target_ts:
|
||||||
|
if closest is None or h["ts"] > closest["ts"]:
|
||||||
|
closest = h
|
||||||
|
if closest is None:
|
||||||
|
return "—", "—"
|
||||||
|
|
||||||
|
current_val = current.get(f"{key}_bytes", 0)
|
||||||
|
diff = current_val - closest[key]
|
||||||
|
if diff < 0:
|
||||||
|
diff = 0
|
||||||
|
elapsed = now - closest["ts"]
|
||||||
|
if elapsed <= 0:
|
||||||
|
elapsed = 1
|
||||||
|
rate = diff / elapsed
|
||||||
|
return format_bytes(diff), format_rate(rate)
|
||||||
|
|
||||||
|
periods = [
|
||||||
|
("1 мин", 60),
|
||||||
|
("5 мин", 300),
|
||||||
|
("60 мин", 3600),
|
||||||
|
("1 день", 86400),
|
||||||
|
("7 дней", 604800),
|
||||||
|
("30 дней", 2592000),
|
||||||
|
("365 дней", 31536000),
|
||||||
|
]
|
||||||
|
|
||||||
|
lines = ["📊 <b>Статистика трафика</b>\n"]
|
||||||
|
|
||||||
|
for label, key in [("Proxy (telemt)", "proxy"), ("Сайт (nginx)", "site")]:
|
||||||
|
lines.append(f"\n<b>{label}:</b>")
|
||||||
|
lines.append("<pre>")
|
||||||
|
lines.append(f"{'Период':<10} │ {'Трафик':>10} │ {'Скорость':>10}")
|
||||||
|
lines.append("─" * 36)
|
||||||
|
for name, secs in periods:
|
||||||
|
total, rate = calc_for_period(secs, key)
|
||||||
|
lines.append(f"{name:<10} │ {total:>10} │ {rate:>10}")
|
||||||
|
lines.append("</pre>")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
async def cb_menu_stats(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
"""Show traffic statistics."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
await query.answer()
|
await query.answer()
|
||||||
|
|
||||||
await safe_edit_message(query,"⏳ Checking status...")
|
stats_text = await get_traffic_stats()
|
||||||
|
|
||||||
status_text = await get_status_text()
|
keyboard = [
|
||||||
keyboard = InlineKeyboardMarkup(
|
[InlineKeyboardButton("🔄 Обновить", callback_data="menu_stats")],
|
||||||
[[InlineKeyboardButton("« Back", callback_data="menu_main")]]
|
[InlineKeyboardButton("« Меню", callback_data="menu_main")],
|
||||||
|
]
|
||||||
|
|
||||||
|
await safe_edit_message(
|
||||||
|
query,
|
||||||
|
stats_text,
|
||||||
|
reply_markup=InlineKeyboardMarkup(keyboard),
|
||||||
|
parse_mode="HTML",
|
||||||
)
|
)
|
||||||
await safe_edit_message(query,
|
|
||||||
status_text, reply_markup=keyboard, parse_mode="HTML"
|
|
||||||
|
async def cb_menu_status(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
|
"""Status callback — show detailed proxy/server status."""
|
||||||
|
query = update.callback_query
|
||||||
|
await query.answer()
|
||||||
|
|
||||||
|
if not await require_auth(update, context):
|
||||||
|
return
|
||||||
|
|
||||||
|
text = await get_status_text()
|
||||||
|
keyboard = [
|
||||||
|
[InlineKeyboardButton("🔄 Обновить", callback_data="menu_status")],
|
||||||
|
[InlineKeyboardButton("« Меню", callback_data="menu_main")],
|
||||||
|
]
|
||||||
|
await safe_edit_message(
|
||||||
|
query,
|
||||||
|
text,
|
||||||
|
reply_markup=InlineKeyboardMarkup(keyboard),
|
||||||
|
parse_mode="HTML",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -427,8 +549,8 @@ async def cb_menu_status(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
def get_install_mode_menu() -> InlineKeyboardMarkup:
|
def get_install_mode_menu() -> InlineKeyboardMarkup:
|
||||||
"""Install mode selection menu."""
|
"""Install mode selection menu."""
|
||||||
buttons = [
|
buttons = [
|
||||||
[InlineKeyboardButton("⚡ Quick Mode", callback_data="install_mode_quick")],
|
[InlineKeyboardButton("⚡ Lite", callback_data="install_mode_lite")],
|
||||||
[InlineKeyboardButton("🔒 Stealth Mode", callback_data="install_mode_stealth")],
|
[InlineKeyboardButton("🛡 Pro", callback_data="install_mode_pro")],
|
||||||
[InlineKeyboardButton("« Back", callback_data="menu_main")],
|
[InlineKeyboardButton("« Back", callback_data="menu_main")],
|
||||||
]
|
]
|
||||||
return InlineKeyboardMarkup(buttons)
|
return InlineKeyboardMarkup(buttons)
|
||||||
@@ -452,7 +574,7 @@ async def cb_menu_install(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
)
|
)
|
||||||
buttons = [
|
buttons = [
|
||||||
[InlineKeyboardButton("🔄 Migrate from v1", callback_data="install_migrate")],
|
[InlineKeyboardButton("🔄 Migrate from v1", callback_data="install_migrate")],
|
||||||
[InlineKeyboardButton("✨ Fresh Install", callback_data="install_mode_quick")],
|
[InlineKeyboardButton("✨ Fresh Install", callback_data="install_mode_lite")],
|
||||||
[InlineKeyboardButton("« Back", callback_data="menu_main")],
|
[InlineKeyboardButton("« Back", callback_data="menu_main")],
|
||||||
]
|
]
|
||||||
keyboard = InlineKeyboardMarkup(buttons)
|
keyboard = InlineKeyboardMarkup(buttons)
|
||||||
@@ -465,39 +587,39 @@ async def cb_menu_install(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def cb_install_mode_quick(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_install_mode_lite(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
"""Quick mode domain selection."""
|
"""Lite mode domain selection."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
await query.answer()
|
await query.answer()
|
||||||
|
|
||||||
# Show domains with pagination (4 per row, 2 rows)
|
# Show domains with pagination (4 per row, 2 rows)
|
||||||
buttons = []
|
buttons = []
|
||||||
for i in range(0, len(QUICK_DOMAINS), 2):
|
for i in range(0, len(LITE_DOMAINS), 2):
|
||||||
row = []
|
row = []
|
||||||
for j in range(2):
|
for j in range(2):
|
||||||
if i + j < len(QUICK_DOMAINS):
|
if i + j < len(LITE_DOMAINS):
|
||||||
domain = QUICK_DOMAINS[i + j]
|
domain = LITE_DOMAINS[i + j]
|
||||||
row.append(
|
row.append(
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
domain, callback_data=f"quick_dom_{i+j}"
|
domain, callback_data=f"lite_dom_{i+j}"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
buttons.append(row)
|
buttons.append(row)
|
||||||
|
|
||||||
buttons.append([InlineKeyboardButton("« Back", callback_data="menu_install")])
|
buttons.append([InlineKeyboardButton("« Back", callback_data="menu_install")])
|
||||||
|
|
||||||
text = "Select a domain for quick mode:"
|
text = "Select a domain for Lite mode:"
|
||||||
keyboard = InlineKeyboardMarkup(buttons)
|
keyboard = InlineKeyboardMarkup(buttons)
|
||||||
await safe_edit_message(query,text, reply_markup=keyboard)
|
await safe_edit_message(query,text, reply_markup=keyboard)
|
||||||
|
|
||||||
|
|
||||||
async def cb_quick_domain(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_lite_domain(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
"""Quick domain selection callback."""
|
"""Lite domain selection callback."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
data = query.data
|
data = query.data
|
||||||
try:
|
try:
|
||||||
domain_idx = int(data.split("_")[-1])
|
domain_idx = int(data.split("_")[-1])
|
||||||
domain = QUICK_DOMAINS[domain_idx]
|
domain = LITE_DOMAINS[domain_idx]
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
await query.answer("Invalid domain selection")
|
await query.answer("Invalid domain selection")
|
||||||
return
|
return
|
||||||
@@ -507,7 +629,7 @@ async def cb_quick_domain(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
|
|
||||||
# Simulate installation (in real scenario, call install script)
|
# Simulate installation (in real scenario, call install script)
|
||||||
config = {
|
config = {
|
||||||
"mode": "quick",
|
"mode": "lite",
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
"port": 443,
|
"port": 443,
|
||||||
"installed_at": datetime.now().isoformat(),
|
"installed_at": datetime.now().isoformat(),
|
||||||
@@ -515,9 +637,9 @@ async def cb_quick_domain(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
|
|
||||||
if save_json(GOTELEGRAM_CONFIG, config):
|
if save_json(GOTELEGRAM_CONFIG, config):
|
||||||
text = (
|
text = (
|
||||||
f"✅ <b>Quick mode installed!</b>\n\n"
|
f"✅ <b>Lite mode installed!</b>\n\n"
|
||||||
f"<b>Domain:</b> {domain}\n"
|
f"<b>Domain:</b> {domain}\n"
|
||||||
f"<b>Mode:</b> Quick\n\n"
|
f"<b>Mode:</b> Lite\n\n"
|
||||||
f"Service starting... Check status in 10 seconds."
|
f"Service starting... Check status in 10 seconds."
|
||||||
)
|
)
|
||||||
keyboard = InlineKeyboardMarkup(
|
keyboard = InlineKeyboardMarkup(
|
||||||
@@ -535,8 +657,8 @@ async def cb_quick_domain(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def cb_install_mode_stealth(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_install_mode_pro(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
"""Stealth mode - show template categories."""
|
"""Pro mode - show template categories."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
await query.answer()
|
await query.answer()
|
||||||
|
|
||||||
@@ -555,22 +677,22 @@ async def cb_install_mode_stealth(update: Update, context: ContextTypes.DEFAULT_
|
|||||||
buttons.append(
|
buttons.append(
|
||||||
[
|
[
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
f"📁 {cat['name']}", callback_data=f"stealth_cat_{cat['id']}"
|
f"📁 {cat['name']}", callback_data=f"pro_cat_{cat['id']}"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
buttons.append([InlineKeyboardButton("« Back", callback_data="menu_install")])
|
buttons.append([InlineKeyboardButton("« Back", callback_data="menu_install")])
|
||||||
|
|
||||||
text = "Stealth Mode - Select Template Category:"
|
text = "Pro Mode - Select Template Category:"
|
||||||
keyboard = InlineKeyboardMarkup(buttons)
|
keyboard = InlineKeyboardMarkup(buttons)
|
||||||
await safe_edit_message(query,text, reply_markup=keyboard)
|
await safe_edit_message(query,text, reply_markup=keyboard)
|
||||||
|
|
||||||
|
|
||||||
async def cb_stealth_category(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_pro_category(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
"""Show templates in category."""
|
"""Show templates in category."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
data = query.data
|
data = query.data
|
||||||
cat_id = data.removeprefix("stealth_cat_")
|
cat_id = data.removeprefix("pro_cat_")
|
||||||
|
|
||||||
await query.answer()
|
await query.answer()
|
||||||
|
|
||||||
@@ -597,22 +719,22 @@ async def cb_stealth_category(update: Update, context: ContextTypes.DEFAULT_TYPE
|
|||||||
buttons.append(
|
buttons.append(
|
||||||
[
|
[
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
f"🎨 {tpl['name']}", callback_data=f"stealth_tpl_{tpl['id']}"
|
f"🎨 {tpl['name']}", callback_data=f"pro_tpl_{tpl['id']}"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
buttons.append([InlineKeyboardButton("« Back", callback_data="install_mode_stealth")])
|
buttons.append([InlineKeyboardButton("« Back", callback_data="install_mode_pro")])
|
||||||
|
|
||||||
text = f"Select template from <b>{html.escape(category['name'])}</b>:"
|
text = f"Select template from <b>{html.escape(category['name'])}</b>:"
|
||||||
keyboard = InlineKeyboardMarkup(buttons)
|
keyboard = InlineKeyboardMarkup(buttons)
|
||||||
await safe_edit_message(query,text, reply_markup=keyboard, parse_mode="HTML")
|
await safe_edit_message(query,text, reply_markup=keyboard, parse_mode="HTML")
|
||||||
|
|
||||||
|
|
||||||
async def cb_stealth_template(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_pro_template(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
"""Show template preview and confirm."""
|
"""Show template preview and confirm."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
data = query.data
|
data = query.data
|
||||||
tpl_id = data.removeprefix("stealth_tpl_")
|
tpl_id = data.removeprefix("pro_tpl_")
|
||||||
|
|
||||||
await query.answer()
|
await query.answer()
|
||||||
|
|
||||||
@@ -651,26 +773,26 @@ async def cb_stealth_template(update: Update, context: ContextTypes.DEFAULT_TYPE
|
|||||||
buttons = [
|
buttons = [
|
||||||
[
|
[
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
"✅ Install", callback_data=f"stealth_confirm_{tpl_id}"
|
"✅ Install", callback_data=f"pro_confirm_{tpl_id}"
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
[InlineKeyboardButton("« Back", callback_data="install_mode_stealth")],
|
[InlineKeyboardButton("« Back", callback_data="install_mode_pro")],
|
||||||
]
|
]
|
||||||
keyboard = InlineKeyboardMarkup(buttons)
|
keyboard = InlineKeyboardMarkup(buttons)
|
||||||
await safe_edit_message(query,text, reply_markup=keyboard, parse_mode="HTML")
|
await safe_edit_message(query,text, reply_markup=keyboard, parse_mode="HTML")
|
||||||
|
|
||||||
|
|
||||||
async def cb_stealth_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_pro_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
"""Confirm and install stealth template."""
|
"""Confirm and install pro template."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
data = query.data
|
data = query.data
|
||||||
tpl_id = data.removeprefix("stealth_confirm_")
|
tpl_id = data.removeprefix("pro_confirm_")
|
||||||
|
|
||||||
await query.answer()
|
await query.answer()
|
||||||
await safe_edit_message(query,"⏳ Installing template...")
|
await safe_edit_message(query,"⏳ Installing template...")
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
"mode": "stealth",
|
"mode": "pro",
|
||||||
"template": tpl_id,
|
"template": tpl_id,
|
||||||
"port": 443,
|
"port": 443,
|
||||||
"installed_at": datetime.now().isoformat(),
|
"installed_at": datetime.now().isoformat(),
|
||||||
@@ -678,9 +800,9 @@ async def cb_stealth_confirm(update: Update, context: ContextTypes.DEFAULT_TYPE)
|
|||||||
|
|
||||||
if save_json(GOTELEGRAM_CONFIG, config):
|
if save_json(GOTELEGRAM_CONFIG, config):
|
||||||
text = (
|
text = (
|
||||||
f"✅ <b>Stealth mode installed!</b>\n\n"
|
f"✅ <b>Pro mode installed!</b>\n\n"
|
||||||
f"<b>Template:</b> {html.escape(tpl_id)}\n"
|
f"<b>Template:</b> {html.escape(tpl_id)}\n"
|
||||||
f"<b>Mode:</b> Stealth\n\n"
|
f"<b>Mode:</b> Pro\n\n"
|
||||||
f"Service starting... Check status in 10 seconds."
|
f"Service starting... Check status in 10 seconds."
|
||||||
)
|
)
|
||||||
keyboard = InlineKeyboardMarkup(
|
keyboard = InlineKeyboardMarkup(
|
||||||
@@ -1074,8 +1196,8 @@ async def cb_menu_change(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
await query.answer()
|
await query.answer()
|
||||||
|
|
||||||
buttons = [
|
buttons = [
|
||||||
[InlineKeyboardButton("⚡ Switch to Quick Mode", callback_data="change_quick")],
|
[InlineKeyboardButton("⚡ Switch to Lite Mode", callback_data="change_lite")],
|
||||||
[InlineKeyboardButton("🔒 Switch to Stealth Mode", callback_data="change_stealth")],
|
[InlineKeyboardButton("🛡 Switch to Pro Mode", callback_data="change_pro")],
|
||||||
[InlineKeyboardButton("« Back", callback_data="menu_main")],
|
[InlineKeyboardButton("« Back", callback_data="menu_main")],
|
||||||
]
|
]
|
||||||
keyboard = InlineKeyboardMarkup(buttons)
|
keyboard = InlineKeyboardMarkup(buttons)
|
||||||
@@ -1084,20 +1206,20 @@ async def cb_menu_change(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def cb_change_quick(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_change_lite(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
"""Switch to quick mode — show domain selection."""
|
"""Switch to lite mode — show domain selection."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
await query.answer()
|
await query.answer()
|
||||||
# Reuse the quick mode domain selection flow
|
# Reuse the lite mode domain selection flow
|
||||||
await cb_install_mode_quick(update, context)
|
await cb_install_mode_lite(update, context)
|
||||||
|
|
||||||
|
|
||||||
async def cb_change_stealth(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_change_pro(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
"""Switch to stealth mode — show template categories."""
|
"""Switch to pro mode — show template categories."""
|
||||||
query = update.callback_query
|
query = update.callback_query
|
||||||
await query.answer()
|
await query.answer()
|
||||||
# Reuse the stealth mode template selection flow
|
# Reuse the pro mode template selection flow
|
||||||
await cb_install_mode_stealth(update, context)
|
await cb_install_mode_pro(update, context)
|
||||||
|
|
||||||
|
|
||||||
async def cb_install_migrate(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
async def cb_install_migrate(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
|
||||||
@@ -1328,27 +1450,28 @@ async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE) ->
|
|||||||
"menu_promo": cb_menu_promo,
|
"menu_promo": cb_menu_promo,
|
||||||
"menu_credits": cb_menu_credits,
|
"menu_credits": cb_menu_credits,
|
||||||
"menu_remove": cb_menu_remove,
|
"menu_remove": cb_menu_remove,
|
||||||
"install_mode_quick": cb_install_mode_quick,
|
"install_mode_lite": cb_install_mode_lite,
|
||||||
"install_mode_stealth": cb_install_mode_stealth,
|
"install_mode_pro": cb_install_mode_pro,
|
||||||
"backup_create": cb_backup_create,
|
"backup_create": cb_backup_create,
|
||||||
"backup_list": cb_backup_list,
|
"backup_list": cb_backup_list,
|
||||||
"ssl_renew": cb_ssl_renew,
|
"ssl_renew": cb_ssl_renew,
|
||||||
"ssl_status": cb_ssl_status,
|
"ssl_status": cb_ssl_status,
|
||||||
"remove_confirm": cb_remove_confirm,
|
"remove_confirm": cb_remove_confirm,
|
||||||
"change_quick": cb_change_quick,
|
"change_lite": cb_change_lite,
|
||||||
"change_stealth": cb_change_stealth,
|
"change_pro": cb_change_pro,
|
||||||
"install_migrate": cb_install_migrate,
|
"install_migrate": cb_install_migrate,
|
||||||
|
"menu_stats": cb_menu_stats,
|
||||||
}
|
}
|
||||||
|
|
||||||
# Pattern-based handlers
|
# Pattern-based handlers
|
||||||
if data.startswith("quick_dom_"):
|
if data.startswith("lite_dom_"):
|
||||||
await cb_quick_domain(update, context)
|
await cb_lite_domain(update, context)
|
||||||
elif data.startswith("stealth_cat_"):
|
elif data.startswith("pro_cat_"):
|
||||||
await cb_stealth_category(update, context)
|
await cb_pro_category(update, context)
|
||||||
elif data.startswith("stealth_tpl_"):
|
elif data.startswith("pro_tpl_"):
|
||||||
await cb_stealth_template(update, context)
|
await cb_pro_template(update, context)
|
||||||
elif data.startswith("stealth_confirm_"):
|
elif data.startswith("pro_confirm_"):
|
||||||
await cb_stealth_confirm(update, context)
|
await cb_pro_confirm(update, context)
|
||||||
elif data.startswith("restore_idx_"):
|
elif data.startswith("restore_idx_"):
|
||||||
await cb_restore_backup(update, context)
|
await cb_restore_backup(update, context)
|
||||||
elif data in handlers:
|
elif data in handlers:
|
||||||
|
|||||||
347
install.sh
347
install.sh
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# ══════════════════════════════════════════════════════════════════════════════
|
# ══════════════════════════════════════════════════════════════════════════════
|
||||||
# GoTelegram v2.2.1 — MTProxy на ядре telemt (Rust + Tokio)
|
# GoTelegram v2.3.0 — MTProxy на ядре telemt (Rust + Tokio)
|
||||||
# Anti-DPI • Fake TLS • TCP Splice • JA3/JA4 Resistance
|
# Anti-DPI • Fake TLS • TCP Splice • JA3/JA4 Resistance
|
||||||
#
|
#
|
||||||
# Установка:
|
# Установка:
|
||||||
@@ -20,8 +20,9 @@ source "$LIB_DIR/telemt_config.sh"
|
|||||||
source "$LIB_DIR/website.sh"
|
source "$LIB_DIR/website.sh"
|
||||||
source "$LIB_DIR/templates_catalog.sh"
|
source "$LIB_DIR/templates_catalog.sh"
|
||||||
source "$LIB_DIR/backup.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() {
|
show_main_menu() {
|
||||||
local proxy_status bot_status nginx_st mode domain secret port ip link ssl_expiry
|
local proxy_status bot_status nginx_st mode domain secret port ip link ssl_expiry
|
||||||
proxy_status=$(telemt_status)
|
proxy_status=$(telemt_status)
|
||||||
@@ -33,14 +34,14 @@ show_main_menu() {
|
|||||||
port=$(get_config_value port 2>/dev/null || echo "443")
|
port=$(get_config_value port 2>/dev/null || echo "443")
|
||||||
ip=$(get_server_ip 2>/dev/null || echo "N/A")
|
ip=$(get_server_ip 2>/dev/null || echo "N/A")
|
||||||
|
|
||||||
local W=60
|
local W=54
|
||||||
local line; line=$(printf '━%.0s' $(seq 1 $W))
|
local line; line=$(printf '━%.0s' $(seq 1 $W))
|
||||||
local line2; line2=$(printf '─%.0s' $(seq 1 $W))
|
local line2; line2=$(printf '─%.0s' $(seq 1 $W))
|
||||||
|
|
||||||
# ── Заголовок ──
|
# ── Заголовок ──
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${CYAN}┏${line}┓${NC}"
|
echo -e " ${BOLD}${CYAN}┏${line}┓${NC}"
|
||||||
echo -e " ${BOLD}${CYAN}┃${NC} ${BOLD}${WHITE}GoTelegram v${GOTELEGRAM_VERSION}${NC} — Панель мониторинга ${BOLD}${CYAN}┃${NC}"
|
echo -e " ${BOLD}${CYAN}┃${NC} ${BOLD}${WHITE}GoTelegram v${GOTELEGRAM_VERSION}${NC} Панель управления ${BOLD}${CYAN}┃${NC}"
|
||||||
echo -e " ${BOLD}${CYAN}┗${line}┛${NC}"
|
echo -e " ${BOLD}${CYAN}┗${line}┛${NC}"
|
||||||
|
|
||||||
# ── Здоровье сервисов ──
|
# ── Здоровье сервисов ──
|
||||||
@@ -62,10 +63,10 @@ show_main_menu() {
|
|||||||
running) nginx_icon="●"; nginx_color="${GREEN}" ;;
|
running) nginx_icon="●"; nginx_color="${GREEN}" ;;
|
||||||
*) nginx_icon="✗"; nginx_color="${RED}" ;;
|
*) nginx_icon="✗"; nginx_color="${RED}" ;;
|
||||||
esac
|
esac
|
||||||
echo -e " ${nginx_color}${nginx_icon}${NC} nginx ${nginx_color}${nginx_st}${NC} ${DIM}(127.0.0.1:8443)${NC}"
|
echo -e " ${nginx_icon}${nginx_color}${NC} nginx ${nginx_color}${nginx_st}${NC} ${DIM}(127.0.0.1:8443)${NC}"
|
||||||
|
|
||||||
# Site (stealth)
|
# Site (pro)
|
||||||
if [ "$mode" = "stealth" ] && [ -n "$domain" ]; then
|
if [ "$mode" = "pro" ] && [ -n "$domain" ]; then
|
||||||
local site_icon site_color
|
local site_icon site_color
|
||||||
if curl -sk --max-time 3 "https://${domain}/" -o /dev/null 2>/dev/null; then
|
if curl -sk --max-time 3 "https://${domain}/" -o /dev/null 2>/dev/null; then
|
||||||
site_icon="●"; site_color="${GREEN}"
|
site_icon="●"; site_color="${GREEN}"
|
||||||
@@ -96,7 +97,7 @@ show_main_menu() {
|
|||||||
|
|
||||||
# ── Прокси-ссылка + QR ──
|
# ── Прокси-ссылка + QR ──
|
||||||
if [ -n "$secret" ] && [ "$proxy_status" = "running" ]; then
|
if [ -n "$secret" ] && [ "$proxy_status" = "running" ]; then
|
||||||
if [ "$mode" = "stealth" ] && [ -n "$domain" ]; then
|
if [ "$mode" = "pro" ] && [ -n "$domain" ]; then
|
||||||
local raw_secret faketls_secret domain_hex
|
local raw_secret faketls_secret domain_hex
|
||||||
raw_secret="$secret"
|
raw_secret="$secret"
|
||||||
domain_hex=$(printf '%s' "$domain" | xxd -p | tr -d '\n')
|
domain_hex=$(printf '%s' "$domain" | xxd -p | tr -d '\n')
|
||||||
@@ -117,27 +118,129 @@ show_main_menu() {
|
|||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo -e " ${DIM}Прокси не настроен. Выберите п.1 для установки.${NC}"
|
echo -e " ${DIM}Прокси не настроен. Выберите пункт 1.${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Меню ──
|
# ── Меню ──
|
||||||
echo -e " ${DIM}${line2}${NC}"
|
echo -e " ${DIM}${line2}${NC}"
|
||||||
echo -e " ${DIM}ПРОКСИ${NC} ${DIM}УПРАВЛЕНИЕ${NC}"
|
echo -e " ${CYAN}1${NC}) Прокси ▸"
|
||||||
echo -e " ${CYAN}1${NC}) Установить / Обновить ${CYAN}8${NC}) Бекап"
|
echo -e " ${CYAN}2${NC}) Статистика ▸"
|
||||||
echo -e " ${CYAN}2${NC}) Статус подробно ${CYAN}9${NC}) Восстановить"
|
echo -e " ${CYAN}3${NC}) Управление ▸"
|
||||||
echo -e " ${CYAN}3${NC}) Скопировать ссылку ${CYAN}10${NC}) Обновить telemt"
|
echo -e " ${CYAN}4${NC}) Telegram-бот ▸"
|
||||||
echo -e " ${CYAN}4${NC}) Поделиться ключом ${CYAN}11${NC}) Сайт / SSL"
|
echo -e " ${CYAN}5${NC}) О программе ▸"
|
||||||
echo -e " ${CYAN}5${NC}) Перезапуск"
|
echo -e " ${CYAN}0${NC}) ${DIM}Выход${NC}"
|
||||||
echo -e " ${CYAN}6${NC}) Логи ${DIM}БОТ И ПРОЧЕЕ${NC}"
|
|
||||||
echo -e " ${CYAN}7${NC}) Сменить режим / шаблон ${CYAN}12${NC}) Telegram-бот"
|
|
||||||
echo -e " ${CYAN}13${NC}) Удалить всё"
|
|
||||||
echo -e " ${CYAN}0${NC}) ${DIM}Выход${NC} ${CYAN}14${NC}) Промо"
|
|
||||||
echo -e " ${DIM}${line2}${NC}"
|
echo -e " ${DIM}${line2}${NC}"
|
||||||
echo -e " ${DIM}Обновление через 30 сек${NC}"
|
echo -e " ${DIM}Обновление через 1 сек${NC}"
|
||||||
echo -ne " ${WHITE}▸ ${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() {
|
menu_install() {
|
||||||
# Проверяем v1
|
# Проверяем v1
|
||||||
@@ -154,29 +257,29 @@ menu_install() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}🎭 Выберите режим маскировки:${NC}"
|
echo -e " ${BOLD}${WHITE}🎭 Выберите режим маскировки:${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
echo -e " ${CYAN}1)${NC} ${GREEN}⚡ Quick${NC} — маскировка под популярный сайт"
|
echo -e " ${CYAN}1)${NC} ${GREEN}⚡ Lite${NC} — маскировка под популярный сайт"
|
||||||
echo -e " ${DIM}Быстро, без домена. telemt маскирует трафик${NC}"
|
echo -e " ${DIM}Быстро, без домена. telemt маскирует трафик${NC}"
|
||||||
echo -e " ${DIM}под выбранный сайт (google.com и т.д.)${NC}"
|
echo -e " ${DIM}под выбранный сайт (google.com и т.д.)${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${CYAN}2)${NC} ${MAGENTA}🛡 Stealth${NC} — свой сайт + полная маскировка"
|
echo -e " ${CYAN}2)${NC} ${MAGENTA}🛡 Pro${NC} — свой сайт + полная маскировка"
|
||||||
echo -e " ${DIM}nginx + SSL + HTML-шаблон + telemt.${NC}"
|
echo -e " ${DIM}nginx + SSL + HTML-шаблон + telemt.${NC}"
|
||||||
echo -e " ${DIM}DPI видит реальный сайт с реальным сертификатом.${NC}"
|
echo -e " ${DIM}DPI видит реальный сайт с реальным сертификатом.${NC}"
|
||||||
echo -e " ${DIM}Требует: домен, направленный на этот сервер.${NC}"
|
echo -e " ${DIM}Требует: домен, направленный на этот сервер.{{NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
echo -ne " ${WHITE}Выбор (1/2):${NC} "
|
echo -ne " ${WHITE}Выбор (1/2):${NC} "
|
||||||
read -r mode_choice
|
read -r mode_choice
|
||||||
mode_choice="${mode_choice:-}"
|
mode_choice="${mode_choice:-}"
|
||||||
|
|
||||||
case "$mode_choice" in
|
case "$mode_choice" in
|
||||||
1) install_quick_mode ;;
|
1) install_lite_mode ;;
|
||||||
2) install_stealth_mode ;;
|
2) install_pro_mode ;;
|
||||||
*) log_error "Неверный выбор: ${mode_choice:-<пусто>}" ;;
|
*) log_error "Неверный выбор: ${mode_choice:-<пусто>}" ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Quick-режим ──────────────────────────────────────────────────────────────
|
# ── Lite-режим ───────────────────────────────────────────────────────────────
|
||||||
install_quick_mode() {
|
install_lite_mode() {
|
||||||
log_step "Установка Quick-режима"
|
log_step "Установка Lite-режима"
|
||||||
|
|
||||||
# Выбор домена
|
# Выбор домена
|
||||||
local domain
|
local domain
|
||||||
@@ -200,7 +303,7 @@ install_quick_mode() {
|
|||||||
echo -e " IP: ${CYAN}${ip}${NC}"
|
echo -e " IP: ${CYAN}${ip}${NC}"
|
||||||
echo -e " Порт: ${CYAN}${port}${NC}"
|
echo -e " Порт: ${CYAN}${port}${NC}"
|
||||||
echo -e " Маскировка: ${CYAN}${domain}${NC}"
|
echo -e " Маскировка: ${CYAN}${domain}${NC}"
|
||||||
echo -e " Режим: ${GREEN}Quick${NC}"
|
echo -e " Режим: ${GREEN}Lite${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if ! confirm "Установить прокси?"; then
|
if ! confirm "Установить прокси?"; then
|
||||||
@@ -212,7 +315,7 @@ install_quick_mode() {
|
|||||||
install_telemt_full || return
|
install_telemt_full || return
|
||||||
|
|
||||||
# Генерируем конфиг telemt
|
# Генерируем конфиг telemt
|
||||||
generate_telemt_toml "$secret" "$port" "quick" "$domain" "443"
|
generate_telemt_toml "$secret" "$port" "lite" "$domain" "443"
|
||||||
|
|
||||||
# Валидация
|
# Валидация
|
||||||
validate_telemt_config || return
|
validate_telemt_config || return
|
||||||
@@ -221,19 +324,19 @@ install_quick_mode() {
|
|||||||
start_telemt || return
|
start_telemt || return
|
||||||
|
|
||||||
# Сохраняем GoTelegram конфиг
|
# Сохраняем GoTelegram конфиг
|
||||||
save_gotelegram_config "telemt" "quick" "$port" "$secret" "$domain" "" ""
|
save_gotelegram_config "telemt" "lite" "$port" "$secret" "$domain" "" ""
|
||||||
|
|
||||||
# Благодарности
|
# Благодарности
|
||||||
show_credits
|
show_credits
|
||||||
|
|
||||||
# Результат
|
# Результат
|
||||||
show_proxy_info
|
show_proxy_info
|
||||||
log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Quick-режим)"
|
log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Lite-режим)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Stealth-режим ────────────────────────────────────────────────────────────
|
# ── Pro-режим ────────────────────────────────────────────────────────────────
|
||||||
install_stealth_mode() {
|
install_pro_mode() {
|
||||||
log_step "Установка Stealth-режима"
|
log_step "Установка Pro-режима"
|
||||||
|
|
||||||
# Ввод домена
|
# Ввод домена
|
||||||
echo ""
|
echo ""
|
||||||
@@ -266,7 +369,7 @@ install_stealth_mode() {
|
|||||||
template_dir=$(interactive_template_selection)
|
template_dir=$(interactive_template_selection)
|
||||||
[ $? -ne 0 ] && return
|
[ $? -ne 0 ] && return
|
||||||
|
|
||||||
# Архитектура Stealth:
|
# Архитектура Pro:
|
||||||
# telemt слушает на 0.0.0.0:443 (принимает ВСЕ подключения)
|
# telemt слушает на 0.0.0.0:443 (принимает ВСЕ подключения)
|
||||||
# nginx слушает на 127.0.0.1:8443 с SSL (обслуживает сайт)
|
# nginx слушает на 127.0.0.1:8443 с SSL (обслуживает сайт)
|
||||||
# MTProxy клиент → :443 → telemt (проксирует)
|
# MTProxy клиент → :443 → telemt (проксирует)
|
||||||
@@ -291,7 +394,7 @@ install_stealth_mode() {
|
|||||||
echo -e " ${BOLD}${WHITE}📋 Конфигурация:${NC}"
|
echo -e " ${BOLD}${WHITE}📋 Конфигурация:${NC}"
|
||||||
echo -e " Домен: ${CYAN}${user_domain}${NC}"
|
echo -e " Домен: ${CYAN}${user_domain}${NC}"
|
||||||
echo -e " Порт: ${CYAN}443 (telemt + nginx внутри)${NC}"
|
echo -e " Порт: ${CYAN}443 (telemt + nginx внутри)${NC}"
|
||||||
echo -e " Режим: ${MAGENTA}Stealth (fake-TLS)${NC}"
|
echo -e " Режим: ${MAGENTA}Pro (fake-TLS)${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if ! confirm "Установить прокси + сайт?"; then
|
if ! confirm "Установить прокси + сайт?"; then
|
||||||
@@ -303,10 +406,10 @@ install_stealth_mode() {
|
|||||||
install_telemt_full || return
|
install_telemt_full || return
|
||||||
|
|
||||||
# Конфиг telemt: слушает 443, маскировка на локальный nginx через dns_override
|
# Конфиг telemt: слушает 443, маскировка на локальный nginx через dns_override
|
||||||
generate_telemt_toml "$raw_secret" "443" "stealth" "$user_domain" "$nginx_internal_port"
|
generate_telemt_toml "$raw_secret" "443" "pro" "$user_domain" "$nginx_internal_port"
|
||||||
|
|
||||||
# Настройка сайта (nginx на внутреннем порту + certbot + шаблон)
|
# Настройка сайта (nginx на внутреннем порту + certbot + шаблон)
|
||||||
setup_stealth_mode "$user_domain" "$template_dir" "$nginx_internal_port" "$ssl_email" || return
|
setup_pro_mode "$user_domain" "$template_dir" "$nginx_internal_port" "$ssl_email" || return
|
||||||
|
|
||||||
# Останавливаем nginx на 443 перед запуском telemt (telemt займёт 443)
|
# Останавливаем nginx на 443 перед запуском telemt (telemt займёт 443)
|
||||||
# nginx уже перенастроен на внутренний порт
|
# nginx уже перенастроен на внутренний порт
|
||||||
@@ -318,22 +421,22 @@ install_stealth_mode() {
|
|||||||
# Сохраняем конфиг
|
# Сохраняем конфиг
|
||||||
local tpl_id
|
local tpl_id
|
||||||
tpl_id=$(basename "$template_dir")
|
tpl_id=$(basename "$template_dir")
|
||||||
save_gotelegram_config "telemt" "stealth" "443" "$raw_secret" "$user_domain" "$user_domain" "$tpl_id"
|
save_gotelegram_config "telemt" "pro" "443" "$raw_secret" "$user_domain" "$user_domain" "$tpl_id"
|
||||||
|
|
||||||
# Результат — используем домен и fake-TLS ссылку
|
# Результат — используем домен и fake-TLS ссылку
|
||||||
show_proxy_info_stealth "$user_domain" "$faketls_secret"
|
show_proxy_info_pro "$user_domain" "$faketls_secret"
|
||||||
echo -e " ${WHITE}Сайт:${NC} ${GREEN}https://${user_domain}${NC}"
|
echo -e " ${WHITE}Сайт:${NC} ${GREEN}https://${user_domain}${NC}"
|
||||||
log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Stealth-режим)"
|
log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Pro-режим)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Статус ───────────────────────────────────────────────────────────────────
|
# ── Статус ───────────────────────────────────────────────────────────────────
|
||||||
menu_status() {
|
menu_status() {
|
||||||
show_proxy_info
|
show_proxy_info
|
||||||
|
|
||||||
# Дополнительно для stealth
|
# Дополнительно для pro
|
||||||
local mode
|
local mode
|
||||||
mode=$(config_get mode 2>/dev/null)
|
mode=$(config_get mode 2>/dev/null)
|
||||||
if [ "$mode" = "stealth" ]; then
|
if [ "$mode" = "pro" ]; then
|
||||||
local domain
|
local domain
|
||||||
domain=$(config_get domain 2>/dev/null)
|
domain=$(config_get domain 2>/dev/null)
|
||||||
if [ -n "$domain" ]; then
|
if [ -n "$domain" ]; then
|
||||||
@@ -396,7 +499,7 @@ menu_restart() {
|
|||||||
restart_telemt
|
restart_telemt
|
||||||
local mode
|
local mode
|
||||||
mode=$(config_get mode 2>/dev/null)
|
mode=$(config_get mode 2>/dev/null)
|
||||||
if [ "$mode" = "stealth" ]; then
|
if [ "$mode" = "pro" ]; then
|
||||||
restart_nginx
|
restart_nginx
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@@ -417,16 +520,16 @@ menu_change_mode() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e " ${WHITE}Текущий режим:${NC} ${CYAN}${current_mode}${NC}"
|
echo -e " ${WHITE}Текущий режим:${NC} ${CYAN}${current_mode}${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${CYAN}1)${NC} Сменить шаблон сайта (только stealth)"
|
echo -e " ${CYAN}1${NC}) Сменить шаблон сайта (только pro)"
|
||||||
echo -e " ${CYAN}2)${NC} Переключить режим (quick ↔ stealth)"
|
echo -e " ${CYAN}2${NC}) Переключить режим (lite ↔ pro)"
|
||||||
echo -e " ${CYAN}0)${NC} Назад"
|
echo -e " ${CYAN}0${NC}) Назад"
|
||||||
echo -ne " ${WHITE}Выбор:${NC} "
|
echo -ne " ${WHITE}Выбор:${NC} "
|
||||||
read -r ch
|
read -r ch
|
||||||
|
|
||||||
case "$ch" in
|
case "$ch" in
|
||||||
1)
|
1)
|
||||||
if [ "$current_mode" != "stealth" ]; then
|
if [ "$current_mode" != "pro" ]; then
|
||||||
log_error "Смена шаблона доступна только в stealth-режиме"
|
log_error "Смена шаблона доступна только в pro-режиме"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
local template_dir
|
local template_dir
|
||||||
@@ -443,12 +546,13 @@ menu_change_mode() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Управление сайтом ───────────────────────────────────────────────────────
|
# ── Управление сайтом ────────────────────────────────────────────────────────
|
||||||
menu_website() {
|
menu_website() {
|
||||||
local mode
|
local mode
|
||||||
mode=$(config_get mode 2>/dev/null)
|
mode=$(config_get mode 2>/dev/null)
|
||||||
if [ "$mode" != "stealth" ]; then
|
|
||||||
log_info "Управление сайтом доступно только в stealth-режиме"
|
if [ "$mode" != "pro" ]; then
|
||||||
|
log_info "Управление сайтом доступно только в pro-режиме"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -460,10 +564,10 @@ menu_website() {
|
|||||||
echo -e " Домен: ${CYAN}${domain}${NC}"
|
echo -e " Домен: ${CYAN}${domain}${NC}"
|
||||||
echo -e " SSL до: $(get_ssl_expiry "$domain")"
|
echo -e " SSL до: $(get_ssl_expiry "$domain")"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${CYAN}1)${NC} Обновить SSL сертификат"
|
echo -e " ${CYAN}1${NC}) Обновить SSL сертификат"
|
||||||
echo -e " ${CYAN}2)${NC} Перезапустить nginx"
|
echo -e " ${CYAN}2${NC}) Перезапустить nginx"
|
||||||
echo -e " ${CYAN}3)${NC} Сменить шаблон"
|
echo -e " ${CYAN}3${NC}) Сменить шаблон"
|
||||||
echo -e " ${CYAN}0)${NC} Назад"
|
echo -e " ${CYAN}0${NC}) Назад"
|
||||||
echo -ne " ${WHITE}Выбор:${NC} "
|
echo -ne " ${WHITE}Выбор:${NC} "
|
||||||
read -r ch
|
read -r ch
|
||||||
|
|
||||||
@@ -484,10 +588,10 @@ menu_remove() {
|
|||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${RED}🗑 Удаление GoTelegram${NC}"
|
echo -e " ${BOLD}${RED}🗑 Удаление GoTelegram${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
echo -e " ${CYAN}1)${NC} Удалить только прокси (telemt)"
|
echo -e " ${CYAN}1${NC}) Удалить только прокси (telemt)"
|
||||||
echo -e " ${CYAN}2)${NC} Удалить только Telegram-бота"
|
echo -e " ${CYAN}2${NC}) Удалить только Telegram-бота"
|
||||||
echo -e " ${CYAN}3)${NC} Удалить всё (прокси + бот + настройки)"
|
echo -e " ${CYAN}3${NC}) Удалить всё (прокси + бот + настройки)"
|
||||||
echo -e " ${CYAN}0)${NC} Назад"
|
echo -e " ${CYAN}0${NC}) Назад"
|
||||||
echo -ne " ${WHITE}Выбор:${NC} "
|
echo -ne " ${WHITE}Выбор:${NC} "
|
||||||
read -r rm_choice
|
read -r rm_choice
|
||||||
|
|
||||||
@@ -501,8 +605,8 @@ menu_remove() {
|
|||||||
remove_telemt
|
remove_telemt
|
||||||
local mode
|
local mode
|
||||||
mode=$(config_get mode 2>/dev/null)
|
mode=$(config_get mode 2>/dev/null)
|
||||||
if [ "$mode" = "stealth" ]; then
|
if [ "$mode" = "pro" ]; then
|
||||||
remove_stealth_mode
|
remove_pro_mode
|
||||||
fi
|
fi
|
||||||
rm -f "$GOTELEGRAM_CONFIG"
|
rm -f "$GOTELEGRAM_CONFIG"
|
||||||
log_success "Прокси удалён"
|
log_success "Прокси удалён"
|
||||||
@@ -520,8 +624,8 @@ menu_remove() {
|
|||||||
remove_telemt
|
remove_telemt
|
||||||
local mode
|
local mode
|
||||||
mode=$(config_get mode 2>/dev/null)
|
mode=$(config_get mode 2>/dev/null)
|
||||||
if [ "$mode" = "stealth" ]; then
|
if [ "$mode" = "pro" ]; then
|
||||||
remove_stealth_mode
|
remove_pro_mode
|
||||||
fi
|
fi
|
||||||
rm -f "$GOTELEGRAM_CONFIG"
|
rm -f "$GOTELEGRAM_CONFIG"
|
||||||
# Бот
|
# Бот
|
||||||
@@ -563,21 +667,21 @@ menu_bot() {
|
|||||||
running)
|
running)
|
||||||
echo -e " Статус: ${GREEN}● Работает${NC}"
|
echo -e " Статус: ${GREEN}● Работает${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${CYAN}1)${NC} 📊 Статус бота"
|
echo -e " ${CYAN}1${NC}) 📊 Статус бота"
|
||||||
echo -e " ${CYAN}2)${NC} 📋 Логи бота"
|
echo -e " ${CYAN}2${NC}) 📋 Логи бота"
|
||||||
echo -e " ${CYAN}3)${NC} 🔄 Перезапустить бота"
|
echo -e " ${CYAN}3${NC}) 🔄 Перезапустить бота"
|
||||||
echo -e " ${CYAN}4)${NC} ⏹ Остановить бота"
|
echo -e " ${CYAN}4${NC}) ⏹ Остановить бота"
|
||||||
echo -e " ${CYAN}5)${NC} ⚙️ Настройки (.env)"
|
echo -e " ${CYAN}5${NC}) ⚙️ Настройки (.env)"
|
||||||
echo -e " ${CYAN}6)${NC} 🗑 Удалить бота"
|
echo -e " ${CYAN}6${NC}) 🗑 Удалить бота"
|
||||||
;;
|
;;
|
||||||
stopped)
|
stopped)
|
||||||
echo -e " Статус: ${YELLOW}○ Остановлен${NC}"
|
echo -e " Статус: ${YELLOW}○ Остановлен${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${CYAN}1)${NC} 📊 Статус бота"
|
echo -e " ${CYAN}1${NC}) 📊 Статус бота"
|
||||||
echo -e " ${CYAN}2)${NC} 📋 Логи бота"
|
echo -e " ${CYAN}2${NC}) 📋 Логи бота"
|
||||||
echo -e " ${CYAN}3)${NC} ▶️ Запустить бота"
|
echo -e " ${CYAN}3${NC}) ▶️ Запустить бота"
|
||||||
echo -e " ${CYAN}5)${NC} ⚙️ Настройки (.env)"
|
echo -e " ${CYAN}5${NC}) ⚙️ Настройки (.env)"
|
||||||
echo -e " ${CYAN}6)${NC} 🗑 Удалить бота"
|
echo -e " ${CYAN}6${NC}) 🗑 Удалить бота"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo -e " Статус: ${RED}✗ Не установлен${NC}"
|
echo -e " Статус: ${RED}✗ Не установлен${NC}"
|
||||||
@@ -585,11 +689,11 @@ menu_bot() {
|
|||||||
echo -e " ${DIM}Бот позволяет управлять прокси прямо из Telegram:${NC}"
|
echo -e " ${DIM}Бот позволяет управлять прокси прямо из Telegram:${NC}"
|
||||||
echo -e " ${DIM}статус, перезапуск, смена режима, бекап, QR-код.${NC}"
|
echo -e " ${DIM}статус, перезапуск, смена режима, бекап, QR-код.${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${CYAN}1)${NC} 🔧 Установить бота"
|
echo -e " ${CYAN}1${NC}) 🔧 Установить бота"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo -e " ${CYAN}0)${NC} « Назад"
|
echo -e " ${CYAN}0${NC}) « Назад"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
echo -ne " ${WHITE}Выбор:${NC} "
|
echo -ne " ${WHITE}Выбор:${NC} "
|
||||||
read -r ch
|
read -r ch
|
||||||
@@ -762,9 +866,9 @@ bot_edit_config() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${CYAN}1)${NC} Сменить BOT_TOKEN"
|
echo -e " ${CYAN}1${NC}) Сменить BOT_TOKEN"
|
||||||
echo -e " ${CYAN}2)${NC} Изменить ALLOWED_IDS"
|
echo -e " ${CYAN}2${NC}) Изменить ALLOWED_IDS"
|
||||||
echo -e " ${CYAN}0)${NC} Назад"
|
echo -e " ${CYAN}0${NC}) Назад"
|
||||||
echo -ne " ${WHITE}Выбор:${NC} "
|
echo -ne " ${WHITE}Выбор:${NC} "
|
||||||
read -r ch
|
read -r ch
|
||||||
|
|
||||||
@@ -842,33 +946,74 @@ main() {
|
|||||||
while true; do
|
while true; do
|
||||||
clear
|
clear
|
||||||
show_main_menu
|
show_main_menu
|
||||||
# Auto-refresh: 30 sec timeout
|
# Auto-refresh: 1 sec timeout
|
||||||
if read -t 30 -r choice; then
|
if read -t 1 -r choice; then
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1) menu_install ;;
|
1) submenu_proxy ;;
|
||||||
2) menu_status ;;
|
2) submenu_stats ;;
|
||||||
3) menu_link ;;
|
3) submenu_manage ;;
|
||||||
4) menu_share ;;
|
4) menu_bot ;;
|
||||||
5) menu_restart ;;
|
5) submenu_about ;;
|
||||||
6) menu_logs ;;
|
|
||||||
7) menu_change_mode ;;
|
|
||||||
8) interactive_backup ;;
|
|
||||||
9) interactive_restore ;;
|
|
||||||
10) update_telemt ;;
|
|
||||||
11) menu_website ;;
|
|
||||||
12) menu_bot ;;
|
|
||||||
13) menu_remove ;;
|
|
||||||
14) menu_promo ;;
|
|
||||||
0|q|exit) echo ""; log_info "До встречи! 👋"; exit 0 ;;
|
0|q|exit) echo ""; log_info "До встречи! 👋"; exit 0 ;;
|
||||||
*) log_error "Неверный выбор" ;;
|
*) log_error "Неверный выбор" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -ne " ${DIM}Нажмите Enter для возврата в меню...${NC}"
|
|
||||||
read -r
|
|
||||||
fi
|
fi
|
||||||
# If read timed out, loop refreshes the dashboard
|
# If read timed out, loop refreshes the dashboard
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Статистика (авто-обновление 1 сек) ──────────────────────────────────────
|
||||||
|
submenu_stats() {
|
||||||
|
# Инициализируем статистику при первом входе
|
||||||
|
if type stats_init &>/dev/null; then
|
||||||
|
stats_init 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
clear
|
||||||
|
echo ""
|
||||||
|
echo -e " ${BOLD}${WHITE}📊 Статистика трафика${NC}"
|
||||||
|
echo -e " ${DIM}$(printf '─%.0s' {1..54})${NC}"
|
||||||
|
|
||||||
|
if type show_traffic_stats &>/dev/null; then
|
||||||
|
show_traffic_stats
|
||||||
|
else
|
||||||
|
echo -e " ${DIM}Модуль статистики не загружен.${NC}"
|
||||||
|
echo -e " ${DIM}Файл lib/stats.sh не найден.${NC}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e " ${DIM}$(printf '─%.0s' {1..54})${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}$(printf '─%.0s' {1..54})${NC}"
|
||||||
|
echo -e " ${DIM}Обновление через 1 сек${NC}"
|
||||||
|
echo -ne " ${WHITE}▸ ${NC}"
|
||||||
|
|
||||||
|
if read -t 1 -r ch; then
|
||||||
|
case "$ch" in
|
||||||
|
1)
|
||||||
|
if type toggle_stats &>/dev/null; then
|
||||||
|
toggle_stats
|
||||||
|
echo -ne " ${DIM}Нажмите Enter...${NC}"; read -r
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
if type install_stats_collector &>/dev/null; then
|
||||||
|
install_stats_collector
|
||||||
|
echo -ne " ${DIM}Нажмите Enter...${NC}"; read -r
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
0|"") return ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# GoTelegram v2.2.1 — Установка Telegram-бота
|
|
||||||
# Создаёт venv, ставит зависимости, настраивает systemd
|
|
||||||
|
|
||||||
set -e
|
|
||||||
RED='\033[0;31m'
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
CYAN='\033[0;36m'
|
|
||||||
NC='\033[0m'
|
|
||||||
|
|
||||||
BOT_DIR="/opt/gotelegram-bot"
|
|
||||||
SERVICE_NAME="gotelegram-bot"
|
|
||||||
GOTELEGRAM_DIR="/opt/gotelegram"
|
|
||||||
|
|
||||||
if [ "$EUID" -ne 0 ]; then
|
|
||||||
echo -e "${RED}Запустите с sudo.${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${CYAN}╔═══════════════════════════════════════════╗${NC}"
|
|
||||||
echo -e "${CYAN}║${NC} ${GREEN}GoTelegram v2.2.1 — Установка бота${NC} ${CYAN}║${NC}"
|
|
||||||
echo -e "${CYAN}╚═══════════════════════════════════════════╝${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# ── Python ───────────────────────────────────────────────────────────────────
|
|
||||||
if ! command -v python3 &>/dev/null; then
|
|
||||||
echo -e "${YELLOW}[*] Установка python3...${NC}"
|
|
||||||
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"
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then
|
|
||||||
echo -e "${GREEN}[*] Копирование файлов бота...${NC}"
|
|
||||||
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
|
|
||||||
echo -e "${RED}Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Копируем каталог шаблонов
|
|
||||||
if [ -f "$SCRIPT_DIR/templates_catalog.json" ]; then
|
|
||||||
mkdir -p "$GOTELEGRAM_DIR"
|
|
||||||
cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/"
|
|
||||||
echo -e "${GREEN}[*] Каталог шаблонов скопирован${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Virtual environment ──────────────────────────────────────────────────────
|
|
||||||
if [ ! -d "$BOT_DIR/venv" ]; then
|
|
||||||
echo -e "${GREEN}[*] Создание виртуального окружения...${NC}"
|
|
||||||
python3 -m venv "$BOT_DIR/venv"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "${GREEN}[*] Установка зависимостей...${NC}"
|
|
||||||
"$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}"
|
|
||||||
TOKEN=""
|
|
||||||
while [ -z "$TOKEN" ]; do
|
|
||||||
read -r TOKEN
|
|
||||||
TOKEN=$(echo "$TOKEN" | tr -d '[:space:]')
|
|
||||||
[ -z "$TOKEN" ] && echo -e "${RED}Токен не может быть пустым.${NC}"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo -ne "${YELLOW}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"
|
|
||||||
echo -e "${GREEN}[*] .env создан${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}[*] .env уже существует${NC}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Systemd ──────────────────────────────────────────────────────────────────
|
|
||||||
cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF
|
|
||||||
[Unit]
|
|
||||||
Description=GoTelegram v2.2.1 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
|
|
||||||
EOF
|
|
||||||
|
|
||||||
systemctl daemon-reload
|
|
||||||
systemctl enable "$SERVICE_NAME"
|
|
||||||
systemctl restart "$SERVICE_NAME" 2>/dev/null || systemctl start "$SERVICE_NAME"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${GREEN}╔═══════════════════════════════════════════╗${NC}"
|
|
||||||
echo -e "${GREEN}║ ✅ Бот установлен и запущен! ║${NC}"
|
|
||||||
echo -e "${GREEN}╚═══════════════════════════════════════════╝${NC}"
|
|
||||||
echo ""
|
|
||||||
echo -e "Проверка: ${CYAN}systemctl status $SERVICE_NAME${NC}"
|
|
||||||
echo -e "Логи: ${CYAN}journalctl -u $SERVICE_NAME -f${NC}"
|
|
||||||
echo -e "Настройки: ${CYAN}$BOT_DIR/.env${NC}"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Благодарности
|
|
||||||
echo -e "${CYAN}─────────────────────────────────────────────${NC}"
|
|
||||||
echo -e "💜 Спасибо авторам открытых проектов:"
|
|
||||||
echo -e " ${CYAN}telemt${NC} — MTProxy engine (Rust)"
|
|
||||||
echo -e " ${CYAN}HTML5 UP${NC} — шаблоны сайтов (CC BY 3.0)"
|
|
||||||
echo -e " ${CYAN}learning-zone${NC} — 150+ HTML5 шаблонов"
|
|
||||||
echo -e " ${CYAN}Start Bootstrap${NC} — Bootstrap шаблоны (MIT)"
|
|
||||||
echo -e "${CYAN}─────────────────────────────────────────────${NC}"
|
|
||||||
0
lib/backup.sh
Normal file → Executable file
0
lib/backup.sh
Normal file → Executable file
6
lib/common.sh
Normal file → Executable file
6
lib/common.sh
Normal file → Executable file
@@ -1,9 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# GoTelegram v2.2 — Общие утилиты
|
# GoTelegram v2.3 — Общие утилиты
|
||||||
# Цвета, логирование, спиннер, системные функции, совместимость с v1
|
# Цвета, логирование, спиннер, системные функции, совместимость с v1
|
||||||
|
|
||||||
# ── Версия ────────────────────────────────────────────────────────────────────
|
# ── Версия ────────────────────────────────────────────────────────────────────
|
||||||
GOTELEGRAM_VERSION="2.2.1"
|
GOTELEGRAM_VERSION="2.3.0"
|
||||||
GOTELEGRAM_NAME="GoTelegram"
|
GOTELEGRAM_NAME="GoTelegram"
|
||||||
|
|
||||||
# ── Пути ──────────────────────────────────────────────────────────────────────
|
# ── Пути ──────────────────────────────────────────────────────────────────────
|
||||||
@@ -270,7 +270,7 @@ save_gotelegram_config() {
|
|||||||
{
|
{
|
||||||
"version": "$GOTELEGRAM_VERSION",
|
"version": "$GOTELEGRAM_VERSION",
|
||||||
"engine": "${1:-telemt}",
|
"engine": "${1:-telemt}",
|
||||||
"mode": "${2:-quick}",
|
"mode": "${2:-lite}",
|
||||||
"port": ${3:-443},
|
"port": ${3:-443},
|
||||||
"secret": "${4:-}",
|
"secret": "${4:-}",
|
||||||
"mask_host": "${5:-google.com}",
|
"mask_host": "${5:-google.com}",
|
||||||
|
|||||||
424
lib/stats.sh
Executable file
424
lib/stats.sh
Executable file
@@ -0,0 +1,424 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# stats.sh — Traffic statistics module for GoTelegram
|
||||||
|
# Tracks proxy (telemt port 443) and site (nginx port 8443) traffic
|
||||||
|
# Uses iptables counters + real-time snapshots + historical CSV
|
||||||
|
|
||||||
|
# Color codes (from common.sh)
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
STATS_DIR="/run/gotelegram"
|
||||||
|
HISTORY_FILE="/opt/gotelegram/stats_history.csv"
|
||||||
|
SNAPSHOTS_DIR="$STATS_DIR/snapshots"
|
||||||
|
CURRENT_SNAPSHOT="$STATS_DIR/stats_current.json"
|
||||||
|
CONFIG_FILE="/opt/gotelegram/config.json"
|
||||||
|
|
||||||
|
# Initialize stats infrastructure
|
||||||
|
stats_init() {
|
||||||
|
# Create runtime directory
|
||||||
|
mkdir -p "$STATS_DIR" "$SNAPSHOTS_DIR" 2>/dev/null
|
||||||
|
chmod 755 "$STATS_DIR" "$SNAPSHOTS_DIR" 2>/dev/null
|
||||||
|
|
||||||
|
# Create iptables chain if not exists
|
||||||
|
if ! iptables -L GOTELEGRAM_STATS -n >/dev/null 2>&1; then
|
||||||
|
iptables -N GOTELEGRAM_STATS 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add chain to INPUT if not already present
|
||||||
|
if ! iptables -C INPUT -j GOTELEGRAM_STATS 2>/dev/null; then
|
||||||
|
iptables -I INPUT -j GOTELEGRAM_STATS 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add rule for proxy traffic (port 443, TCP)
|
||||||
|
if ! iptables -C GOTELEGRAM_STATS -p tcp --dport 443 2>/dev/null; then
|
||||||
|
iptables -A GOTELEGRAM_STATS -p tcp --dport 443 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add rule for site traffic (loopback, port 8443, TCP)
|
||||||
|
if ! iptables -C GOTELEGRAM_STATS -i lo -p tcp --dport 8443 2>/dev/null; then
|
||||||
|
iptables -A GOTELEGRAM_STATS -i lo -p tcp --dport 8443 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Initialize CSV header if file doesn't exist
|
||||||
|
if [[ ! -f "$HISTORY_FILE" ]]; then
|
||||||
|
echo "epoch,proxy_bytes,site_bytes" > "$HISTORY_FILE" 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Write initial snapshot
|
||||||
|
stats_collect
|
||||||
|
}
|
||||||
|
|
||||||
|
# Collect current traffic statistics from iptables
|
||||||
|
stats_collect() {
|
||||||
|
local proxy_bytes=0 proxy_pkts=0 site_bytes=0 site_pkts=0
|
||||||
|
local ts=$(date +%s)
|
||||||
|
local temp_file=$(mktemp)
|
||||||
|
|
||||||
|
# Parse iptables output: format is "pkts bytes target"
|
||||||
|
# We need to extract bytes (2nd column) for each rule
|
||||||
|
local iptables_output=$(iptables -L GOTELEGRAM_STATS -v -n -x 2>/dev/null)
|
||||||
|
|
||||||
|
# Extract counters for port 443 (proxy)
|
||||||
|
proxy_bytes=$(echo "$iptables_output" | grep "dpt:443" | grep -v "lo" | awk '{print $2}')
|
||||||
|
proxy_pkts=$(echo "$iptables_output" | grep "dpt:443" | grep -v "lo" | awk '{print $1}')
|
||||||
|
|
||||||
|
# Extract counters for port 8443 on loopback (site)
|
||||||
|
site_bytes=$(echo "$iptables_output" | grep "dpt:8443" | awk '{print $2}')
|
||||||
|
site_pkts=$(echo "$iptables_output" | grep "dpt:8443" | awk '{print $1}')
|
||||||
|
|
||||||
|
# Default to 0 if not found
|
||||||
|
proxy_bytes=${proxy_bytes:-0}
|
||||||
|
proxy_pkts=${proxy_pkts:-0}
|
||||||
|
site_bytes=${site_bytes:-0}
|
||||||
|
site_pkts=${site_pkts:-0}
|
||||||
|
|
||||||
|
# Write current snapshot as JSON
|
||||||
|
if command -v jq &>/dev/null; then
|
||||||
|
echo "{\"ts\":$ts,\"proxy_bytes\":$proxy_bytes,\"proxy_pkts\":$proxy_pkts,\"site_bytes\":$site_bytes,\"site_pkts\":$site_pkts}" > "$CURRENT_SNAPSHOT" 2>/dev/null
|
||||||
|
else
|
||||||
|
cat > "$CURRENT_SNAPSHOT" 2>/dev/null <<EOF
|
||||||
|
{"ts":$ts,"proxy_bytes":$proxy_bytes,"proxy_pkts":$proxy_pkts,"site_bytes":$site_bytes,"site_pkts":$site_pkts}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save snapshot for rate calculation (one per minute)
|
||||||
|
local minute_key=$(date +%s -d "1 minute ago" +%Y%m%d%H%M 2>/dev/null || date +%Y%m%d%H%M)
|
||||||
|
local snapshot_file="$SNAPSHOTS_DIR/snap_${minute_key}.json"
|
||||||
|
cp "$CURRENT_SNAPSHOT" "$snapshot_file" 2>/dev/null
|
||||||
|
|
||||||
|
# Append to history CSV (once per minute, check if last entry is fresh)
|
||||||
|
if [[ -f "$HISTORY_FILE" ]]; then
|
||||||
|
local last_ts=$(tail -1 "$HISTORY_FILE" 2>/dev/null | cut -d, -f1)
|
||||||
|
local current_minute=$((ts - (ts % 60)))
|
||||||
|
|
||||||
|
if [[ -z "$last_ts" ]] || [[ $((current_minute - last_ts)) -ge 60 ]]; then
|
||||||
|
echo "$current_minute,$proxy_bytes,$site_bytes" >> "$HISTORY_FILE" 2>/dev/null
|
||||||
|
|
||||||
|
# Cleanup old entries (keep only 365 days)
|
||||||
|
stats_cleanup_history
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "$temp_file" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read current snapshot as JSON
|
||||||
|
stats_read_current() {
|
||||||
|
if [[ -f "$CURRENT_SNAPSHOT" ]]; then
|
||||||
|
cat "$CURRENT_SNAPSHOT"
|
||||||
|
else
|
||||||
|
echo "{}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Extract value from JSON (fallback if jq not available)
|
||||||
|
json_get() {
|
||||||
|
local json="$1"
|
||||||
|
local key="$2"
|
||||||
|
|
||||||
|
if command -v jq &>/dev/null; then
|
||||||
|
echo "$json" | jq -r ".${key}" 2>/dev/null || echo "0"
|
||||||
|
else
|
||||||
|
echo "$json" | grep -o "\"$key\":[^,}]*" | cut -d: -f2 | tr -d ' "' || echo "0"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert bytes to human-readable format
|
||||||
|
format_bytes() {
|
||||||
|
local bytes=$1
|
||||||
|
|
||||||
|
if (( bytes < 1024 )); then
|
||||||
|
printf "%.0f B" "$bytes"
|
||||||
|
elif (( bytes < 1024 * 1024 )); then
|
||||||
|
printf "%.1f KB" "$(echo "scale=1; $bytes / 1024" | bc 2>/dev/null || echo "$((bytes / 1024))")"
|
||||||
|
elif (( bytes < 1024 * 1024 * 1024 )); then
|
||||||
|
printf "%.1f MB" "$(echo "scale=1; $bytes / 1024 / 1024" | bc 2>/dev/null || echo "$((bytes / 1024 / 1024))")"
|
||||||
|
elif (( bytes < 1024 * 1024 * 1024 * 1024 )); then
|
||||||
|
printf "%.1f GB" "$(echo "scale=1; $bytes / 1024 / 1024 / 1024" | bc 2>/dev/null || echo "$((bytes / 1024 / 1024 / 1024))")"
|
||||||
|
else
|
||||||
|
printf "%.1f TB" "$(echo "scale=1; $bytes / 1024 / 1024 / 1024 / 1024" | bc 2>/dev/null || echo "$((bytes / 1024 / 1024 / 1024 / 1024))")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert bytes/sec to human-readable rate
|
||||||
|
format_rate() {
|
||||||
|
local bytes_per_sec=$1
|
||||||
|
|
||||||
|
if (( bytes_per_sec < 1024 )); then
|
||||||
|
printf "%.0f B/s" "$bytes_per_sec"
|
||||||
|
elif (( bytes_per_sec < 1024 * 1024 )); then
|
||||||
|
printf "%.1f KB/s" "$(echo "scale=1; $bytes_per_sec / 1024" | bc 2>/dev/null || echo "$((bytes_per_sec / 1024))")"
|
||||||
|
elif (( bytes_per_sec < 1024 * 1024 * 1024 )); then
|
||||||
|
printf "%.1f MB/s" "$(echo "scale=1; $bytes_per_sec / 1024 / 1024" | bc 2>/dev/null || echo "$((bytes_per_sec / 1024 / 1024))")"
|
||||||
|
else
|
||||||
|
printf "%.1f GB/s" "$(echo "scale=1; $bytes_per_sec / 1024 / 1024 / 1024" | bc 2>/dev/null || echo "$((bytes_per_sec / 1024 / 1024 / 1024))")"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Calculate traffic rates and totals from history
|
||||||
|
stats_calculate_rates() {
|
||||||
|
local traffic_type="$1" # "proxy" or "site"
|
||||||
|
local col_idx=2 # proxy_bytes is column 2
|
||||||
|
|
||||||
|
[[ "$traffic_type" == "site" ]] && col_idx=3 # site_bytes is column 3
|
||||||
|
|
||||||
|
local now=$(date +%s)
|
||||||
|
local result=""
|
||||||
|
|
||||||
|
# 1 minute rate
|
||||||
|
local ts_1m=$((now - 60))
|
||||||
|
local bytes_now=$(tail -1 "$HISTORY_FILE" 2>/dev/null | cut -d, -f"$col_idx")
|
||||||
|
local bytes_1m=$(awk -F, -v ts="$ts_1m" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
|
||||||
|
local diff_1m=$((bytes_now - (bytes_1m > 0 ? bytes_1m : bytes_now)))
|
||||||
|
[[ $diff_1m -lt 0 ]] && diff_1m=0
|
||||||
|
local rate_1m=$((diff_1m / 60))
|
||||||
|
local bytes_1m_fmt=$(format_bytes "$diff_1m")
|
||||||
|
local rate_1m_fmt=$(format_rate "$rate_1m")
|
||||||
|
|
||||||
|
# 5 minute rate
|
||||||
|
local ts_5m=$((now - 300))
|
||||||
|
local bytes_5m=$(awk -F, -v ts="$ts_5m" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
|
||||||
|
local diff_5m=$((bytes_now - (bytes_5m > 0 ? bytes_5m : bytes_now)))
|
||||||
|
[[ $diff_5m -lt 0 ]] && diff_5m=0
|
||||||
|
local rate_5m=$((diff_5m / 300))
|
||||||
|
local bytes_5m_fmt=$(format_bytes "$diff_5m")
|
||||||
|
local rate_5m_fmt=$(format_rate "$rate_5m")
|
||||||
|
|
||||||
|
# 60 minute rate
|
||||||
|
local ts_60m=$((now - 3600))
|
||||||
|
local bytes_60m=$(awk -F, -v ts="$ts_60m" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
|
||||||
|
local diff_60m=$((bytes_now - (bytes_60m > 0 ? bytes_60m : bytes_now)))
|
||||||
|
[[ $diff_60m -lt 0 ]] && diff_60m=0
|
||||||
|
local rate_60m=$((diff_60m / 3600))
|
||||||
|
local bytes_60m_fmt=$(format_bytes "$diff_60m")
|
||||||
|
local rate_60m_fmt=$(format_rate "$rate_60m")
|
||||||
|
|
||||||
|
# 1 day total
|
||||||
|
local ts_1d=$((now - 86400))
|
||||||
|
local bytes_1d=$(awk -F, -v ts="$ts_1d" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
|
||||||
|
local diff_1d=$((bytes_now - (bytes_1d > 0 ? bytes_1d : bytes_now)))
|
||||||
|
[[ $diff_1d -lt 0 ]] && diff_1d=0
|
||||||
|
local rate_1d=$((diff_1d > 0 ? diff_1d / 86400 : 0))
|
||||||
|
local bytes_1d_fmt=$(format_bytes "$diff_1d")
|
||||||
|
local rate_1d_fmt=$(format_rate "$rate_1d")
|
||||||
|
|
||||||
|
# 7 days total
|
||||||
|
local ts_7d=$((now - 604800))
|
||||||
|
local bytes_7d=$(awk -F, -v ts="$ts_7d" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
|
||||||
|
local diff_7d=$((bytes_now - (bytes_7d > 0 ? bytes_7d : bytes_now)))
|
||||||
|
[[ $diff_7d -lt 0 ]] && diff_7d=0
|
||||||
|
local rate_7d=$((diff_7d > 0 ? diff_7d / 604800 : 0))
|
||||||
|
local bytes_7d_fmt=$(format_bytes "$diff_7d")
|
||||||
|
local rate_7d_fmt=$(format_rate "$rate_7d")
|
||||||
|
|
||||||
|
# 30 days total
|
||||||
|
local ts_30d=$((now - 2592000))
|
||||||
|
local bytes_30d=$(awk -F, -v ts="$ts_30d" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
|
||||||
|
local diff_30d=$((bytes_now - (bytes_30d > 0 ? bytes_30d : bytes_now)))
|
||||||
|
[[ $diff_30d -lt 0 ]] && diff_30d=0
|
||||||
|
local rate_30d=$((diff_30d > 0 ? diff_30d / 2592000 : 0))
|
||||||
|
local bytes_30d_fmt=$(format_bytes "$diff_30d")
|
||||||
|
local rate_30d_fmt=$(format_rate "$rate_30d")
|
||||||
|
|
||||||
|
# 365 days total
|
||||||
|
local ts_365d=$((now - 31536000))
|
||||||
|
local bytes_365d=$(awk -F, -v ts="$ts_365d" '$1 >= ts' "$HISTORY_FILE" 2>/dev/null | head -1 | cut -d, -f"$col_idx")
|
||||||
|
local diff_365d=$((bytes_now - (bytes_365d > 0 ? bytes_365d : bytes_now)))
|
||||||
|
[[ $diff_365d -lt 0 ]] && diff_365d=0
|
||||||
|
local rate_365d=$((diff_365d > 0 ? diff_365d / 31536000 : 0))
|
||||||
|
local bytes_365d_fmt=$(format_bytes "$diff_365d")
|
||||||
|
local rate_365d_fmt=$(format_rate "$rate_365d")
|
||||||
|
|
||||||
|
# Return as pipe-delimited format for table display
|
||||||
|
echo "$bytes_1m_fmt|$rate_1m_fmt|$bytes_5m_fmt|$rate_5m_fmt|$bytes_60m_fmt|$rate_60m_fmt|$bytes_1d_fmt|$rate_1d_fmt|$bytes_7d_fmt|$rate_7d_fmt|$bytes_30d_fmt|$rate_30d_fmt|$bytes_365d_fmt|$rate_365d_fmt"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main display function for traffic statistics
|
||||||
|
show_traffic_stats() {
|
||||||
|
# Ensure stats are collected
|
||||||
|
stats_collect
|
||||||
|
|
||||||
|
# Get current counters
|
||||||
|
local current_json=$(stats_read_current)
|
||||||
|
local proxy_pkts=$(json_get "$current_json" "proxy_pkts")
|
||||||
|
local site_pkts=$(json_get "$current_json" "site_pkts")
|
||||||
|
|
||||||
|
# Calculate rates for proxy
|
||||||
|
local proxy_rates=$(stats_calculate_rates "proxy")
|
||||||
|
IFS='|' read -r p1m p1mr p5m p5mr p60m p60mr p1d p1dr p7d p7dr p30d p30dr p365d p365dr <<< "$proxy_rates"
|
||||||
|
|
||||||
|
# Calculate rates for site
|
||||||
|
local site_rates=$(stats_calculate_rates "site")
|
||||||
|
IFS='|' read -r s1m s1mr s5m s5mr s60m s60mr s1d s1dr s7d s7dr s30d s30dr s365d s365dr <<< "$site_rates"
|
||||||
|
|
||||||
|
# Display proxy stats
|
||||||
|
{
|
||||||
|
echo ""
|
||||||
|
echo -e "${BLUE} Proxy (telemt, порт 443):${NC}"
|
||||||
|
echo -e "${BLUE} ─────────────────────────────────────────${NC}"
|
||||||
|
echo -e "${BLUE} Период │ Входящий │ Скорость${NC}"
|
||||||
|
echo -e "${BLUE} ─────────────────────────────────────────${NC}"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "1 мин" "$p1m" "$p1mr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "5 мин" "$p5m" "$p5mr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "60 мин" "$p60m" "$p60mr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "1 день" "$p1d" "$p1dr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "7 дней" "$p7d" "$p7dr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "30 дней" "$p30d" "$p30dr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "365 дней" "$p365d" "$p365dr"
|
||||||
|
echo -e "${BLUE} ─────────────────────────────────────────${NC}"
|
||||||
|
printf " Пакетов: %d\n\n" "$proxy_pkts"
|
||||||
|
|
||||||
|
echo -e "${BLUE} Сайт (nginx, порт 8443):${NC}"
|
||||||
|
echo -e "${BLUE} ─────────────────────────────────────────${NC}"
|
||||||
|
echo -e "${BLUE} Период │ Входящий │ Скорость${NC}"
|
||||||
|
echo -e "${BLUE} ─────────────────────────────────────────${NC}"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "1 мин" "$s1m" "$s1mr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "5 мин" "$s5m" "$s5mr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "60 мин" "$s60m" "$s60mr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "1 день" "$s1d" "$s1dr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "7 дней" "$s7d" "$s7dr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "30 дней" "$s30d" "$s30dr"
|
||||||
|
printf " %-9s │ %14s │ %s\n" "365 дней" "$s365d" "$s365dr"
|
||||||
|
echo -e "${BLUE} ─────────────────────────────────────────${NC}"
|
||||||
|
printf " Пакетов: %d\n" "$site_pkts"
|
||||||
|
echo ""
|
||||||
|
} >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Clean up history older than 365 days
|
||||||
|
stats_cleanup_history() {
|
||||||
|
if [[ ! -f "$HISTORY_FILE" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local now=$(date +%s)
|
||||||
|
local ts_365d=$((now - 31536000))
|
||||||
|
local temp_file=$(mktemp)
|
||||||
|
|
||||||
|
# Keep header + entries from last 365 days
|
||||||
|
{
|
||||||
|
head -1 "$HISTORY_FILE"
|
||||||
|
awk -F, -v ts="$ts_365d" '$1 >= ts' "$HISTORY_FILE" | tail -n +2
|
||||||
|
} > "$temp_file" 2>/dev/null
|
||||||
|
|
||||||
|
mv "$temp_file" "$HISTORY_FILE" 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Toggle stats collection on/off
|
||||||
|
toggle_stats() {
|
||||||
|
local current_state="false"
|
||||||
|
|
||||||
|
# Read current state from config
|
||||||
|
if [[ -f "$CONFIG_FILE" ]] && command -v jq &>/dev/null; then
|
||||||
|
current_state=$(jq -r '.stats_enabled // false' "$CONFIG_FILE" 2>/dev/null)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Toggle
|
||||||
|
if [[ "$current_state" == "true" ]]; then
|
||||||
|
# Disable stats
|
||||||
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
|
if command -v jq &>/dev/null; then
|
||||||
|
jq '.stats_enabled = false' "$CONFIG_FILE" > "${CONFIG_FILE}.tmp" 2>/dev/null
|
||||||
|
mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove iptables rules
|
||||||
|
iptables -D INPUT -j GOTELEGRAM_STATS 2>/dev/null
|
||||||
|
iptables -F GOTELEGRAM_STATS 2>/dev/null
|
||||||
|
iptables -X GOTELEGRAM_STATS 2>/dev/null
|
||||||
|
|
||||||
|
# Clean up directories
|
||||||
|
rm -rf "$STATS_DIR" 2>/dev/null
|
||||||
|
|
||||||
|
echo "Сбор статистики ОТКЛЮЧЕН" >&2
|
||||||
|
else
|
||||||
|
# Enable stats
|
||||||
|
if [[ -f "$CONFIG_FILE" ]]; then
|
||||||
|
if command -v jq &>/dev/null; then
|
||||||
|
jq '.stats_enabled = true' "$CONFIG_FILE" > "${CONFIG_FILE}.tmp" 2>/dev/null
|
||||||
|
mv "${CONFIG_FILE}.tmp" "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Initialize stats collection
|
||||||
|
stats_init
|
||||||
|
|
||||||
|
echo "Сбор статистики ВКЛЮЧЕН" >&2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Install systemd service for stats collection
|
||||||
|
install_stats_collector() {
|
||||||
|
local service_file="/etc/systemd/system/gotelegram-stats.service"
|
||||||
|
|
||||||
|
# Check if running as root
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "Требуется root для установки сервиса" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get script directory (resolve symlinks)
|
||||||
|
local script_dir=$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
|
||||||
|
local lib_dir=$(dirname "$script_dir")
|
||||||
|
|
||||||
|
# Create systemd service file
|
||||||
|
cat > "$service_file" <<'EOF'
|
||||||
|
[Unit]
|
||||||
|
Description=GoTelegram Traffic Stats Collector
|
||||||
|
After=network.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
ExecStart=/bin/bash -c 'source /opt/gotelegram/lib/common.sh; source /opt/gotelegram/lib/stats.sh; stats_init; while true; do stats_collect; sleep 1; done'
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
chmod 644 "$service_file"
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable gotelegram-stats.service
|
||||||
|
systemctl start gotelegram-stats.service
|
||||||
|
|
||||||
|
echo "Сервис gotelegram-stats установлен и запущен" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Remove stats collector service
|
||||||
|
remove_stats_collector() {
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
echo "Требуется root для удаления сервиса" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
systemctl stop gotelegram-stats.service 2>/dev/null
|
||||||
|
systemctl disable gotelegram-stats.service 2>/dev/null
|
||||||
|
rm -f /etc/systemd/system/gotelegram-stats.service
|
||||||
|
systemctl daemon-reload
|
||||||
|
|
||||||
|
# Remove iptables rules
|
||||||
|
iptables -D INPUT -j GOTELEGRAM_STATS 2>/dev/null
|
||||||
|
iptables -F GOTELEGRAM_STATS 2>/dev/null
|
||||||
|
iptables -X GOTELEGRAM_STATS 2>/dev/null
|
||||||
|
|
||||||
|
# Clean up directories and files
|
||||||
|
rm -rf "$STATS_DIR" 2>/dev/null
|
||||||
|
rm -f "$HISTORY_FILE" 2>/dev/null
|
||||||
|
|
||||||
|
echo "Сервис статистики удалён" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# Export functions for external use
|
||||||
|
export -f stats_init stats_collect stats_read_current stats_calculate_rates
|
||||||
|
export -f show_traffic_stats format_bytes format_rate toggle_stats
|
||||||
|
export -f stats_cleanup_history install_stats_collector remove_stats_collector
|
||||||
|
export -f json_get
|
||||||
0
lib/telemt.sh
Normal file → Executable file
0
lib/telemt.sh
Normal file → Executable file
20
lib/telemt_config.sh
Normal file → Executable file
20
lib/telemt_config.sh
Normal file → Executable file
@@ -29,17 +29,17 @@ QUICK_DOMAINS=(
|
|||||||
generate_telemt_toml() {
|
generate_telemt_toml() {
|
||||||
local secret="$1"
|
local secret="$1"
|
||||||
local port="${2:-443}"
|
local port="${2:-443}"
|
||||||
local mask_mode="${3:-quick}" # quick | stealth
|
local mask_mode="${3:-lite}" # lite | pro
|
||||||
local mask_domain="${4:-google.com}"
|
local mask_domain="${4:-google.com}"
|
||||||
local mask_port="${5:-443}"
|
local mask_port="${5:-443}"
|
||||||
local output="${6:-$TELEMT_CONFIG}"
|
local output="${6:-$TELEMT_CONFIG}"
|
||||||
|
|
||||||
mkdir -p "$(dirname "$output")"
|
mkdir -p "$(dirname "$output")"
|
||||||
|
|
||||||
# DNS override для stealth: домен резолвится в 127.0.0.1
|
# DNS override для pro: домен резолвится в 127.0.0.1
|
||||||
# чтобы mask-трафик шёл на локальный nginx, а не в интернет
|
# чтобы mask-трафик шёл на локальный nginx, а не в интернет
|
||||||
local dns_line=""
|
local dns_line=""
|
||||||
if [ "$mask_mode" = "stealth" ]; then
|
if [ "$mask_mode" = "pro" ]; then
|
||||||
dns_line="dns_overrides = [\"${mask_domain}:${mask_port}:127.0.0.1\"]"
|
dns_line="dns_overrides = [\"${mask_domain}:${mask_port}:127.0.0.1\"]"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ listen_addr_ipv4 = "0.0.0.0"
|
|||||||
tls_domain = "${mask_domain}"
|
tls_domain = "${mask_domain}"
|
||||||
mask = true
|
mask = true
|
||||||
mask_port = ${mask_port}
|
mask_port = ${mask_port}
|
||||||
tls_emulation = $([ "$mask_mode" = "stealth" ] && echo "false" || echo "true")
|
tls_emulation = $([ "$mask_mode" = "pro" ] && echo "false" || echo "true")
|
||||||
|
|
||||||
[access.users]
|
[access.users]
|
||||||
main = "${secret}"
|
main = "${secret}"
|
||||||
@@ -261,7 +261,7 @@ show_proxy_info() {
|
|||||||
status=$(telemt_status)
|
status=$(telemt_status)
|
||||||
|
|
||||||
local mode
|
local mode
|
||||||
mode=$(config_get mode 2>/dev/null || echo "quick")
|
mode=$(config_get mode 2>/dev/null || echo "lite")
|
||||||
|
|
||||||
local status_icon status_text
|
local status_icon status_text
|
||||||
case "$status" in
|
case "$status" in
|
||||||
@@ -290,21 +290,21 @@ show_proxy_info() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Вывод информации о прокси (Stealth-режим) ──────────────────────────────
|
# ── Вывод информации о прокси (Pro-режим) ──────────────────────────────────
|
||||||
# В stealth-режиме ссылка содержит домен (не IP) и fake-TLS секрет (ee...)
|
# В pro-режиме ссылка содержит домен (не IP) и fake-TLS секрет (ee...)
|
||||||
show_proxy_info_stealth() {
|
show_proxy_info_pro() {
|
||||||
local domain="$1"
|
local domain="$1"
|
||||||
local faketls_secret="$2"
|
local faketls_secret="$2"
|
||||||
|
|
||||||
local link="tg://proxy?server=${domain}&port=443&secret=${faketls_secret}"
|
local link="tg://proxy?server=${domain}&port=443&secret=${faketls_secret}"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}✅ Stealth-прокси настроен${NC}"
|
echo -e " ${BOLD}${WHITE}✅ Pro-прокси настроен${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
echo -e " ${WHITE}Ядро:${NC} telemt (Rust)"
|
echo -e " ${WHITE}Ядро:${NC} telemt (Rust)"
|
||||||
echo -e " ${WHITE}Домен:${NC} ${CYAN}${domain}${NC}"
|
echo -e " ${WHITE}Домен:${NC} ${CYAN}${domain}${NC}"
|
||||||
echo -e " ${WHITE}Порт:${NC} ${CYAN}443${NC} (внешний, telemt)"
|
echo -e " ${WHITE}Порт:${NC} ${CYAN}443${NC} (внешний, telemt)"
|
||||||
echo -e " ${WHITE}Режим:${NC} ${MAGENTA}Stealth (fake-TLS)${NC}"
|
echo -e " ${WHITE}Режим:${NC} ${MAGENTA}Pro (fake-TLS)${NC}"
|
||||||
echo -e " ${WHITE}nginx:${NC} ${CYAN}127.0.0.1:8443${NC} (внутренний)"
|
echo -e " ${WHITE}nginx:${NC} ${CYAN}127.0.0.1:8443${NC} (внутренний)"
|
||||||
echo -e " ${WHITE}Secret:${NC} ${CYAN}${faketls_secret:0:20}...${NC}"
|
echo -e " ${WHITE}Secret:${NC} ${CYAN}${faketls_secret:0:20}...${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
|
|||||||
0
lib/templates_catalog.sh
Normal file → Executable file
0
lib/templates_catalog.sh
Normal file → Executable file
20
lib/website.sh
Normal file → Executable file
20
lib/website.sh
Normal file → Executable file
@@ -39,8 +39,8 @@ generate_nginx_config() {
|
|||||||
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
||||||
|
|
||||||
cat > "$NGINX_SITE_CONF" << 'EONGINX'
|
cat > "$NGINX_SITE_CONF" << 'EONGINX'
|
||||||
# GoTelegram v2.2 — nginx config
|
# GoTelegram v2.3 — nginx config
|
||||||
# Stealth: nginx на 127.0.0.1:8443 (внутренний), telemt на 0.0.0.0:443 (внешний)
|
# Pro: nginx на 127.0.0.1:8443 (внутренний), telemt на 0.0.0.0:443 (внешний)
|
||||||
# Обычный браузер → :443 → telemt → 127.0.0.1:8443 → nginx (сайт)
|
# Обычный браузер → :443 → telemt → 127.0.0.1:8443 → nginx (сайт)
|
||||||
|
|
||||||
server {
|
server {
|
||||||
@@ -258,14 +258,14 @@ deploy_template_to_nginx() {
|
|||||||
log_success "Шаблон развёрнут в $WEBSITE_ROOT"
|
log_success "Шаблон развёрнут в $WEBSITE_ROOT"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Полная установка stealth-режима ──────────────────────────────────────────
|
# ── Полная установка pro-режима ──────────────────────────────────────────────
|
||||||
setup_stealth_mode() {
|
setup_pro_mode() {
|
||||||
local domain="$1"
|
local domain="$1"
|
||||||
local template_dir="$2"
|
local template_dir="$2"
|
||||||
local proxy_port="${3:-443}"
|
local proxy_port="${3:-443}"
|
||||||
local email="${4:-}"
|
local email="${4:-}"
|
||||||
|
|
||||||
log_step "Настройка stealth-режима"
|
log_step "Настройка pro-режима"
|
||||||
|
|
||||||
# 1. Устанавливаем nginx
|
# 1. Устанавливаем nginx
|
||||||
run_with_spinner "Установка nginx" install_nginx || return 1
|
run_with_spinner "Установка nginx" install_nginx || return 1
|
||||||
@@ -298,7 +298,7 @@ setup_stealth_mode() {
|
|||||||
# 8. Показываем благодарности авторам шаблонов
|
# 8. Показываем благодарности авторам шаблонов
|
||||||
show_credits
|
show_credits
|
||||||
|
|
||||||
log_success "Stealth-режим настроен: https://${domain}"
|
log_success "Pro-режим настроен: https://${domain}"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,13 +322,13 @@ restart_nginx() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Удаление stealth-режима ──────────────────────────────────────────────────
|
# ── Удаление pro-режима ──────────────────────────────────────────────────────
|
||||||
remove_stealth_mode() {
|
remove_pro_mode() {
|
||||||
log_info "Удаление stealth-режима..."
|
log_info "Удаление pro-режима..."
|
||||||
rm -f "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
rm -f "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
||||||
rm -rf "$WEBSITE_ROOT"
|
rm -rf "$WEBSITE_ROOT"
|
||||||
systemctl restart nginx 2>/dev/null
|
systemctl restart nginx 2>/dev/null
|
||||||
log_success "Stealth-режим удалён (nginx оставлен)"
|
log_success "Pro-режим удалён (nginx оставлен)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Смена шаблона ────────────────────────────────────────────────────────────
|
# ── Смена шаблона ────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user