feat(clash): 新增 🐙 Github 分组,GitHub 流量独立分流
原先 GitHub 流量被切成三份:api.github.com 和 Copilot 系域名被 ai.list 带进 OpenAI&Anthropic,其余靠 ProxyGFWlist 的 DOMAIN-KEYWORD,github 落到 🌍 海外访问,kgithub.com 走 ChinaOnly 直连。现在把后两类收拢成独立分组。 - 新增 Rules/GitHub.list:30 条 DOMAIN-SUFFIX + 69 条 IP-CIDR + 2 条 IP-CIDR6 - 新增 generate_github_rules.py:从 api.github.com/meta 生成 IP 段, 用标记块只重写 IP 部分,手工补充的域名不会被覆盖 几个取舍: - ruleset 排在 ai.list 之后,Copilot 与 api.github.com 继续跟 Cursor/OpenAI 同出口,AI 体验不受影响 - 移除 DOMAIN-KEYWORD,github,否则会抢走 ChinaOnly.list 里的 kgithub.com - IP 段只取 git/web/api/packages/pages,排除 actions(6980 条)/codespaces, 那是 Azure 大段,会把非 GitHub 流量误判进代理组 - IP 规则全部带 no-resolve,用途仅是兜住 SSH 推拉(redir-host 下 sniffer 嗅探不到 SSH 域名);全为纯 IP-CIDR,不含 IP-ASN,避免 OpenClash 启动死锁 - 节点候选不放家宽:clone/release 属带宽消耗型,与 AI 组的防风控诉求相反 via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GNghjXGrjt64G32W7t6Bhu
This commit is contained in:
146
generate_github_rules.py
Normal file
146
generate_github_rules.py
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
从 GitHub 官方 meta API (https://api.github.com/meta) 拉取 IP 段,
|
||||
生成/更新 Rules/GitHub.list 中的 IP-CIDR 规则块。
|
||||
|
||||
域名规则由人工维护在标记块之外, 本脚本只重写标记块内的 IP 规则,
|
||||
不会覆盖手工补充的域名条目。
|
||||
"""
|
||||
|
||||
import ipaddress
|
||||
import json
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
META_URL = "https://api.github.com/meta"
|
||||
OUTPUT_FILE = Path("Rules/GitHub.list")
|
||||
|
||||
BEGIN_MARK = "# === BEGIN AUTO-GENERATED IP RULES (generate_github_rules.py) ==="
|
||||
END_MARK = "# === END AUTO-GENERATED IP RULES ==="
|
||||
|
||||
# 只取这几个服务的网段。
|
||||
# 刻意排除:
|
||||
# actions / actions_macos / codespaces —— 是 Azure 大段(actions 近 7000 条),
|
||||
# 会把大量非 GitHub 流量误判进代理组
|
||||
# copilot —— Copilot 归 AI 分组, 见 ACL4SSR_Online_Full_Mannix.ini 规则顺序
|
||||
# hooks / importer / github_enterprise_importer —— 入站或企业迁移用, 客户端不需要
|
||||
INCLUDED_KEYS = ["git", "web", "api", "packages", "pages"]
|
||||
|
||||
# 域名骨架, 仅在 Rules/GitHub.list 不存在时写入
|
||||
DEFAULT_DOMAIN_SECTION = """\
|
||||
# GitHub 全站规则
|
||||
# 域名部分基于 https://github.com/blackmatrix7/ios_rule_script rule/Clash/GitHub/GitHub.list
|
||||
# 本地改动:
|
||||
# - 移除 DOMAIN-KEYWORD,github —— 会抢走 ChinaOnly.list 里的 kgithub.com 等国内镜像
|
||||
# - githubcopilot.com / githubnext.com 保留作兜底, 实际由更靠前的 ai.list 命中并归入 AI 分组
|
||||
# IP 部分由 generate_github_rules.py 从 https://api.github.com/meta 生成, 见文件末尾标记块
|
||||
|
||||
DOMAIN-SUFFIX,atom.io
|
||||
DOMAIN-SUFFIX,dependabot.com
|
||||
DOMAIN-SUFFIX,ghcr.io
|
||||
DOMAIN-SUFFIX,git.io
|
||||
DOMAIN-SUFFIX,github-atom-io-herokuapp-com.freetls.fastly.net
|
||||
DOMAIN-SUFFIX,github-avatars.oss-cn-hongkong.aliyuncs.com
|
||||
DOMAIN-SUFFIX,github-cloud.s3.amazonaws.com
|
||||
DOMAIN-SUFFIX,github.blog
|
||||
DOMAIN-SUFFIX,github.com
|
||||
DOMAIN-SUFFIX,github.community
|
||||
DOMAIN-SUFFIX,github.dev
|
||||
DOMAIN-SUFFIX,github.io
|
||||
DOMAIN-SUFFIX,githubapp.com
|
||||
DOMAIN-SUFFIX,githubassets.com
|
||||
DOMAIN-SUFFIX,githubcopilot.com
|
||||
DOMAIN-SUFFIX,githubhackathon.com
|
||||
DOMAIN-SUFFIX,githubnext.com
|
||||
DOMAIN-SUFFIX,githubpreview.dev
|
||||
DOMAIN-SUFFIX,githubstatus.com
|
||||
DOMAIN-SUFFIX,githubuniverse.com
|
||||
DOMAIN-SUFFIX,githubusercontent.com
|
||||
DOMAIN-SUFFIX,myoctocat.com
|
||||
DOMAIN-SUFFIX,npm.community
|
||||
DOMAIN-SUFFIX,npmjs.com
|
||||
DOMAIN-SUFFIX,npmjs.org
|
||||
DOMAIN-SUFFIX,opensource.guide
|
||||
DOMAIN-SUFFIX,rawgit.com
|
||||
DOMAIN-SUFFIX,rawgithub.com
|
||||
DOMAIN-SUFFIX,repo.new
|
||||
DOMAIN-SUFFIX,thegithubshop.com
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def fetch_meta():
|
||||
"""拉取 GitHub meta API"""
|
||||
req = urllib.request.Request(META_URL, headers={"User-Agent": "curl/8"})
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.load(resp)
|
||||
|
||||
|
||||
def extract_networks(meta):
|
||||
"""按 INCLUDED_KEYS 取网段并去重, IPv4 / IPv6 分开返回"""
|
||||
v4, v6 = set(), set()
|
||||
|
||||
for key in INCLUDED_KEYS:
|
||||
for cidr in meta.get(key, []):
|
||||
try:
|
||||
net = ipaddress.ip_network(cidr, strict=False)
|
||||
except ValueError:
|
||||
print(f"跳过无法解析的网段: {key} -> {cidr}")
|
||||
continue
|
||||
(v4 if net.version == 4 else v6).add(net)
|
||||
|
||||
# collapse_addresses 会合并相邻/包含关系的网段, git/web/api 高度重叠
|
||||
return (
|
||||
sorted(ipaddress.collapse_addresses(v4)),
|
||||
sorted(ipaddress.collapse_addresses(v6)),
|
||||
)
|
||||
|
||||
|
||||
def build_ip_block(v4, v6):
|
||||
"""生成标记块内容。no-resolve 必须保留: 没有它, 每条 IP 规则都会触发一次
|
||||
DNS 解析, 拖慢整条规则链, 且这些规则的用途只是兜住已经是 IP 的连接
|
||||
(git@github.com 的 SSH 推拉在 redir-host 模式下嗅探不到域名)。"""
|
||||
lines = [
|
||||
BEGIN_MARK,
|
||||
f"# 来源: {META_URL} 的 {' / '.join(INCLUDED_KEYS)} 字段",
|
||||
"# 请勿手工编辑本块, 运行 python3 generate_github_rules.py 重新生成",
|
||||
"",
|
||||
]
|
||||
lines += [f"IP-CIDR,{net},no-resolve" for net in v4]
|
||||
if v6:
|
||||
lines.append("")
|
||||
lines += [f"IP-CIDR6,{net},no-resolve" for net in v6]
|
||||
lines += ["", END_MARK]
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def write_rules(ip_block, output_file):
|
||||
"""保留域名部分, 只替换标记块"""
|
||||
if output_file.exists():
|
||||
text = output_file.read_text(encoding="utf-8")
|
||||
else:
|
||||
text = DEFAULT_DOMAIN_SECTION
|
||||
|
||||
if BEGIN_MARK in text and END_MARK in text:
|
||||
head = text.split(BEGIN_MARK)[0]
|
||||
tail = text.split(END_MARK, 1)[1]
|
||||
text = head + ip_block + tail.lstrip("\n")
|
||||
else:
|
||||
text = text.rstrip("\n") + "\n\n" + ip_block
|
||||
|
||||
output_file.parent.mkdir(exist_ok=True)
|
||||
output_file.write_text(text, encoding="utf-8")
|
||||
|
||||
|
||||
def main():
|
||||
meta = fetch_meta()
|
||||
v4, v6 = extract_networks(meta)
|
||||
|
||||
print(f"合并后 IPv4 网段 {len(v4)} 条, IPv6 网段 {len(v6)} 条")
|
||||
|
||||
write_rules(build_ip_block(v4, v6), OUTPUT_FILE)
|
||||
print(f"已更新 {OUTPUT_FILE}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user