import os import urllib.request import zipfile import json import subprocess import stat import sys import logging import time import re import signal logging.basicConfig(level=logging.INFO, format='%(message)s', handlers=[logging.StreamHandler(sys.stdout)]) # Блокируем сигналы остановки от панели def sig_handler(signum, frame): logging.info(f"⚠️ Панель попыталась остановить процесс (сигнал {signum}). Игнорируем!") signal.signal(signal.SIGINT, sig_handler) signal.signal(signal.SIGTERM, sig_handler) try: signal.signal(signal.SIGHUP, sig_handler) except AttributeError: pass XRAY_URL = "https://github.com/XTLS/Xray-core/releases/download/v1.8.24/Xray-linux-64.zip" CF_URL = "https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64" def setup_binaries(): if not os.path.exists("xray"): logging.info("📥 Скачивание Xray-core...") try: req = urllib.request.Request(XRAY_URL, headers={'User-Agent': 'Mozilla/5.0'}) with urllib.request.urlopen(req, timeout=15) as response, open("xray.zip", 'wb') as out_file: out_file.write(response.read()) with zipfile.ZipFile("xray.zip", 'r') as zip_ref: zip_ref.extractall(".") os.remove("xray.zip") os.chmod("xray", os.stat("xray").st_mode | stat.S_IEXEC) except Exception as e: logging.error(f"❌ Ошибка скачивания Xray: {e}") sys.exit(1) if not os.path.exists("cloudflared"): logging.info("📥 Скачивание Cloudflared...") try: req = urllib.request.Request(CF_URL, headers={'User-Agent': 'Mozilla/5.0'}) with urllib.request.urlopen(req, timeout=30) as response, open("cloudflared", 'wb') as out_file: out_file.write(response.read()) os.chmod("cloudflared", os.stat("cloudflared").st_mode | stat.S_IEXEC) except Exception as e: logging.error(f"❌ Ошибка скачивания Cloudflared: {e}") sys.exit(1) logging.info("✅ Все нужные файлы установлены!") def create_config(): # Xray слушает прямо на SERVER_PORT — как в оригинальном скрипте port = int(os.environ.get('SERVER_PORT', 11772)) client_id = "b831b87d-8153-4b65-9e6b-1f81d1134a41" ws_path = "/wisp" config = { "log": {"loglevel": "warning"}, "inbounds": [{ "port": port, "listen": "127.0.0.1", "protocol": "vless", "settings": { "clients": [{"id": client_id, "level": 0}], "decryption": "none" }, "streamSettings": { "network": "ws", "wsSettings": {"path": ws_path} } }], "outbounds": [{"protocol": "freedom"}] } with open("config.json", "w") as f: json.dump(config, f, indent=2) return client_id, port, ws_path def run_services(client_id, port, ws_path): # Запуск Xray в фоне (изолирован от сигналов панели) xray_proc = subprocess.Popen( ["./xray", "run", "-c", "config.json"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True ) # Запуск Cloudflared (изолирован от сигналов панели) cf_proc = subprocess.Popen( ["./cloudflared", "tunnel", "--url", f"http://127.0.0.1:{port}", "--no-autoupdate"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, start_new_session=True ) logging.info("⏳ Подключение к серверам Cloudflare... Ждем генерацию ссылки (обычно 10-20 секунд)") cf_url = None url_pattern = re.compile(r'https://[a-zA-Z0-9-]+\.trycloudflare\.com') start_time = time.time() # Читаем вывод CF пока не получим URL for line in iter(cf_proc.stdout.readline, ''): if cf_url is None: match = url_pattern.search(line) if match: cf_url = match.group(0) host = cf_url.replace('https://', '') vless_url = ( f"vless://{client_id}@{host}:443" f"?type=ws&security=tls&path={ws_path}" f"&host={host}&sni={host}#WispCF" ) logging.info("\n" + "=" * 65) logging.info("🚀 УСПЕХ! ОБРАТНЫЙ ТУННЕЛЬ ПОДНЯТ!") logging.info("=" * 65) logging.info("Скопируй ЭТУ ссылку и вставь в телефон (v2rayNG / V2Ray Tun):") logging.info(f"\n{vless_url}\n") logging.info("=" * 65) logging.info("Сервер работает стабильно. Фаервол Wispbyte обойден.") logging.info("Внимание: если перезагрузить сервер (Restart), ссылка поменяется!") break # URL получен — выходим из цикла, процессы НЕ убиваем elif time.time() - start_time > 20 and "INF" in line: logging.info(f"[CF] {line.strip()}") # После получения URL — просто держим скрипт живым # Xray и Cloudflared работают независимо в своих сессиях logging.info("💤 Основной процесс перешёл в режим ожидания (Xray и CF работают независимо)...") while True: # Проверяем раз в 30 секунд — живы ли процессы, если нет — перезапускаем time.sleep(30) if xray_proc.poll() is not None: logging.info("⚠️ Xray упал! Перезапускаем...") xray_proc = subprocess.Popen( ["./xray", "run", "-c", "config.json"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True ) if cf_proc.poll() is not None: logging.info("⚠️ Cloudflared упал! Перезапускаем...") cf_proc = subprocess.Popen( ["./cloudflared", "tunnel", "--url", f"http://127.0.0.1:{port}", "--no-autoupdate"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True ) if __name__ == "__main__": logging.info("🚀 Инициализация системы обхода (Cloudflare Tunnel)...") setup_binaries() client_id, port, ws_path = create_config() run_services(client_id, port, ws_path)