Files
UFA-Clash/get_vpn.py
T
2026-08-14 15:38:36 +03:00

257 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
sub_url = os.getenv("VPN_SUB_URL")
if not sub_url:
print("Ошибка: Переменная окружения VPN_SUB_URL не задана!")
sys.exit(1)
# Имитируем заголовки оригинального ядра Mihomo (Clash.Meta) для OpenClash
headers = {
"User-Agent": "Mihomo/1.18.3 Clash.Meta OpenClash",
"Accept": "application/yaml, text/yaml, */*",
"Connection": "keep-alive",
}
# Принудительно заставляем панель выдать мета-конфиг
if "flag=" not in sub_url:
separator = "&" if "?" in sub_url else "?"
sub_url += f"{separator}flag=meta"
try:
print("Отправка запроса к серверу VPN...")
response = requests.get(sub_url, headers=headers, timeout=20)
response.raise_for_status() # Вызовет ошибку при статусе не 200
raw_content = response.text.strip()
# ФАЙЛ 1: Сохраняем оригинальный чистый конфиг от провайдера
with open("clash.yaml", "w", encoding="utf-8") as f:
f.write(raw_content)
print("Оригинальный конфиг успешно сохранен в clash.yaml")
# Парсим 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",
"DOMAIN,git.darkmode.duckdns.org,Proxy",
"RULE-SET,ru-blocked,Proxy",
"GEOIP,RU,DIRECT",
"DOMAIN,ivanovonews.ru,Proxy",
"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()