Files
ios-rule-script-subset/scripts/build_singbox_rules.py

104 lines
2.9 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SURGE_DIR = ROOT / "rule" / "Surge"
SINGBOX_DIR = ROOT / "rule" / "sing-box"
SINGBOX_DIR.mkdir(parents=True, exist_ok=True)
UPSTREAM_TARGETS = {
"Lan": "Lan.list",
"Apple": "Apple.list",
"OpenAI": "OpenAI.list",
"Gemini": "Gemini.list",
"Claude": "Claude.list",
"China": "China.list",
"ChinaIPs": "ChinaIPs.list",
"Proxy": "Proxy.list",
}
FIELD_MAP = {
"DOMAIN": "domain",
"DOMAIN-SUFFIX": "domain_suffix",
"DOMAIN-KEYWORD": "domain_keyword",
"DOMAIN-REGEX": "domain_regex",
"IP-CIDR": "ip_cidr",
"IP-CIDR6": "ip_cidr",
"PROCESS-NAME": "process_name",
}
def build_rule_set(entries: dict[str, list[str]]) -> dict:
rules = []
for field in ("domain", "domain_suffix", "domain_keyword", "domain_regex", "ip_cidr", "process_name"):
values = entries.get(field, [])
if values:
rules.append({field: values})
return {"version": 3, "rules": rules}
def parse_surge_list(path: Path) -> dict:
entries: dict[str, list[str]] = {}
for raw in path.read_text(encoding="utf-8").splitlines():
line = raw.strip()
if not line or line.startswith("#"):
continue
parts = [part.strip() for part in line.split(",")]
if len(parts) < 2:
continue
kind = parts[0].upper()
field = FIELD_MAP.get(kind)
if not field:
continue
value = parts[1]
if not value:
continue
entries.setdefault(field, [])
if value not in entries[field]:
entries[field].append(value)
return build_rule_set(entries)
MANUAL_RULESETS = {
"ManualBackHome": {"version": 3, "rules": [{"ip_cidr": ["192.168.10.0/24"]}]},
"ManualReject": {"version": 3, "rules": [{"domain": ["www.axure.com"]}]},
"ManualAI": {"version": 3, "rules": [{"domain_keyword": ["macked"]}]},
"ManualDirect": {
"version": 3,
"rules": [
{
"domain_suffix": [
"umeng.com",
"umsns.com",
"umindex.com",
"nice.com",
"apple.com",
"alicdn.com",
"qujiangkeji.com",
"banxueketang.com",
"doubj.cn",
"local",
]
}
],
},
}
for name, filename in UPSTREAM_TARGETS.items():
data = parse_surge_list(SURGE_DIR / filename)
(SINGBOX_DIR / f"{name}.json").write_text(
json.dumps(data, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)
for name, payload in MANUAL_RULESETS.items():
(SINGBOX_DIR / f"{name}.json").write_text(
json.dumps(payload, ensure_ascii=False, indent=2) + "\n",
encoding="utf-8",
)