| 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(): |
| |
| 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_proc = subprocess.Popen( |
| ["./xray", "run", "-c", "config.json"], |
| stdout=subprocess.DEVNULL, |
| stderr=subprocess.DEVNULL, |
| start_new_session=True |
| ) |
|
|
| |
| 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() |
|
|
| |
| 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 |
| elif time.time() - start_time > 20 and "INF" in line: |
| logging.info(f"[CF] {line.strip()}") |
|
|
| |
| |
| logging.info("💤 Основной процесс перешёл в режим ожидания (Xray и CF работают независимо)...") |
| while True: |
| |
| 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) |
|
|