diff --git a/install.sh b/install.sh index c27f58e..3e62962 100644 --- a/install.sh +++ b/install.sh @@ -1,10 +1,6 @@ #!/bin/bash -# ๐Ÿš€ SwiftGram MTProxy โ€” Smart Modular Manager -# v1.0.5 โ€” Final Working Version (TCP + UDP Calls) -# ะŸะพะปะฝั‹ะน ั„ัƒะฝะบั†ะธะพะฝะฐะป, ะฑะตะท ั€ะตะบะปะฐะผั‹, ะธัะฟั€ะฐะฒะปะตะฝะฐ ะปะพะณะธะบะฐ ะฟะพั€ั‚ะพะฒ. - -# โ”€โ”€ ะะฐัั‚ั€ะพะนะบะธ ั€ะตะฟะพะทะธั‚ะพั€ะธั โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -REPO_RAW_URL="https://git.bargcraft.top/kobalt/swiftgram/raw/branch/main" +# ๐Ÿš€ SwiftGram MTProxy โ€” Smart Modular Manager (Self-contained) +# v1.1.0 โ€” ะคะธะบั ะทะฒะพะฝะบะพะฒ (UDP) + ะะฒั‚ะพะฝะพะผะฝั‹ะน ั€ะตะถะธะผ # โ”€โ”€ ะฆะฒะตั‚ะฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ RED='\033[0;31m' @@ -138,17 +134,6 @@ install_base_deps() { 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 ""; } -check_port() { - local port="$1" - if docker ps --format '{{.Names}}' 2>/dev/null | grep -q "^${CONTAINER_NAME}$"; then - local hp=$(docker inspect "$CONTAINER_NAME" --format='{{range $p,$c := .HostConfig.PortBindings}}{{(index $c 0).HostPort}} {{end}}' 2>/dev/null) - for p in $hp; do [ "$p" = "$port" ] && return 1; done - fi - local line=$(ss -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1) - [ -n "$line" ] && { echo "$line"; return 0; } - return 1 -} - find_smart_port() { local port=443 if ss -tlnp | grep -qE ":${port}\b"; then @@ -162,20 +147,221 @@ find_smart_port() { echo "$port" } -# โ”€โ”€ ะ˜ะฝั‚ะตะปะปะตะบั‚ัƒะฐะปัŒะฝั‹ะน ะฐะฝะฐะปะธะท ะดะพะผะตะฝะฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -analyze_best_domain() { - spinner_start "ะะฝะฐะปะธะท ะดะพะผะตะฝะพะฒ ะดะปั Fake TLS..." - local test_domains=("google.com" "wikipedia.org" "github.com" "habr.com" "microsoft.com") - local best="google.com" - local min=999 - for d in "${test_domains[@]}"; do - local t=$(ping -c 1 -W 1 "$d" 2>/dev/null | grep 'time=' | awk -F'time=' '{print $2}' | awk '{print $1}') - if [ -n "$t" ] && (( $(echo "$t < $min" | bc -l 2>/dev/null || [ ${t%.*} -lt ${min%.*} ]) )); then - min=$t; best=$d - fi - done - spinner_stop - echo "$best" +# โ”€โ”€ ะ’ัˆะธั‚ั‹ะต ะธัั…ะพะดะฝะธะบะธ ะฑะพั‚ะฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +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" } # โ”€โ”€ ะŸะพะบะฐะทะฐั‚ัŒ ะดะฐะฝะฝั‹ะต ะฟะพะดะบะปัŽั‡ะตะฝะธั โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @@ -187,14 +373,10 @@ show_config() { local PORT=$(echo "$DATA" | grep -oP '(?<="port": ")[^"]*') local SECRET=$(echo "$DATA" | grep -oP '(?<="secret": ")[^"]*') local IP4=$(get_ip4) - local IP6=$(get_ip6) local LINK="tg://proxy?server=$IP4&port=$PORT&secret=$SECRET" echo -e "\n${CYAN}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" - echo -e "${CYAN}โ•‘ ะ”ะะะะซะ• ะŸะžะ”ะšะ›ะฎะงะ•ะะ˜ะฏ โ•‘${NC}" - echo -e "${CYAN}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" echo -e " IPv4: ${WHITE}$IP4${NC}" - [ -n "$IP6" ] && echo -e " IPv6: ${WHITE}$IP6${NC}" echo -e " ะŸะพั€ั‚: ${WHITE}$PORT${NC} (TCP + UDP)" echo -e " Secret: ${WHITE}$SECRET${NC}" echo -e "\n ะกัั‹ะปะบะฐ: ${BLUE}$LINK${NC}" @@ -205,51 +387,24 @@ show_config() { # โ”€โ”€ 1) ะฃัั‚ะฐะฝะพะฒะบะฐ MTProxy โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ menu_install() { clear - echo -e "${CYAN}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" - echo -e "${CYAN}โ•‘ ะ’ั‹ะฑะตั€ะธั‚ะต ะดะพะผะตะฝ ะดะปั ะผะฐัะบะธั€ะพะฒะบะธ (Fake TLS) โ•‘${NC}" - echo -e "${CYAN}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" - - local 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" - "lenta.ru" "rbc.ru" "ria.ru" "kommersant.ru" - "stepik.org" "duolingo.com" "khanacademy.org" "ted.com" - ) - - local best_suggest=$(analyze_best_domain) - echo -e " ${GREEN}โ†’ ะ ะตะบะพะผะตะฝะดัƒะตะผั‹ะน ะดะพะผะตะฝ (ะปัƒั‡ัˆะธะน ะฟะธะฝะณ): $best_suggest${NC}\n" - - for i in "${!domains[@]}"; do - printf " ${YELLOW}%2d)${NC} %-22s" "$((i+1))" "${domains[$i]}" - [[ $(( (i+1) % 2 )) -eq 0 ]] && echo "" - done - echo -e "\n ${CYAN}21)${NC} ะ’ะฒะตัั‚ะธ ัะฒะพะน ะดะพะผะตะฝ" - - local d_idx DOMAIN - read -p "ะ’ะฐัˆ ะฒั‹ะฑะพั€ [1-21]: " d_idx - if [ "$d_idx" = "21" ]; then - read -p " ะ’ะฒะตะดะธั‚ะต ะดะพะผะตะฝ: " DOMAIN - DOMAIN=$(echo "$DOMAIN" | tr -d '[:space:]') - else - DOMAIN=${domains[$((d_idx-1))]} - fi - DOMAIN=${DOMAIN:-$best_suggest} + 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) - echo -e " ${GREEN}โœ“${NC} ะ˜ั‚ะพะณะพะฒั‹ะน ะฟะพั€ั‚: ${WHITE}$PORT${NC}" - optimize_system fix_firewall "$PORT" - spinner_start "ะ—ะฐะฟัƒัะบ ะบะพะฝั‚ะตะนะฝะตั€ะฐ (ะบะพะผะฐะฝะดะฐ ะพั€ะธะณะธะฝะฐะปะฐ)..." + 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 \ @@ -265,8 +420,6 @@ menu_install() { echo "{\"domain\": \"$DOMAIN\", \"port\": \"$PORT\", \"secret\": \"$SECRET\"}" > "$BOT_DIR/proxy.json" echo -e "\n${GREEN}โœ“ SwiftGram ัƒัะฟะตัˆะฝะพ ะทะฐะฟัƒั‰ะตะฝ!${NC}" show_config - else - echo -e "\n${RED}โœ— ะžัˆะธะฑะบะฐ ะทะฐะฟัƒัะบะฐ. ะŸั€ะพะฒะตั€ัŒั‚ะต: docker logs $CONTAINER_NAME${NC}" fi read -p "ะะฐะถะผะธั‚ะต Enter..." } @@ -274,27 +427,17 @@ menu_install() { # โ”€โ”€ 3) ะะฐัั‚ั€ะพะนะบะฐ ะฑะพั‚ะฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ menu_setup_bot() { clear - echo -e "${CYAN}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" - echo -e "${CYAN}โ•‘ ะะะกะขะ ะžะ™ะšะ TELEGRAM ะ‘ะžะขะ โ•‘${NC}" - echo -e "${CYAN}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" + 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 - if ! command -v python3 &>/dev/null; then run_with_progress "ะฃัั‚ะฐะฝะพะฒะบะฐ Python3" install_pkg python3 python3-pip python3-venv; fi - mkdir -p "$BOT_DIR" cd "$BOT_DIR" - - spinner_start "ะ—ะฐะณั€ัƒะทะบะฐ ะผะพะดัƒะปะตะน ะฑะพั‚ะฐ..." - curl -sL "$REPO_RAW_URL/requirements.txt" -o "requirements.txt" - curl -sL "$REPO_RAW_URL/bot.py" -o "bot.py" - spinner_stop - [ ! -d "venv" ] && python3 -m venv venv >/dev/null 2>&1 - ./venv/bin/pip install --upgrade pip -q ./venv/bin/pip install -r requirements.txt -q - echo -e "\n${YELLOW}ะ’ะฒะตะดะธั‚ะต BOT_TOKEN:${NC}" - read -r TOKEN - echo -e "${YELLOW}ะ’ะฐัˆ Telegram ID:${NC}" - read -r ADMIN_ID + read -p "ะ’ะฒะตะดะธั‚ะต BOT_TOKEN: " TOKEN + read -p "ะ’ะฐัˆ Telegram ID: " ADMIN_ID { echo "BOT_TOKEN=$TOKEN" @@ -302,7 +445,6 @@ menu_setup_bot() { echo "CONTAINER_NAME=$CONTAINER_NAME" echo "CONFIG_PATH=$BOT_DIR/proxy.json" } > .env - chmod 600 .env cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF [Unit] @@ -324,27 +466,6 @@ EOF read -p "ะะฐะถะผะธั‚ะต Enter..." } -# โ”€โ”€ 7) ะฃะดะฐะปะตะฝะธะต โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -menu_remove() { - clear - echo -e "${RED}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" - echo -e "${RED}โ•‘ ะฃะ”ะะ›ะ•ะะ˜ะ• SWIFTGRAM โ•‘${NC}" - echo -e "${RED}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" - read -p " ะฃะดะฐะปะธั‚ัŒ ะฟะพะปะฝะพัั‚ัŒัŽ? (y/N): " yn - [[ "$yn" != "y" ]] && return - local words=("ะฃะ”ะะ›ะ˜ะขะฌ" "SWIFTGRAM" "ะžะงะ˜ะกะขะšะ") - local confirm_word="${words[$((RANDOM % ${#words[@]}))]}" - echo -e " ะ’ะฒะตะดะธั‚ะต ัะปะพะฒะพ: ${WHITE}$confirm_word${NC}" - read -p " >>> " input_word - [[ "$input_word" != "$confirm_word" ]] && return - spinner_start "ะžั‡ะธัั‚ะบะฐ..." - docker stop "$CONTAINER_NAME" &>/dev/null; docker rm "$CONTAINER_NAME" &>/dev/null - systemctl stop "$SERVICE_NAME" 2>/dev/null; systemctl disable "$SERVICE_NAME" 2>/dev/null - rm -f "/etc/systemd/system/${SERVICE_NAME}.service"; rm -rf "$BOT_DIR"; rm -f /usr/local/bin/swiftgram - spinner_stop - echo -e "${GREEN}โœ“ ะกะธัั‚ะตะผะฐ ะพั‡ะธั‰ะตะฝะฐ.${NC}"; read -p "Enter..." -} - # โ”€โ”€ ะ“ะปะฐะฒะฝั‹ะน ั†ะธะบะป โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ install_base_deps SELF="$(realpath "$0")" @@ -352,17 +473,12 @@ SELF="$(realpath "$0")" while true; do clear - echo -e "${MAGENTA}โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—${NC}" - echo -e "${MAGENTA}โ•‘ SWIFTGRAM MANAGER (Professional) โ•‘${NC}" - echo -e "${MAGENTA}โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•${NC}" + echo -e "${MAGENTA}SWIFTGRAM MANAGER (UDP Fixed)${NC}" docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$" && echo -e " ะŸั€ะพะบัะธ: ${GREEN}ะ ะะ‘ะžะขะะ•ะข${NC}" || echo -e " ะŸั€ะพะบัะธ: ${RED}ะ’ะซะšะ›ะฎะงะ•ะ${NC}" - systemctl is-active --quiet "$SERVICE_NAME" && echo -e " ะ‘ะพั‚: ${GREEN}ะ ะะ‘ะžะขะะ•ะข${NC}" || echo -e " ะ‘ะพั‚: ${YELLOW}ะะ• ะะะกะขะ ะžะ•ะ${NC}" - echo -e "\n ${GREEN}1)${NC} ะฃัั‚ะฐะฝะพะฒะธั‚ัŒ / ะžะฑะฝะพะฒะธั‚ัŒ ะฟั€ะพะบัะธ\n ${GREEN}2)${NC} ะŸะพะบะฐะทะฐั‚ัŒ ะดะฐะฝะฝั‹ะต (QR)\n ${CYAN}3)${NC} ะะฐัั‚ั€ะพะธั‚ัŒ Telegram-ะฑะพั‚ะฐ\n ${GREEN}4)${NC} ะŸะตั€ะตะทะฐะฟัƒัั‚ะธั‚ัŒ ะฟั€ะพะบัะธ\n ${GREEN}5)${NC} ะ›ะพะณะธ ะฟั€ะพะบัะธ\n ${RED}6)${NC} ะฃะดะฐะปะธั‚ัŒ SwiftGram\n ${WHITE}0)${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 ;; - 4) docker restart "$CONTAINER_NAME"; echo "ะ“ะพั‚ะพะฒะพ"; sleep 1 ;; - 5) docker logs --tail 50 "$CONTAINER_NAME"; read -p "Enter..." ;; - 6) menu_remove ;; 0) exit 0 ;; + 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 \ No newline at end of file