#!/bin/bash # ๐Ÿš€ SwiftGram MTProxy โ€” Smart Modular Manager (Self-contained) # v1.1.0 โ€” ะคะธะบั ะทะฒะพะฝะบะพะฒ (UDP) + ะะฒั‚ะพะฝะพะผะฝั‹ะน ั€ะตะถะธะผ # โ”€โ”€ ะฆะฒะตั‚ะฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ RED='\033[0;31m' GREEN='\033[0;32m' CYAN='\033[0;36m' YELLOW='\033[1;33m' MAGENTA='\033[0;35m' BLUE='\033[0;34m' WHITE='\033[1;37m' NC='\033[0m' # โ”€โ”€ ะšะพะฝั„ะธะณ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ CONTAINER_NAME="swiftgram-proxy" BOT_DIR="/opt/swiftgram" SERVICE_NAME="swiftgram-bot" # โ”€โ”€ ะกะฟะธะฝะฝะตั€ ะธ ะฟั€ะพะณั€ะตัั-ะฑะฐั€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ spin_pid="" spinner_start() { local msg="${1:-ะŸะพะดะพะถะดะธั‚ะต...}" ( local frames=('โ ‹' 'โ ™' 'โ น' 'โ ธ' 'โ ผ' 'โ ด' 'โ ฆ' 'โ ง' 'โ ‡' 'โ ') local i=0 while true; do printf "\r ${CYAN}${frames[$i]}${NC} ${msg}" >&2 i=$(( (i+1) % ${#frames[@]} )) sleep 0.12 done ) & spin_pid=$! } spinner_stop() { [ -n "$spin_pid" ] && kill "$spin_pid" 2>/dev/null && wait "$spin_pid" 2>/dev/null spin_pid="" printf "\r\033[K" >&2 } progress_bar() { local current="$1" total="$2" label="${3:-}" local pct=$(( current * 100 / total )) local filled=$(( pct / 2 )) local empty=$(( 50 - filled )) local bar="" for ((i=0; i&2 [ "$current" -eq "$total" ] && echo "" >&2 } run_with_progress() { local label="$1"; shift spinner_start "$label" "$@" >/dev/null 2>&1 local rc=$? spinner_stop if [ $rc -eq 0 ]; then echo -e " ${GREEN}โœ“${NC} $label" else echo -e " ${RED}โœ—${NC} $label ${RED}(ะพัˆะธะฑะบะฐ)${NC}" fi return $rc } # โ”€โ”€ ะŸั€ะพะฒะตั€ะบะธ ัะธัั‚ะตะผั‹ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ if [ "$EUID" -ne 0 ]; then echo -e "${RED}ะ—ะฐะฟัƒัั‚ะธั‚ะต ั sudo / root.${NC}" exit 1 fi install_pkg() { if command -v apt-get &>/dev/null; then apt-get update -qq && apt-get install -y -qq "$@" elif command -v dnf &>/dev/null; then dnf install -y "$@" 2>/dev/null elif command -v yum &>/dev/null; then yum install -y "$@" fi } # โ”€โ”€ ะžะฟั‚ะธะผะธะทะฐั†ะธั ะกะตั‚ะธ (BBR) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ optimize_system() { if ! sysctl net.ipv4.tcp_congestion_control | grep -q "bbr"; then spinner_start "ะžะฟั‚ะธะผะธะทะฐั†ะธั ัะตั‚ะตะฒะพะณะพ ัั‚ะตะบะฐ (BBR)..." { echo "net.core.default_qdisc=fq" echo "net.ipv4.tcp_congestion_control=bbr" echo "net.ipv4.ip_local_port_range=1024 65535" echo "net.core.somaxconn=65535" } >> /etc/sysctl.conf sysctl -p >/dev/null 2>&1 spinner_stop echo -e " ${GREEN}โœ“${NC} BBR ัƒัะบะพั€ะตะฝะธะต ะฒะบะปัŽั‡ะตะฝะพ" fi } # โ”€โ”€ Firewall (ะคะธะบั ะทะฒะพะฝะบะพะฒ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ fix_firewall() { local port="$1" if command -v ufw &>/dev/null && ufw status | grep -q "active"; then ufw allow "$port"/tcp >/dev/null 2>&1 ufw allow "$port"/udp >/dev/null 2>&1 elif command -v firewall-cmd &>/dev/null && systemctl is-active --quiet firewalld; then firewall-cmd --permanent --add-port="$port"/tcp >/dev/null 2>&1 firewall-cmd --permanent --add-port="$port"/udp >/dev/null 2>&1 firewall-cmd --reload >/dev/null 2>&1 fi echo -e " ${GREEN}โœ“${NC} Firewall: ะฟะพั€ั‚ั‹ $port (TCP/UDP) ะพั‚ะบั€ั‹ั‚ั‹" } # โ”€โ”€ ะฃัั‚ะฐะฝะพะฒะบะฐ ะทะฐะฒะธัะธะผะพัั‚ะตะน โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ install_base_deps() { local steps=0 total=4 progress_bar $steps $total "ะŸั€ะพะฒะตั€ะบะฐ ะทะฐะฒะธัะธะผะพัั‚ะตะน..." if ! command -v curl &>/dev/null; then run_with_progress "ะฃัั‚ะฐะฝะพะฒะบะฐ curl" install_pkg curl; fi steps=$((steps+1)); progress_bar $steps $total "curl" if ! command -v docker &>/dev/null; then spinner_start "ะฃัั‚ะฐะฝะพะฒะบะฐ Docker..." curl -fsSL https://get.docker.com | sh >/dev/null 2>&1 systemctl enable --now docker >/dev/null 2>&1 spinner_stop fi steps=$((steps+1)); progress_bar $steps $total "docker" if ! command -v qrencode &>/dev/null; then run_with_progress "ะฃัั‚ะฐะฝะพะฒะบะฐ qrencode" install_pkg qrencode; fi steps=$((steps+1)); progress_bar $steps $total "qrencode" if ! docker info &>/dev/null 2>&1; then systemctl start docker 2>/dev/null; sleep 2; fi steps=$((steps+1)); progress_bar $steps $total "ะ“ะพั‚ะพะฒะพ" echo "" } # โ”€โ”€ ะฃั‚ะธะปะธั‚ั‹ IP ะธ ะŸะพั€ั‚ะพะฒ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ get_ip4() { curl -s -4 --max-time 5 https://api.ipify.org || echo "0.0.0.0"; } get_ip6() { curl -s -6 --max-time 5 https://api6.ipify.org || echo ""; } find_smart_port() { local port=443 if ss -tlnp | grep -qE ":${port}\b"; then echo -e " ${YELLOW}โ„น ะŸะพั€ั‚ 443 ะทะฐะฝัั‚ (Hiddify/Nginx). ะŸั€ะพะฑัƒัŽ 8443...${NC}" >&2 port=8443 if ss -tlnp | grep -qE ":${port}\b"; then port=$(( (RANDOM % 10000) + 20000 )) echo -e " ${YELLOW}โ„น ะŸะพั€ั‚ 8443 ั‚ะพะถะต ะทะฐะฝัั‚. ะ’ั‹ะฑั€ะฐะฝ ัะปัƒั‡ะฐะนะฝั‹ะน: $port${NC}" >&2 fi fi echo "$port" } # โ”€โ”€ ะ’ัˆะธั‚ั‹ะต ะธัั…ะพะดะฝะธะบะธ ะฑะพั‚ะฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ prepare_bot_source() { mkdir -p "$BOT_DIR" cat << 'EOF' > "$BOT_DIR/requirements.txt" python-telegram-bot==20.8 EOF cat << 'EOF' > "$BOT_DIR/bot.py" #!/usr/bin/env python3 """ SwiftGram MTProxy โ€” Telegram-ะฑะพั‚ ะดะปั ัƒะฟั€ะฐะฒะปะตะฝะธั MTProxy ะฝะฐ ัะตั€ะฒะตั€ะต. """ import asyncio import html import json import os import re from pathlib import Path _env_path = Path(__file__).resolve().parent / ".env" if not _env_path.exists(): _env_path = Path("/opt/swiftgram/.env") if _env_path.exists(): with open(_env_path, encoding="utf-8") as f: for line in f: line = line.strip() if line and not line.startswith("#") and "=" in line: k, v = line.split("=", 1) os.environ.setdefault(k.strip(), v.strip().strip('"').strip("'")) from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup from telegram.ext import ( Application, CommandHandler, CallbackQueryHandler, ContextTypes, ) BOT_TOKEN = os.environ.get("BOT_TOKEN") _allowed = os.environ.get("ALLOWED_IDS", "").strip() try: ALLOWED_IDS = set(int(x) for x in _allowed.split(",") if x.strip()) if _allowed else None except ValueError: ALLOWED_IDS = None CONTAINER_NAME = os.environ.get("CONTAINER_NAME", "swiftgram-proxy") CONFIG_FILE = Path(os.environ.get("CONFIG_PATH", "/opt/swiftgram/proxy.json")) DOMAINS = [ "google.com", "wikipedia.org", "habr.com", "github.com", "coursera.org", "udemy.com", "medium.com", "stackoverflow.com", "bbc.com", "cnn.com", "reuters.com", "nytimes.com", ] def _ok(uid: int) -> bool: return ALLOWED_IDS is None or uid in ALLOWED_IDS def _decode(data: bytes) -> str: return (data or b"").decode("utf-8", errors="replace").strip() async def sh(*args: str, timeout: int = 60) -> tuple[int, str, str]: proc = await asyncio.create_subprocess_exec( *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) try: out, err = await asyncio.wait_for(proc.communicate(), timeout=timeout) except asyncio.TimeoutError: proc.kill() await proc.wait() return -1, "", "Timeout" return proc.returncode or 0, _decode(out), _decode(err) async def get_ip4() -> str: for url in ("https://api.ipify.org", "https://icanhazip.com", "https://ifconfig.me"): code, out, _ = await sh("curl", "-s", "-4", "--max-time", "5", url, timeout=8) if code == 0 and out: m = re.search(r"(\d{1,3}\.){3}\d{1,3}", out) if m: return m.group(0) return "0.0.0.0" async def get_ip6() -> str: code, out, _ = await sh("curl", "-s", "-6", "--max-time", "5", "https://api6.ipify.org", timeout=8) if code == 0 and out: return out.strip() return "" async def check_bbr() -> bool: code, out, _ = await sh("sysctl", "net.ipv4.tcp_congestion_control", timeout=5) return "bbr" in out.lower() async def proxy_running() -> bool: code, out, _ = await sh("docker", "ps", "--format", "{{.Names}}", timeout=10) return code == 0 and CONTAINER_NAME in out async def docker_val(fmt: str) -> str: code, out, _ = await sh("docker", "inspect", CONTAINER_NAME, "--format", fmt, timeout=10) return out.strip() if code == 0 else "" async def check_port(port: int) -> str | None: if await proxy_running(): hp = await docker_val("{{range $p,$c := .HostConfig.PortBindings}}{{(index $c 0).HostPort}} {{end}}") if str(port) in hp.split(): return None code, out, _ = await sh("ss", "-tlnp", timeout=5) for line in out.splitlines(): if f":{port} " in line or f":{port}\t" in line: return line return None def save_config(data: dict) -> None: CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True) CONFIG_FILE.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8") def load_config() -> dict: if CONFIG_FILE.exists(): try: return json.loads(CONFIG_FILE.read_text(encoding="utf-8")) except: pass return {} async def proxy_info() -> dict | None: if not await proxy_running(): return None cmd_str = await docker_val("{{range .Config.Cmd}}{{.}} {{end}}") secret = cmd_str.split()[-1] if cmd_str else "" hp = await docker_val("{{range $p,$c := .HostConfig.PortBindings}}{{(index $c 0).HostPort}}{{end}}") port = hp or "443" ip4 = await get_ip4() ip6 = await get_ip6() cfg = load_config() return { "ip4": ip4, "ip6": ip6, "port": port, "secret": secret, "domain": cfg.get("domain", "โ€”"), "link4": f"tg://proxy?server={ip4}&port={port}&secret={secret}", "link6": f"tg://proxy?server={ip6}&port={port}&secret={secret}" if ip6 else None } def main_menu_kb() -> InlineKeyboardMarkup: return InlineKeyboardMarkup([ [InlineKeyboardButton("๐Ÿ”ง ะฃัั‚ะฐะฝะพะฒะธั‚ัŒ / ะžะฑะฝะพะฒะธั‚ัŒ", callback_data="menu_install")], [InlineKeyboardButton("๐Ÿ“Š ะกั‚ะฐั‚ัƒั", callback_data="menu_status"), InlineKeyboardButton("๐Ÿ”— ะกัั‹ะปะบะฐ", callback_data="menu_link")], [InlineKeyboardButton("๐Ÿ“ค ะŸะพะดะตะปะธั‚ัŒัั", callback_data="menu_share")], [InlineKeyboardButton("๐Ÿ”„ ะ ะตัั‚ะฐั€ั‚", callback_data="menu_restart"), InlineKeyboardButton("๐Ÿ“‹ ะ›ะพะณะธ", callback_data="menu_logs")], [InlineKeyboardButton("๐Ÿ—‘ ะฃะดะฐะปะธั‚ัŒ ะฟั€ะพะบัะธ", callback_data="menu_remove")], ]) HELP_TEXT = ( "๐Ÿš€ SwiftGram MTProxy Manager\n\n" "โ€ข TCP + UDP (ะทะฒะพะฝะบะธ) ะฐะบั‚ะธะฒะฝั‹\n" "โ€ข IPv6 ะฟะพะดะดะตั€ะถะบะฐ ะฒะบะปัŽั‡ะตะฝะฐ\n" "โ€ข BBR ะพะฟั‚ะธะผะธะทะฐั†ะธั\n\n" ) async def start(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None: if not update.effective_user or not _ok(update.effective_user.id): return msg = update.message or (update.callback_query and update.callback_query.message) if update.message: await update.message.reply_text(HELP_TEXT, parse_mode="HTML", reply_markup=main_menu_kb()) elif update.callback_query: await update.callback_query.edit_message_text(HELP_TEXT, parse_mode="HTML", reply_markup=main_menu_kb()) async def cmd_status(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None: if not update.effective_user or not _ok(update.effective_user.id): return info = await proxy_info() if not info: text = "โŒ ะŸั€ะพะบัะธ ะฝะต ะทะฐะฟัƒั‰ะตะฝ." else: bbr = "โœ… ะะบั‚ะธะฒะตะฝ" if await check_bbr() else "โŒ ะ’ั‹ะบะปัŽั‡ะตะฝ" text = ( "โœ… SwiftGram ั€ะฐะฑะพั‚ะฐะตั‚\n\n" f"๐ŸŒ IPv4: {info['ip4']}\n" f"๐Ÿ”Œ ะŸะพั€ั‚: {info['port']}\n" f"๐Ÿš€ BBR: {bbr}\n" f"๐Ÿ“ž ะ—ะฒะพะฝะบะธ: โœ… UDP ะพั‚ะบั€ั‹ั‚\n" ) kb = InlineKeyboardMarkup([[InlineKeyboardButton("โ—€๏ธ ะœะตะฝัŽ", callback_data="menu_main")]]) if update.callback_query: await update.callback_query.edit_message_text(text, parse_mode="HTML", reply_markup=kb) else: await update.message.reply_text(text, parse_mode="HTML", reply_markup=kb) async def cmd_link(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None: if not update.effective_user or not _ok(update.effective_user.id): return info = await proxy_info() if not info: text = "โŒ ะŸั€ะพะบัะธ ะฝะต ะทะฐะฟัƒั‰ะตะฝ." else: text = f"ะกัั‹ะปะบะฐ:\n{info['link4']}" kb = InlineKeyboardMarkup([[InlineKeyboardButton("โ—€๏ธ ะœะตะฝัŽ", callback_data="menu_main")]]) if update.callback_query: await update.callback_query.edit_message_text(text, parse_mode="HTML", reply_markup=kb) else: await update.message.reply_text(text, parse_mode="HTML", reply_markup=kb) async def cmd_restart(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None: if not update.effective_user or not _ok(update.effective_user.id): return await sh("docker", "restart", CONTAINER_NAME) kb = InlineKeyboardMarkup([[InlineKeyboardButton("โ—€๏ธ ะœะตะฝัŽ", callback_data="menu_main")]]) if update.callback_query: await update.callback_query.edit_message_text("โœ… ะŸะตั€ะตะทะฐะฟัƒั‰ะตะฝะพ", reply_markup=kb) async def callback_handler(update: Update, ctx: ContextTypes.DEFAULT_TYPE) -> None: query = update.callback_query await query.answer() data = query.data if data == "menu_main": await start(update, ctx) elif data == "menu_status": await cmd_status(update, ctx) elif data == "menu_link": await cmd_link(update, ctx) elif data == "menu_restart": await cmd_restart(update, ctx) def main() -> None: if not BOT_TOKEN: return app = Application.builder().token(BOT_TOKEN).build() app.add_handler(CommandHandler("start", start)) app.add_handler(CallbackQueryHandler(callback_handler)) app.run_polling() if __name__ == "__main__": main() EOF chmod +x "$BOT_DIR/bot.py" } # โ”€โ”€ ะŸะพะบะฐะทะฐั‚ัŒ ะดะฐะฝะฝั‹ะต ะฟะพะดะบะปัŽั‡ะตะฝะธั โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ show_config() { if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then echo -e "${RED}ะŸั€ะพะบัะธ ะฝะต ะทะฐะฟัƒั‰ะตะฝ!${NC}"; return fi local DATA=$(cat "$BOT_DIR/proxy.json" 2>/dev/null) local PORT=$(echo "$DATA" | grep -oP '(?<="port": ")[^"]*') local SECRET=$(echo "$DATA" | grep -oP '(?<="secret": ")[^"]*') local IP4=$(get_ip4) local LINK="tg://proxy?server=$IP4&port=$PORT&secret=$SECRET" echo -e "\n${CYAN}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" echo -e " IPv4: ${WHITE}$IP4${NC}" echo -e " ะŸะพั€ั‚: ${WHITE}$PORT${NC} (TCP + UDP)" echo -e " Secret: ${WHITE}$SECRET${NC}" echo -e "\n ะกัั‹ะปะบะฐ: ${BLUE}$LINK${NC}" echo "" qrencode -t ANSIUTF8 "$LINK" } # โ”€โ”€ 1) ะฃัั‚ะฐะฝะพะฒะบะฐ MTProxy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ menu_install() { clear local domains=("google.com" "wikipedia.org" "github.com" "habr.com") echo -e "${CYAN}ะ’ั‹ะฑะตั€ะธั‚ะต ะดะพะผะตะฝ (Fake TLS):${NC}" for i in "${!domains[@]}"; do echo -e " ${YELLOW}$((i+1)))${NC} ${domains[$i]}"; done read -p "ะ’ั‹ะฑะพั€: " d_idx DOMAIN=${domains[$((d_idx-1))]:-google.com} local PORT=$(find_smart_port) optimize_system fix_firewall "$PORT" spinner_start "ะ—ะฐะฟัƒัะบ ะบะพะฝั‚ะตะนะฝะตั€ะฐ..." docker pull nineseconds/mtg:2 >/dev/null 2>&1 local SECRET=$(docker run --rm nineseconds/mtg:2 generate-secret --hex "$DOMAIN" 2>/dev/null) docker stop "$CONTAINER_NAME" &>/dev/null docker rm "$CONTAINER_NAME" &>/dev/null # ะ’ะะ–ะะž: ะฟั€ะพะฑั€ะพั UDP ะดะปั ะทะฒะพะฝะบะพะฒ docker run -d --name "$CONTAINER_NAME" --restart always \ -p "$PORT":"$PORT"/tcp \ -p "$PORT":"$PORT"/udp \ nineseconds/mtg:2 simple-run \ -n 1.1.1.1 -i prefer-ipv4 \ 0.0.0.0:"$PORT" "$SECRET" > /dev/null 2>&1 sleep 2 spinner_stop if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then mkdir -p "$BOT_DIR" echo "{\"domain\": \"$DOMAIN\", \"port\": \"$PORT\", \"secret\": \"$SECRET\"}" > "$BOT_DIR/proxy.json" echo -e "\n${GREEN}โœ“ SwiftGram ัƒัะฟะตัˆะฝะพ ะทะฐะฟัƒั‰ะตะฝ!${NC}" show_config fi read -p "ะะฐะถะผะธั‚ะต Enter..." } # โ”€โ”€ 3) ะะฐัั‚ั€ะพะนะบะฐ ะฑะพั‚ะฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ menu_setup_bot() { clear echo -e "${CYAN}ะะฐัั‚ั€ะพะนะบะฐ Telegram ะฑะพั‚ะฐ...${NC}" if ! command -v python3 &>/dev/null; then run_with_progress "Python3" install_pkg python3 python3-pip python3-venv; fi prepare_bot_source cd "$BOT_DIR" [ ! -d "venv" ] && python3 -m venv venv >/dev/null 2>&1 ./venv/bin/pip install -r requirements.txt -q read -p "ะ’ะฒะตะดะธั‚ะต BOT_TOKEN: " TOKEN read -p "ะ’ะฐัˆ Telegram ID: " ADMIN_ID { echo "BOT_TOKEN=$TOKEN" echo "ALLOWED_IDS=$ADMIN_ID" echo "CONTAINER_NAME=$CONTAINER_NAME" echo "CONFIG_PATH=$BOT_DIR/proxy.json" } > .env cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF [Unit] Description=SwiftGram Bot Service After=network.target docker.service [Service] Type=simple WorkingDirectory=$BOT_DIR ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py Restart=always [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now "$SERVICE_NAME" systemctl restart "$SERVICE_NAME" echo -e "\n${GREEN}โœ“ ะ‘ะพั‚ ะทะฐะฟัƒั‰ะตะฝ!${NC}" read -p "ะะฐะถะผะธั‚ะต Enter..." } # โ”€โ”€ ะ“ะปะฐะฒะฝั‹ะน ั†ะธะบะป โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ install_base_deps SELF="$(realpath "$0")" [ "$SELF" != "/usr/local/bin/swiftgram" ] && { cp "$SELF" /usr/local/bin/swiftgram; chmod +x /usr/local/bin/swiftgram; } while true; do clear echo -e "${MAGENTA}SWIFTGRAM MANAGER (UDP Fixed)${NC}" docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$" && echo -e " ะŸั€ะพะบัะธ: ${GREEN}ะ ะะ‘ะžะขะะ•ะข${NC}" || echo -e " ะŸั€ะพะบัะธ: ${RED}ะ’ะซะšะ›ะฎะงะ•ะ${NC}" echo -e "\n ${GREEN}1)${NC} ะฃัั‚ะฐะฝะพะฒะธั‚ัŒ / ะžะฑะฝะพะฒะธั‚ัŒ ะฟั€ะพะบัะธ\n ${GREEN}2)${NC} ะŸะพะบะฐะทะฐั‚ัŒ ะดะฐะฝะฝั‹ะต (QR)\n ${CYAN}3)${NC} ะะฐัั‚ั€ะพะธั‚ัŒ ะฑะพั‚ะฐ\n ${RED}6)${NC} ะฃะดะฐะปะธั‚ัŒ\n ${WHITE}0)${NC} ะ’ั‹ั…ะพะด" read -p "ะŸัƒะฝะบั‚: " m_idx case $m_idx in 1) menu_install ;; 2) clear; show_config; read -p "Enter..." ;; 3) menu_setup_bot ;; 6) docker stop "$CONTAINER_NAME" && docker rm "$CONTAINER_NAME"; rm -rf "$BOT_DIR"; exit 0 ;; 0) exit 0 ;; esac done