Skip to content

Instantly share code, notes, and snippets.

@sht0rmx
Created March 29, 2026 11:14
Show Gist options
  • Select an option

  • Save sht0rmx/332d02e008ff9383d77af607f1ca274d to your computer and use it in GitHub Desktop.

Select an option

Save sht0rmx/332d02e008ff9383d77af607f1ca274d to your computer and use it in GitHub Desktop.
parse vless links(txt file with one link per line) to clash meta (sing-box)
import re
import yaml
import urllib.parse
from pathlib import Path
import sys
import argparse
try:
parsed = urllib.parse.urlparse(vless_url)
uuid = parsed.username
server = parsed.hostname
port = parsed.port
name = urllib.parse.unquote(parsed.fragment) if parsed.fragment else f"VLESS {server}:{port}"
params = urllib.parse.parse_qs(parsed.query)
config = {
"name": name,
"type": "vless",
"server": server,
"port": int(port),
"uuid": uuid,
"network": params.get("type", ["tcp"])[0],
"udp": True,
"tls": True,
"flow": params.get("flow", [""])[0] or "",
"servername": params.get("sni", [""])[0] or "",
"client-fingerprint": params.get("fp", ["chrome"])[0]
}
if params.get("security", [""])[0] == "reality":
pbk = params.get("pbk", [""])[0]
sid = params.get("sid", [""])[0]
if pbk:
config["reality-opts"] = {
"public-key": pbk,
"short-id": sid or ""
}
config = {k: v for k, v in config.items() if v}
return config
except Exception as e:
print(f"❌ Ошибка парсинга {vless_url[:50]}...: {e}")
return None
def generate_full_config(proxies):
if proxies:
whitelist_proxy = proxies[0].copy()
whitelist_proxy["name"] = "✅ Whitelist"
proxies.insert(0, whitelist_proxy)
config = {
"mixed-port": 7890,
"allow-lan": True,
"enable-process": True,
"find-process-mode": "always",
"mode": "rule",
"log-level": "info",
"sniffer": {
"enable": True,
"sniff": {
"HTTP": {
"ports": ["80", "8080-8880"],
"override-destination": True
},
"TLS": {
"ports": ["443", "8443"],
"override-destination": False
},
"QUIC": {
"ports": ["443", "8443", "853"],
"override-destination": False
}
}
},
"tun": {
"enable": True,
"stack": "mixed",
"auto-route": True,
"strict-route": True,
"auto-redirect": True,
"auto-detect-interface": True
},
"hosts": {
"dns.google": ["8.8.8.8", "8.8.4.4"],
"one.one.one.one": ["1.1.1.1", "1.0.0.1"]
},
"dns": {
"enable": True,
"use-hosts": True,
"listen": "127.0.0.1:10853",
"enhanced-mode": "redir-host",
"default-nameserver": [
"https://1.0.0.1/dns-query",
"https://8.8.8.8/dns-query"
],
"proxy-server-nameserver": [
"https://one.one.one.one/dns-query",
"https://dns.google/dns-query"
],
"direct-nameserver": [
"https://one.one.one.one/dns-query",
"https://dns.google/dns-query"
],
"nameserver": [
"https://one.one.one.one/dns-query#Whitelist"
]
},
"rule-providers": {
"ru-blocked-all": {
"type": "http",
"behavior": "domain",
"format": "mrs",
"url": "https://github.com/frayZV/runetfreedom-mrs/releases/latest/download/ru-blocked-all.mrs",
"path": "./rule-sets/ru-blocked-all.mrs",
"interval": 86400,
"proxy": "Whitelist"
},
"refilter_ip": {
"type": "http",
"behavior": "ipcidr",
"format": "mrs",
"url": "https://raw.githubusercontent.com/runetfreedom/russia-blocked-geoip/release/mrs/re-filter.mrs",
"path": "./rule-sets/refilter_ip.mrs",
"interval": 86400,
"proxy": "Whitelist"
},
"runet_ip": {
"type": "http",
"behavior": "ipcidr",
"format": "mrs",
"url": "https://raw.githubusercontent.com/runetfreedom/russia-blocked-geoip/release/mrs/ru-blocked.mrs",
"path": "./rule-sets/runet_ip.mrs",
"interval": 86400,
"proxy": "Whitelist"
},
"runet_community_ip": {
"type": "http",
"behavior": "ipcidr",
"format": "mrs",
"url": "https://raw.githubusercontent.com/runetfreedom/russia-blocked-geoip/release/mrs/ru-blocked-community.mrs",
"path": "./rule-sets/runet_community_ip.mrs",
"interval": 86400,
"proxy": "Whitelist"
}
},
"proxies": proxies,
"proxy-groups": [
{
"name": "Whitelist",
"type": "select",
"proxies": [proxy["name"] for proxy in proxies]
}
],
"rules": [
"OR,((DOMAIN,ipwhois.app),(DOMAIN,ipwho.is),(DOMAIN,api.ip.sb),(DOMAIN,ipapi.co),(DOMAIN,ipinfo.io)),Whitelist",
"DOMAIN,www.google.com,Whitelist",
"RULE-SET,ru-blocked-all,Whitelist",
"PROCESS-NAME,RocketLeague.exe,Whitelist",
"PROCESS-NAME-REGEX,(?i)discord.*,Whitelist",
"RULE-SET,refilter_ip,Whitelist,no-resolve",
"RULE-SET,runet_ip,Whitelist,no-resolve",
"RULE-SET,runet_community_ip,Whitelist,no-resolve",
"DST-PORT,25,DIRECT",
"DST-PORT,110,DIRECT",
"DST-PORT,143,DIRECT",
"DST-PORT,465,DIRECT",
"DST-PORT,587,DIRECT",
"DST-PORT,993,DIRECT",
"DST-PORT,995,DIRECT",
"MATCH,DIRECT"
]
}
return config
def main():
parser = argparse.ArgumentParser(description="VLESS -> Clash Meta YAML парсер")
parser.add_argument("input", nargs="?", help="Входной TXT файл")
parser.add_argument("-o", "--output", help="Выходной YAML файл")
parser.add_argument("-v", "--verbose", action="store_true", help="Подробный вывод")
args = parser.parse_args()
input_file = args.input or "vless_links.txt"
output_file = args.output or "clash_config.yml"
if not Path(input_file).exists():
print(f"Файл {input_file} не найден!")
print("Создайте TXT файл с VLESS ссылками (по одной на строку)")
sys.exit(1)
print("Чтение VLESS ссылок...")
with open(input_file, 'r', encoding='utf-8') as f:
vless_urls = [line.strip() for line in f if line.strip()]
if not vless_urls:
print("Нет валидных VLESS ссылок!")
sys.exit(1)
print(f"Найдено ссылок: {len(vless_urls)}")
proxies = []
for i, url in enumerate(vless_urls, 1):
if args.verbose:
print(f"Парсинг {i}/{len(vless_urls)}: {url[:60]}...")
proxy = parse_vless_url(url)
if proxy:
proxies.append(proxy)
if not proxies:
print("Не удалось распарсить ни одной ссылки!")
sys.exit(1)
print(f"Успешно распарсено: {len(proxies)} прокси")
print("Генерация конфигурации...")
config = generate_full_config(proxies)
with open(output_file, 'w', encoding='utf-8') as f:
yaml.dump(config, f, default_flow_style=False, allow_unicode=True, sort_keys=False, indent=2)
print(f"Конфиг сохранен: {output_file}")
print(f"Прокси в группе Whitelist: {len(proxies)}")
print("\nГотово! Импортируйте clash_config.yml в Clash Meta")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment