From 58db50ff5b659ee5db1dfc86e7311bf9ec4930fe Mon Sep 17 00:00:00 2001 From: sirmrak Date: Fri, 14 Aug 2026 14:29:21 +0300 Subject: [PATCH] Update get_vpn.py --- get_vpn.py | 298 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 211 insertions(+), 87 deletions(-) diff --git a/get_vpn.py b/get_vpn.py index abf0cc8..5026dbd 100644 --- a/get_vpn.py +++ b/get_vpn.py @@ -2,6 +2,12 @@ import os import sys import requests +try: + import yaml +except ImportError: + print("Ошибка: Отсутствует библиотека PyYAML. Установите её командой: pip install pyyaml") + sys.exit(1) + def fetch_and_prepare_configs(): # Забираем ссылку из секретов Gitea/GitHub @@ -26,106 +32,224 @@ def fetch_and_prepare_configs(): try: print("Отправка запроса к серверу VPN...") response = requests.get(sub_url, headers=headers, timeout=20) - - if response.status_code != 200: - print( - f"Ошибка сервера: Статус {response.status_code}. Проверьте ссылку-подписку." - ) - sys.exit(1) + response.raise_for_status() # Вызовет ошибку при статусе не 200 raw_content = response.text.strip() - # Проверяем, что в ответе реально есть сервера - if "proxies:" not in raw_content: - print( - "Ошибка: Сервер вернул YAML, но в нём отсутствует блок нод 'proxies:'." - ) - sys.exit(1) - # ФАЙЛ 1: Сохраняем оригинальный чистый конфиг от провайдера with open("clash.yaml", "w", encoding="utf-8") as f: f.write(raw_content) print("Оригинальный конфиг успешно сохранен в clash.yaml") - # ФАЙЛ 2: Создаем модифицированный конфиг для раздельного обхода - # Переводим режим работы в rule (по правилам) вместо global (весь трафик) - split_content = raw_content.replace("mode: global", "mode: rule") - - if "rules:" in split_content: - # Отрезаем старые правила провайдера (всё, что после 'rules:') - base_part = split_content.split("rules:")[0] - - # Формируем огромный расширенный список правил с разделением трафика - advanced_rules = """rules: - # 1. Локальные адреса и домашняя сеть (в обход VPN) - - DOMAIN-SUFFIX,local,DIRECT - - IP-CIDR,127.0.0.0/8,DIRECT - - IP-CIDR,172.16.0.0/12,DIRECT - - IP-CIDR,192.168.0.0/16,DIRECT - - IP-CIDR,10.0.0.0/8,DIRECT - - IP-CIDR,198.18.0.0/16,DIRECT - - # 2. Популярные заблокированные ресурсы и сервисы (через VPN) - - GEOSITE,onion,→ Remnawave - - GEOSITE,twitter,→ Remnawave - - GEOSITE,instagram,→ Remnawave - - GEOSITE,facebook,→ Remnawave - - GEOSITE,meta,→ Remnawave - - GEOSITE,youtube,→ Remnawave - - GEOSITE,netflix,→ Remnawave - - GEOSITE,openai,→ Remnawave - - GEOSITE,chatgpt,→ Remnawave - - GEOSITE,discord,→ Remnawave - - GEOSITE,protonmail,→ Remnawave - - GEOSITE,intel,→ Remnawave - - GEOSITE,amd,→ Remnawave - - GEOSITE,nvidia,→ Remnawave - - GEOSITE,spotify,→ Remnawave - - GEOSITE,linkedin,→ Remnawave - - GEOSITE,rutracker,→ Remnawave - - GEOSITE,notion,→ Remnawave - - GEOSITE,docker,→ Remnawave - - GEOSITE,medium,→ Remnawave - - GEOSITE,behance,→ Remnawave - - GEOSITE,pinterest,→ Remnawave - - GEOSITE,canva,→ Remnawave - - GEOSITE,epicgames,→ Remnawave - - # 3. Весь остальной зарубежный контент (по желанию, через VPN) - - GEOSITE,geolocation-!cn,→ Remnawave - - # 4. Российские государственные сайты, банки и крупные ресурсы (напрямую, без VPN) - - GEOSITE,win-russia,DIRECT - - GEOSITE,category-gov-ru,DIRECT - - GEOSITE,ru,DIRECT - - GEOSITE,yandex,DIRECT - - GEOSITE,vk,DIRECT - - GEOSITE,mailru,DIRECT - - GEOSITE,sberbank,DIRECT - - GEOSITE,tinkoff,DIRECT - - GEOSITE,kinopoisk,DIRECT - - GEOSITE,avito,DIRECT - - GEOIP,RU,DIRECT - - # 5. Финальное замыкающее правило: всё, что не подошло под списки выше, идет НАПРЯМУЮ - - MATCH,DIRECT -""" - # Склеиваем настройки и новые правила - full_split_yaml = base_part + advanced_rules - - with open("clash_split.yaml", "w", encoding="utf-8") as f: - f.write(full_split_yaml) - print("Раздельный конфиг успешно сохранен в clash_split.yaml") - else: - print( - "Ошибка: В исходном конфиге не найдена секция rules: для замены!" - ) + # Парсим YAML в словарь Python для безопасного редактирования + config = yaml.safe_load(raw_content) + if not isinstance(config, dict): + print("Ошибка: Не удалось распарсить YAML конфиг. Проверьте ссылку-подписку.") sys.exit(1) + # --------------------------------------------------------- + # 1. Базовые настройки + # --------------------------------------------------------- + # ВАЖНО: Для работы правил (раздельного обхода) режим должен быть 'rule', а не 'global' + config['mode'] = 'rule' + config['external-controller'] = '0.0.0.0:9090' + config['ipv6'] = False + config['keep-alive-idle'] = 30 + config['keep-alive-interval'] = 15 + + # --------------------------------------------------------- + # 2. Заменяем секцию DNS + # --------------------------------------------------------- + config['dns'] = { + 'enable': True, + 'use-hosts': True, + 'enhanced-mode': 'fake-ip', + 'fake-ip-range': '198.18.0.1/16', + 'default-nameserver': ['77.88.8.8', '8.8.8.8'], + 'nameserver': ['77.88.8.8', '77.88.8.1'], + 'fake-ip-filter': [ + "*.lan", "stun.*.*.*", "stun.*.*", "time.windows.com", + "time.nist.gov", "time.apple.com", "time.asia.apple.com", + "*.openwrt.pool.ntp.org", "pool.ntp.org", "ntp.ubuntu.com", + "time1.apple.com", "time2.apple.com", "time3.apple.com", + "time4.apple.com", "time5.apple.com", "time6.apple.com", + "time7.apple.com", "time1.google.com", "time2.google.com", + "time3.google.com", "time4.google.com", "api.joox.com", + "joox.com", "*.xiami.com", "*.msftconnecttest.com", + "*.msftncsi.com", "+.xboxlive.com", "*.*.stun.playstation.net", + "xbox.*.*.microsoft.com", "*.ipv6.microsoft.com", + "speedtest.cros.wr.pvp.net" + ] + } + + # --------------------------------------------------------- + # 3. Формируем группу Proxy (url-test) + # --------------------------------------------------------- + # Собираем все имена прокси из оригинального конфига + proxy_names = [] + if 'proxies' in config and isinstance(config['proxies'], list): + # Если есть верхнеуровневый список proxies (стандарт Clash) + proxy_names = [p.get('name') for p in config['proxies'] if isinstance(p, dict) and 'name' in p] + else: + # Фолбэк: собираем имена из существующих групп proxy-groups + for group in config.get('proxy-groups', []): + if isinstance(group, dict) and 'proxies' in group: + proxy_names.extend(group['proxies']) + # Убираем дубликаты, сохраняя порядок + proxy_names = list(dict.fromkeys(proxy_names)) + + new_proxy_group = { + 'name': 'Proxy', + 'type': 'url-test', + 'proxies': proxy_names, + 'url': 'http://www.gstatic.com/generate_204', + 'interval': 300, + 'tolerance': 50 + } + + # Заменяем старую главную группу (например, "→ Remnawave" или "PROXY") на новую + updated_groups = [] + replaced = False + for group in config.get('proxy-groups', []): + if isinstance(group, dict) and group.get('name') in ['→ Remnawave', 'PROXY', 'Proxy']: + updated_groups.append(new_proxy_group) + replaced = True + else: + updated_groups.append(group) + + # Если такой группы не было, добавляем её в начало + if not replaced: + updated_groups.insert(0, new_proxy_group) + + config['proxy-groups'] = updated_groups + + # --------------------------------------------------------- + # 4. Заменяем правила (rules) + # --------------------------------------------------------- + config['rules'] = [ + "RULE-SET,telegram,Proxy", + "RULE-SET,geosite-discord,Proxy", + "RULE-SET,discord-voice,Proxy", + "RULE-SET,geosite-whatsapp,Proxy", + "RULE-SET,geosite-viber,Proxy", + "RULE-SET,geosite-signal,Proxy", + "RULE-SET,geosite-youtube,Proxy", + "RULE-SET,geosite-twitter,Proxy", + "RULE-SET,geosite-facebook,Proxy", + "RULE-SET,geosite-instagram,Proxy", + "RULE-SET,geosite-openai,Proxy", + "RULE-SET,geosite-anthropic,Proxy", + "RULE-SET,geosite-google-gemini,Proxy", + "RULE-SET,geosite-xai,Proxy", + "RULE-SET,cdn-all,Proxy", + "DOMAIN-SUFFIX,com,Proxy", + "RULE-SET,ru-blocked,Proxy", + "GEOIP,RU,DIRECT", + "MATCH,DIRECT" + ] + + # --------------------------------------------------------- + # 5. Добавляем rule-providers + # --------------------------------------------------------- + config['rule-providers'] = { + 'geosite-discord': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/discord.yaml', + 'interval': 86400 + }, + 'geosite-whatsapp': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/whatsapp.yaml', + 'interval': 86400 + }, + 'geosite-viber': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/viber.yaml', + 'interval': 86400 + }, + 'geosite-signal': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/signal.yaml', + 'interval': 86400 + }, + 'geosite-youtube': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/youtube.yaml', + 'interval': 86400 + }, + 'geosite-twitter': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/twitter.yaml', + 'interval': 86400 + }, + 'geosite-facebook': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/facebook.yaml', + 'interval': 86400 + }, + 'geosite-instagram': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/instagram.yaml', + 'interval': 86400 + }, + 'geosite-openai': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/openai.yaml', + 'interval': 86400 + }, + 'geosite-anthropic': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/anthropic.yaml', + 'interval': 86400 + }, + 'geosite-google-gemini': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/google-gemini.yaml', + 'interval': 86400 + }, + 'geosite-xai': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/refs/heads/meta/geo/geosite/xai.yaml', + 'interval': 86400 + }, + 'cdn-all': { + 'behavior': 'ipcidr', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/123jjck/cdn-ip-ranges/refs/heads/main/cdn-only/cdn-only_plain_ipv4.txt', + 'interval': 86400, 'format': 'text' + }, + 'telegram': { + 'behavior': 'ipcidr', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/123jjck/cdn-ip-ranges/refs/heads/main/telegram/telegram_plain_ipv4.txt', + 'interval': 86400, 'format': 'text' + }, + 'discord-voice': { + 'behavior': 'ipcidr', 'type': 'http', + 'url': 'https://raw.githubusercontent.com/123jjck/cdn-ip-ranges/refs/heads/main/discord-voice/discord-voice_plain_ipv4.txt', + 'interval': 86400, 'format': 'text' + }, + 'ru-blocked': { + 'behavior': 'domain', 'type': 'http', + 'url': 'https://cdn.jsdelivr.net/gh/shvchk/unblock-net/lists/clash/ru-blocked', + 'interval': 86400, 'format': 'text' + } + } + + # ФАЙЛ 2: Сохраняем модифицированный конфиг + with open("clash_split.yaml", "w", encoding="utf-8") as f: + # sort_keys=False сохраняет исходный порядок ключей + # allow_unicode=True гарантирует, что эмодзи (🇩🇪, 🇷🇺) не превратятся в \u-последовательности + # width=1000 предотвращает нежелательный перенос длинных строк + yaml.dump(config, f, default_flow_style=False, sort_keys=False, allow_unicode=True, width=1000) + + print("Раздельный конфиг успешно сохранен в clash_split.yaml") + + except requests.exceptions.RequestException as e: + print(f"Ошибка сети: {e}") + sys.exit(1) except Exception as e: print(f"Критическая ошибка во время выполнения: {e}") sys.exit(1) if __name__ == "__main__": - fetch_and_prepare_configs() + fetch_and_prepare_configs() \ No newline at end of file