diff --git a/.github/scripts/README.md b/.github/scripts/README.md index 6c8d7939a..ced8cc400 100644 --- a/.github/scripts/README.md +++ b/.github/scripts/README.md @@ -55,7 +55,7 @@ domain 语义转换:QX 展开为 `DOMAIN` / `DOMAIN-SUFFIX` 行、Clash 出 do ## `sync-config.py` — 配置文件同步 **源**:`Surge/Profile.conf` -**目标**:`Clash/Sample.yaml`、`Clash/Mihomo.yaml`、`Clash/Script/Script.js`、`Clash/Script/MyScript.js`、`Clash/Script/MyScriptColor.js`、`Clash/Script/MyClashBox.js`、`Surge/Balloon.lcf`(Loon)、`Quantumult/Sample.conf`、`Surge/Surfboard.conf`、`sing-box/config.json` +**目标**:`Clash/Sample.yaml`、`Clash/Mihomo.yaml`、`Clash/Script/Stash.stoverride`、`Clash/Script/MyStash.stoverride`、`Clash/Script/Script.js`、`Clash/Script/MyScript.js`、`Clash/Script/MyScriptColor.js`、`Clash/Script/MyClashBox.js`、`Surge/Balloon.lcf`(Loon)、`Quantumult/Sample.conf`、`Surge/Surfboard.conf`、`sing-box/config.json` 各平台静态头部由 `sync-config/` 下的 ini 文件提供(支持 `<< path` / `<< https://url` 引用)。sing-box 完整配置以 `sync-config/sing-box.ini`(JSON 内容)为静态基座——仅保留 `sniff`/`hijack-dns`(sing-box 专属基础设施,Surge 无等价规则);`route.rules`/`route.rule_set` 其余全部(含 QUIC 拦截、SSH 直连、私有网络、CN/geo、各服务分流)从 `[Rule]` 生成后 splice 进哨兵位——自有清单用本仓库 `.srs`,Loyalsoldier/VirgilClyne 等外部规则集映射到 SagerNet 官方等价规则集。 @@ -65,10 +65,55 @@ domain 语义转换:QX 展开为 `DOMAIN` / `DOMAIN-SUFFIX` 行、Clash 出 do `<<: *Region, filter: *Filter<码>`(`include-all-providers` 与 `use:` 同走 mihomo 保序路径,功能一致)。 +`Clash/Script/Stash.stoverride` 是 `Clash/Sample.yaml` 的二次转换产物,与 `Clash/Mihomo.yaml` +同一定位:整份配置逐行转录(含注释与排版),**只改写 Stash 与 mihomo 真正有差异的点**,因此 +可直接作为覆写文件导入 Stash 使用。差异点仅以下四类: + +- **省略 mihomo 专属的顶层键 / 整块**(连同其前置注释):监听与控制面(`mixed-port` / + `allow-lan` / `bind-address` / `external-controller`)、`ipv6`、geo 数据源(`geodata-loader` / + `geox-url` / `geo-auto-update` / `geo-update-interval`)、`unified-delay` / `tcp-concurrent` / + `find-process-mode` / `global-ua` / `keep-alive-interval`,以及 `profile` / `ntp` / `sniffer` / + `tun` 和空占位 `proxies`。这些能力在 Stash 中由客户端自身管理,或无对应项。 +- **DNS 子键过滤**:只保留 Stash 文档支持的 `default-nameserver` / `nameserver` / + `nameserver-policy` / `proxy-server-nameserver` / `fake-ip-filter`,其余 14 个 mihomo 专属键则予以省略 + (`enhanced-mode` / `fake-ip-range` / `cache-algorithm` / `direct-nameserver` 等)。 +- **DNS 写法转换**:mihomo 用每条 nameserver 的 `#RULES` 后缀表达「跟随规则」,Stash 为全局 + 开关 → 补 `follow-rule: true`,并在转译时去除该后缀(Stash 的 `#` 片段只承载 `h3=true` + 这类选项);`nameserver-policy` 里逗号拼接的多域名单键是 mihomo 专属, + 按 Stash 语法拆分为独立键(官方仅支持精确域名 / 通配域名 / `geosite:`)。 +- **Provider 处理**:`proxy-providers` 整块注释停用——它是本仓库自己的订阅,不适用于他人的 + 配置;内容已按 Stash 口径转换(移除 mihomo 专属的 `type`、`header` 改为文档拼写的 `headers`), + 取消注释即可启用。相应地,策略组的 `use: [Server]` 改为 `include-all: true`,直接从基础配置 + 的 `proxies` 中按 `filter` 取节点——否则组内无代理,会被 Stash 当作 `DIRECT` 处理。 +- **整体替换标记**:`hosts` / `dns` / `proxy-groups` / `rule-providers` / `rules` 均加 `#!replace`, + 使覆写以本文件为准;`proxies` 不输出,因此基础配置的节点原样保留。 + +其余内容——`hosts` / `mode` / `log-level`、23 个策略组(含地区 `filter`)、 +30 个规则集、36 条规则——全部原样保留:Stash 的规则类型是 Clash Premium 超集,我们用到的 +`RULE-SET` / `GEOIP` / `GEOSITE` / `MATCH` / `no-resolve`、`AND` / `OR` / `NOT` 逻辑规则(含嵌套) +及内置策略 `REJECT` / `REJECT-DROP`,官方文档均明确支持。 + +两点需留意:`GEOSITE` 的 domain-list-community 数据不随 Stash 分发,首次使用时按需从 github.com +拉取;`format: mrs` 的 MRS 支持有官方说明(限 `behavior` 为 `domain` / `ipcidr`,本仓库 8 个 mrs +规则集正好全在此范围内),但格式表未列出该 `format` 取值,沿用 mihomo 写法,需实测确认。 + +`Clash/Script/MyStash.stoverride` 是 Stash 的私人定制版,复用 `Enhanced/` 下的同一份 +overlay:只要 overlay 里除 `output` 外再声明一个 `stash_output`,就会在 Stash 基座上叠加同样 +的私人差异(目前只有 `myscript.overlay.json` 声明了)。overlay 的差异声明本身与输出格式无关, +但 `_apply_overlay` 面向解析后的结构、供 Script.js 使用,而 Stash 侧为文本级转译(需保留 +Sample.yaml 的注释与排版),因此这些指令在 `_stash_apply_overlay` 中按文本重新实现,**遇到尚未 +实现的指令直接报错**,避免私人差异被静默丢弃。 + +其中 `disabled_by_default` 没有静态等价物——它是 Script.js 的运行时开关(`ruleOptionsEnable`), +YAML 覆写没有「默认关但可开」这种状态。因此按声明**整组移除**:移除该组、以其为落点的规则,以及 +其余分组候选中对它的引用,并清理因此不再被任何 `RULE-SET` 引用的规则集。`extra_pool_groups` +的新增池组在 Script.js 里靠运行时过滤 `config.proxies` 填充,静态 YAML 必须显式写节点来源, +统一按基座地区组的写法输出 `include-all: true` + `filter`。 + `Clash/Script/Script.js` 是 `Clash/Mihomo.yaml` 生成完毕后再解析出来的等效 mihomo 覆写 脚本(Enhance Script),供 Clash Verge Rev / FlClash / Bettbox 等客户端直接对任意订阅动态生成同一套策略组 / 规则 / 基础设置,无需依赖本仓库自身的 proxy-providers。它只读 Mihomo.yaml 的解析结果、 -不重新实现转换逻辑,因此随 `Profile.conf` 改动自动同步,直接改动会被下次同步覆盖。地区组 / `🇺🇳 Server` +不重新实现转换逻辑,因此随 `Profile.conf` 改动自动同步,直接修改将在下次同步时被覆盖。地区组 / `🇺🇳 Server` 组不用 mihomo 的 `include-all` / `include-all-proxies`(它对候选节点做隐式字母序排序, 无开关可关,见 `_gen_clash_script_js` 注释):订阅里的内联节点由运行时按 `poolGroupFilters` 手动过滤 `config.proxies` 填入并保持订阅原始顺序;订阅只给 @@ -85,8 +130,8 @@ domain 语义转换:QX 展开为 `DOMAIN` / `DOMAIN-SUFFIX` 行、Clash 出 do 规则落点重定向、`remove_groups` 整组删除、`group_overrides` 类型/filter 覆盖、`group_proxies_insert` 候选节点插入、 `extra_pool_groups` 额外分组、`move_after` 调整展示顺序、`disabled_by_default` 让部分 分组默认关闭),因此公共部分(rules/rule-providers/基础设置、以及未被 overlay 覆盖的 -分组)随 `Profile.conf` 自动同步,私人差异集中改对应的 `*.overlay.json` 即可——直接改 -生成产物本体会被下次同步覆盖。overlay 还可以用 `extends: "<其他 overlay 文件名>"` 声明基于另一份 +分组)随 `Profile.conf` 自动同步,私人差异集中修改对应的 `*.overlay.json` 即可——直接修改 +生成产物本体将在下次同步时被覆盖。overlay 还可以用 `extends: "<其他 overlay 文件名>"` 声明基于另一份 已生成的 overlay 结果继续叠加(链式:`clashbox.overlay.json` extends `myscriptcolor.overlay.json` extends `myscript.overlay.json`,图标继承自 MyScriptColor), 只需要写与被继承者的差异,公共部分(地区 fallback、Relay 中转链等)不必重复声明。 diff --git a/.github/scripts/sync-config.py b/.github/scripts/sync-config.py index 85c7ec71a..c7e6f6e18 100644 --- a/.github/scripts/sync-config.py +++ b/.github/scripts/sync-config.py @@ -159,8 +159,8 @@ def _gen_clash_action_wrapper_groups( # --------------------------------------------------------------------------- _CST = timezone(timedelta(hours=8)) -# 冒号后的空格可选:loon/qx 头部经 _process_builtin_* 的 rstrip 会吃掉 -# 空占位 `# Date: ` 的尾空格变成 `# Date:`,仍需能匹配并打戳。 +# 冒号后的空格可选:loon/qx 头部经 _process_builtin_* 的 rstrip 会去除 +# 空占位 `# Date: ` 的尾空格变成 `# Date:`,仍需能匹配并写入时间戳。 _DATE_LINE_RE = re.compile(r"^# Date:.*$", re.MULTILINE) @@ -2800,8 +2800,8 @@ def _ordered_group(g: dict) -> dict: def _rule_policy_index(parts: list[str]) -> int: """Surge/Clash 规则行里策略字段的下标。`MATCH,POLICY` 策略在 index 1; - `AND/OR/NOT,(...),POLICY` 策略永远是最后一个逗号分段(拆括号里的逗号 - 也没关系,反正策略本身不含逗号,取 -1 仍然对);其余类型固定是 + `AND/OR/NOT,(...),POLICY` 策略始终为最后一个逗号分段(括号内的逗号 + 不影响判断:策略本身不含逗号,取 -1 仍然成立);其余类型固定是 `TYPE,VALUE,POLICY[,no-resolve]` 形式,策略在 index 2。 """ if parts[0] == "MATCH": @@ -2825,10 +2825,10 @@ def _apply_overlay( - rule_policy_redirect:把 rules 里以某分组为策略目标的行改指另一分组 ({旧落点: 新落点},用改名前的基座名字)。先于 remove_groups 执行, - 因此「删掉某组但保留其规则」可以两者搭配(如 📛 REJECT-DROP 组删掉、 + 因此「移除某组但保留其规则」可以两者搭配(如 📛 REJECT-DROP 组移除、 其规则落点改指 ⛔️ REJECT)。 - - remove_groups:整组删掉(如 📛 REJECT-DROP),同时从其余分组的 proxies 候选 - 里剔除对它的引用、删掉 rules 里以它为策略目标的行。 + - remove_groups:整组移除(如 📛 REJECT-DROP),同时从其余分组的 proxies 候选 + 里剔除对它的引用、移除 rules 中以其为策略目标的行。 - rename_map:批量改名({旧名: 新名}),同步更新其余分组 proxies 候选里的旧名 引用、pool_filters 的 key、以及 rules 里以该分组为策略目标的行,避免残留 指向旧名字的悬空引用。多个 overlay 之间要做同一批改名时用这个,而不是在 @@ -3078,7 +3078,7 @@ def _gen_mihomo_yaml(sample_yaml_text: str) -> str: "# Date: ", "# Author: @HotKids", "#", - "# 自动生成(sync-config.py 从 Clash/Sample.yaml 转译),请勿手改;改内容请改 Surge/Profile.conf。", + "# 自动生成(sync-config.py 从 Clash/Sample.yaml 转译),请勿手动修改;如需调整请修改 Surge/Profile.conf。", "", ] @@ -3220,7 +3220,7 @@ def _gen_clash_script_js( # 结构性池组(Server + 地区,均来自 Sample.yaml 的 use:[Server],或链式继承自 # base_state)——这些没有直接对应的 RULE-SET 目标,不纳入可选开关。overlay 的 # extra_pool_groups 新增的同样是结构性的(Relay 链 / 新地区)。但 group_overrides - # 给既有分组(如 📧 Mail)追加 filter 只是让它"顺带拿到全部节点",不改变它本来是 + # 给既有分组(如 📧 Mail)追加 filter 只是使其一并纳入全部节点,不改变它本来是 # 个可开关的功能分组这件事,因此不计入本集合。 if overlay: _apply_overlay(groups, pool_filters, rules, structural_pool_names, overlay, overlay_label) @@ -3259,17 +3259,17 @@ def _gen_clash_script_js( if overlay: source_lines = [ - " * 自动生成,请勿手改:由 sync-config.py 从 Surge/Profile.conf(经", + " * 自动生成,请勿手动修改:由 sync-config.py 从 Surge/Profile.conf(经", f" * Clash/Mihomo.yaml)叠加 sync-config/Enhanced/{overlay_label}(私人差异声明)", - " * 而来,直接改本文件会在下次同步时被覆盖。公共部分请改 Surge/Profile.conf;", + " * 而来,直接修改本文件将在下次同步时被覆盖。公共部分请修改 Surge/Profile.conf;", " * 私人差异(改名 / 换图标 / 额外分组 / 分组类型 / 候选节点 / 默认开关等)", f" * 请改 {overlay_label}。", ] else: source_lines = [ - " * 自动生成,请勿手改:由 sync-config.py 从 Surge/Profile.conf(经", - " * Clash/Mihomo.yaml)转译而来,直接改本文件会在下次同步时被覆盖;", - " * 要改内容请改 Surge/Profile.conf。", + " * 自动生成,请勿手动修改:由 sync-config.py 从 Surge/Profile.conf(经", + " * Clash/Mihomo.yaml)转译而来,直接修改本文件将在下次同步时被覆盖;", + " * 如需调整请修改 Surge/Profile.conf。", ] lines = [ @@ -3833,7 +3833,7 @@ def _sync_surfboard( SB_SKIP_GROUP_KW = ("Gateway", "Apple TV") # 各地区示例节点(占位用途:sing-box 无订阅机制,先内置一份可直接连通的示例 -# Shadowsocks 节点,方便直接改 server/password 试用;真实使用请用订阅工具替换) +# Shadowsocks 节点,便于直接修改 server/password 试用;真实使用请用订阅工具替换) # 按地区组 policy-regex-filter 命中的关键词匹配,与本仓库 Profile.conf 的固定 5 个地区一一对应 SB_EXAMPLE_NODES = { "HK": ("🇭🇰 HK", "hk.hotkids.me"), @@ -4111,6 +4111,456 @@ def _sync_singbox(config: dict, group_lines: list[str], rule_lines: list[str]) - print(f" {'✓ sing-box/config.json 已更新' if changed else '✓ sing-box/config.json 无变化'}") +# --------------------------------------------------------------------------- +# Stash 覆写(.stoverride) +# --------------------------------------------------------------------------- +# +# Clash/Stash.stoverride 是 Clash/Sample.yaml 的二次转换产物(与 Clash/Mihomo.yaml +# 同一定位):整份配置原样转录,只在 Stash 与 mihomo 真正有差异的点上改写,因此 +# 可以直接作为覆写文件导入 Stash 使用。差异点仅以下四类,其余逐行转录(含注释与排版)。 + +# 1) mihomo 专属的顶层键 / 整块——Stash 文档中不存在,且多为 Stash 由客户端自身管理 +# 的能力(监听端口、TUN、嗅探、geo 数据源等),连同其前置注释一并省略。 +_STASH_DROP_TOP = { + "mixed-port", "allow-lan", "bind-address", "ipv6", "external-controller", + "unified-delay", "tcp-concurrent", "find-process-mode", "geodata-loader", + "global-ua", "keep-alive-interval", "geo-auto-update", "geo-update-interval", + "geox-url", "profile", "ntp", "sniffer", "tun", "proxies", +} + +# 2) dns 块内 Stash 支持的子键(其余为 mihomo 专属,予以省略) +_STASH_REPLACE_TOP = {"hosts", "dns", "proxy-providers", "proxy-groups", + "rule-providers", "rules"} + +_STASH_DNS_KEEP = { + "default-nameserver", "nameserver", "nameserver-policy", + "proxy-server-nameserver", "fake-ip-filter", +} + +_TOP_KEY_RE = re.compile(r"^([A-Za-z][\w-]*):") +_SUB_KEY_RE = re.compile(r"^(\s+)(['\"]?)([^:'\"]+)\2\s*:") + + +def _yq(value) -> str: + """YAML 单引号标量(组名 / filter 正则含 emoji、空格、反斜杠,统一加引号最为稳妥)。""" + return "'" + str(value).replace("'", "''") + "'" + + +def _stash_clean_nameserver(server: str) -> str: + """mihomo 的 nameserver 策略后缀(#RULES / #策略名)在 Stash 中不存在——Stash 的 + `#` 片段只承载选项(如 h3=true)。保留 h3= 这类合法选项,其余后缀一律去除。""" + if "#" not in server: + return server + base, frag = server.split("#", 1) + return server if frag.startswith("h3=") else base + + +def _stash_comment_out(lines: list[str], top_key: str) -> list[str]: + """将某个顶层块整体注释掉(内容保留,需要时取消注释即可启用)。""" + start = next((i for i, l in enumerate(lines) if re.match(rf"^{re.escape(top_key)}:", l)), -1) + if start < 0: + return lines + end = next((i for i in range(start + 1, len(lines)) + if lines[i] and not lines[i].startswith((" ", "#"))), len(lines)) + out = list(lines) + for i in range(start, end): + if out[i].strip(): + # 该块整体停用,无需保留 #!replace 标记 + out[i] = "# " + out[i].replace(" #!replace", "") + return out + + +def _sync_stash(config: dict) -> None: + """Clash/Sample.yaml → Clash/Stash.stoverride(只改 Stash 与 mihomo 的差异点)。""" + out_path = config.get("Stash", {}).get("output") + clash_out = config.get("Clash", {}).get("output") + if not out_path or not clash_out: + return + base_path = REPO_ROOT / clash_out + if not base_path.exists(): + return + + print("\n── sync-config: Clash Sample.yaml → Stash .stoverride ──") + src = base_path.read_text(encoding="utf-8").splitlines() + + out: list[str] = [] + buf: list[str] = [] # 待决的注释 / 空行(跟随其后的键一起保留或丢弃) + top = "" # 当前顶层键 + keep_top = True + dns_keep = True # dns 块内当前子键是否保留 + policy_split: list[str] | None = None # 逗号拼接键待展开的域名 + policy_val: list[str] = [] # 该键的值行 + skip_use_items = False # use: 的列表项(已换成 include-all) + changes: list[str] = [] + + def flush() -> None: + # 省略某个键时,其两侧的空行会在删除处相邻叠加;此处舍弃与已输出空行相邻的 + # 前导空行,避免出现源文件没有的空行堆积(源本身的空行结构保持不变)。 + while buf and not buf[0].strip() and out and not out[-1].strip(): + buf.pop(0) + out.extend(buf) + buf.clear() + + # 文件头(首个顶层键之前的注释/空行,含 # Clash / # Date / # Author / # 通用设置) + # 始终保留:其不属于任何键,不应随被省略的首个键一同丢失。 + first_key = next((i for i, l in enumerate(src) if _TOP_KEY_RE.match(l)), 0) + header = [l.rstrip() for l in src[:first_key]] + if header and header[0].startswith("# Clash"): + del header[0] + header = [l for l in header if not l.startswith("# Author:")] + # 紧贴首个键的那段注释是该键的说明(首个键必为被省略的 mixed-port), + # 一并省略,避免遗留无主注释;靠空行分隔的分区标题(# 通用设置)保留。 + while header and header[-1].lstrip().startswith("#"): + header.pop() + out.extend(header) + + for raw in src[first_key:]: + line = raw.rstrip() + stripped = line.strip() + + # ── 逗号拼接的 nameserver-policy 键:收集值行后按域名展开 ── + if policy_split is not None: + if stripped and not stripped.startswith("#") and len(line) - len(line.lstrip()) >= 6: + policy_val.append(line) + continue + for dom in policy_split: + out.append(f' "{dom}":') + out.extend(policy_val) + policy_split, policy_val = None, [] + # 继续按普通行处理当前行 + + m_top = _TOP_KEY_RE.match(line) + if m_top: + top = m_top.group(1) + keep_top = top not in _STASH_DROP_TOP + if not keep_top: + buf.clear() + changes.append(f"略去 {top}") + continue + flush() + out.append(f"{line} #!replace" if top in _STASH_REPLACE_TOP else line) + if top == "dns": + # mihomo 用每条 nameserver 的 #RULES 后缀表达「DNS 跟随规则」, + # Stash 的等价物是全局开关 follow-rule。 + out += [ + " # DNS 查询跟随规则出站(mihomo 用 nameserver 的 #RULES 后缀表达,", + " # Stash 为全局开关)。官方提示多数场景无需开启:DNS 经代理转发可能", + " # 破坏云服务商 CDN 优化并轻微增加延迟;如需 DNS 直连,将其改为 false。", + " # 下方 proxy-server-nameserver 已为代理服务器域名提供独立解析,", + " # 满足官方要求的前置条件之一(避免递归查询)。", + " follow-rule: true", + ] + changes.append("dns: #RULES → follow-rule") + continue + + if not stripped or stripped.startswith("#"): + buf.append(line) + continue + + if not keep_top: + buf.clear() + continue + + indent = len(line) - len(line.lstrip()) + m_sub = _SUB_KEY_RE.match(line) + + # use 引用的是本仓库自己的 provider,而它在 Stash 产物里已停用; + # 改用 include-all 从基础配置的 proxies 取节点(filter 仍照常生效)。 + if skip_use_items: + if indent >= 6 and not m_sub: + continue + skip_use_items = False + if top == "proxy-groups" and indent == 4 and m_sub and m_sub.group(3).strip() == "use": + flush() + out.append(" include-all: true") + skip_use_items = True + changes.append("proxy-groups: use → include-all") + continue + + # ── dns:按 Stash 支持的子键过滤 ── + if top == "dns" and indent == 2 and m_sub: + key = m_sub.group(3).strip() + dns_keep = key in _STASH_DNS_KEEP + if not dns_keep: + buf.clear() + continue + flush() + out.append(line) + continue + if top == "dns" and indent > 2 and not dns_keep: + buf.clear() + continue + + # nameserver 条目:去除 #RULES 后缀 + if top == "dns" and dns_keep and stripped.startswith("- ") and "#RULES" in line: + flush() + val = stripped[2:].strip().strip("'\"") + out.append(f' - "{_stash_clean_nameserver(val)}"') + changes.append("nameserver 去 #RULES 后缀") + continue + + # nameserver-policy:逗号拼接多域名的单键是 mihomo 专属;Stash 只认 + # 「精确域名 / 通配域名 / geosite:」,拼接键将被视作字面域名,无法命中。 + if top == "dns" and indent == 4 and m_sub and "," in m_sub.group(3): + flush() + policy_split = [d.strip() for d in m_sub.group(3).split(",") if d.strip()] + policy_val = [] + changes.append(f"nameserver-policy 拆键 ×{len(policy_split)}") + continue + + # ── provider:type 是 mihomo 专属;header 在 Stash 中为 headers ── + if top in ("proxy-providers", "rule-providers"): + key = m_sub.group(3).strip() if m_sub else "" + if key == "type": + buf.clear() + continue + if key == "header": + flush() + out.append(line.replace("header:", "headers:", 1)) + changes.append("proxy-providers: header → headers") + continue + + flush() + out.append(line) + + if policy_split is not None: + for dom in policy_split: + out.append(f' "{dom}":') + out.extend(policy_val) + + # 在文件头的 # Author 之后补一行生成说明(覆写的 name/desc 仅用于展示, + # 源文件没有这些键,不属于差异点,不自行添加) + insert_at = next((i for i, l in enumerate(out) if l.startswith("# Date:")), -1) + 1 + out[insert_at:insert_at] = [ + "", + # name / desc / author 仅用于在 Stash 覆写列表中展示 + f"name: {Path(out_path).stem} for Android", + "desc: 自动生成(sync-config.py 从 Clash/Sample.yaml 转译),请勿手动修改;如需调整请修改 Surge/Profile.conf。", + "author: '@HotKids'", + 'icon: "https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Want.png"', + ] + + out = _stash_comment_out(out, "proxy-providers") + + body = "\n".join(out).rstrip() + "\n" + changed = _write_stamped_if_changed(REPO_ROOT / out_path, body) + for note in dict.fromkeys(changes): + print(f" · {note}") + print(f" {'✓ ' + out_path + ' 已更新' if changed else '✓ ' + out_path + ' 无变化'}") + + _sync_stash_overlays(out) + + +# ── Enhanced/*.overlay.json → Stash 私人定制版 ──────────────────────────── +# +# overlay 声明的是「相对基座的私人差异」,本身与输出格式无关(_apply_overlay 面向 +# 解析后的 groups/rules 结构,供 Script.js 使用)。Stash 侧为文本级转译(需保留 +# Sample.yaml 的注释与排版),因此这里按同一份 overlay 在文本层实现对应改写。 +# 只有声明了 stash_output 的 overlay 才会产出 .stoverride;未实现的指令直接报错, +# 避免私人差异被静默丢弃。 +_STASH_OVERLAY_OK = { + "_comment", "output", "stash_output", "extends", + "disabled_by_default", "rules_insert", "group_overrides", + "group_proxies_insert", "extra_pool_groups", +} + + +def _stash_group_spans(lines: list[str]) -> dict[str, tuple[int, int]]: + """定位 proxy-groups 块内每个组的行区间 {组名: (起, 止)}(止为开区间)。""" + # 键行可能带 #!replace 行内标记,按前缀匹配而不是全等 + start = next((i for i, l in enumerate(lines) if re.match(r"^proxy-groups:", l)), -1) + if start < 0: + return {} + end = next((i for i in range(start + 1, len(lines)) + if lines[i] and not lines[i].startswith((" ", "#"))), len(lines)) + spans: dict[str, tuple[int, int]] = {} + cur, cur_start = None, None + for i in range(start + 1, end): + m = re.match(r"^ - name:\s*(.+?)\s*$", lines[i]) + if m: + if cur is not None: + spans[cur] = (cur_start, i) + cur, cur_start = m.group(1).strip().strip("'\""), i + if cur is not None: + spans[cur] = (cur_start, end) + return spans + + +def _stash_render_group(g: dict) -> list[str]: + """按 Sample.yaml 的引号风格与字段顺序渲染一个新增策略组。 + + 源风格:name 用双引号、proxies 条目不加引号、filter 用单引号; + 字段顺序 name → type → icon → hidden → use/proxies → 测速参数 → filter。 + """ + out = [f' - name: "{g["name"]}"', f" type: {g.get('type', 'select')}"] + if g.get("icon"): + out.append(f" icon: {g['icon']}") + if g.get("hidden"): + out.append(" hidden: true") + # 池组的节点来源:与基座地区组写法一致,从基础配置的 proxies 中按 filter 筛选 + out.append(" include-all: true") + for key in ("interval", "tolerance", "lazy"): + if key in g: + out.append(f" {key}: {g[key]}") + if g.get("filter"): + out.append(f" filter: {_yq(g['filter'])}") + out.append("") # 组间空行,与 Sample.yaml 一致 + return out + + +# 组内字段的规范顺序(与 Sample.yaml 一致),group_overrides 新增字段时按此定位 +_STASH_FIELD_ORDER = ["name", "type", "icon", "hidden", "include-all", "use", "proxies", + "interval", "tolerance", "lazy", "filter"] + + +def _stash_apply_overlay(lines: list[str], overlay: dict, label: str) -> list[str]: + """把一份 overlay 的差异叠加到已转译好的 Stash 文本上(就地返回新列表)。""" + unknown = set(overlay) - _STASH_OVERLAY_OK + if unknown: + raise ValueError( + f"{label}: Stash 转译尚未实现这些 overlay 指令 {sorted(unknown)};" + f"请在 _stash_apply_overlay 中补齐,避免私人差异被静默丢弃" + ) + lines = list(lines) + notes: list[str] = [] + + # 展示字段替换为本定制版专属内容,避免与基座在覆写列表中同名 + for i, l in enumerate(lines): + if l.startswith("name: "): + lines[i] = f"name: {Path(overlay['stash_output']).stem} for Android" + elif l.startswith("desc: "): + lines[i] = (f"desc: 自动生成(sync-config.py 从 Clash/Sample.yaml 转译," + f"叠加 {label}),请勿手动修改;如需调整请修改 Surge/Profile.conf。") + + # 1) group_overrides:改写既有组的字段(filter 为 null 表示移除该行) + for name, patch in (overlay.get("group_overrides") or {}).items(): + span = _stash_group_spans(lines).get(name) + if span is None: + raise ValueError(f"{label}: group_overrides 引用了不存在的分组 {name!r}") + s, e = span + for key, val in patch.items(): + idx = next((i for i in range(s, e) + if re.match(rf"^ {re.escape(key)}:", lines[i])), None) + if val is None: + if idx is not None: + del lines[idx] + continue + rendered = (f" {key}: {_yq(val)}" if key == "filter" + else f" {key}: {'true' if val is True else val}") + if idx is not None: + lines[idx] = rendered + else: + # 按 Sample.yaml 的字段顺序插到第一个「应排在它之后」的字段前 + rank = _STASH_FIELD_ORDER.index(key) if key in _STASH_FIELD_ORDER else len(_STASH_FIELD_ORDER) + at = e + for i in range(s, e): + m2 = re.match(r"^ ([\w-]+):", lines[i]) + if m2 and m2.group(1) in _STASH_FIELD_ORDER \ + and _STASH_FIELD_ORDER.index(m2.group(1)) > rank: + at = i + break + lines.insert(at, rendered) + notes.append(f"{name}: 覆盖 {'/'.join(patch)}") + + # 2) group_proxies_insert:在候选列表里紧邻锚点插入 + for name, spec in (overlay.get("group_proxies_insert") or {}).items(): + span = _stash_group_spans(lines).get(name) + if span is None: + raise ValueError(f"{label}: group_proxies_insert 引用了不存在的分组 {name!r}") + s, e = span + anchor = spec.get("after") or spec.get("before") + idx = next((i for i in range(s, e) + if lines[i].strip().strip("-").strip().strip("'\"") == anchor), None) + if idx is None: + raise ValueError(f"{label}: {name} 的候选里找不到锚点 {anchor!r}") + at = idx + 1 if spec.get("after") else idx + lines[at:at] = [f" - {p}" for p in spec["insert"]] + notes.append(f"{name}: 候选插入 {len(spec['insert'])} 项") + + # 3) extra_pool_groups:整组新增,插入至锚点组之后 + for g in (overlay.get("extra_pool_groups") or []): + spans = _stash_group_spans(lines) + anchor = g.get("insert_after") + if anchor not in spans: + raise ValueError(f"{label}: extra_pool_groups 的锚点分组 {anchor!r} 不存在") + at = spans[anchor][1] + lines[at:at] = _stash_render_group(g) + notes.append(f"新增分组 {g['name']}") + + # 4) rules_insert:在锚点规则前/后插入 + for spec in (overlay.get("rules_insert") or []): + anchor = spec.get("after") or spec.get("before") + idx = next((i for i, l in enumerate(lines) + if l.startswith(" - ") and anchor in l), None) + if idx is None: + raise ValueError(f"{label}: rules_insert 找不到锚点规则 {anchor!r}") + at = idx + 1 if spec.get("after") else idx + lines[at:at] = [f" - {r}" for r in spec["rules"]] + notes.append(f"规则插入 {len(spec['rules'])} 条") + + # 5) disabled_by_default:静态配置没有运行时开关,按声明整组移除—— + # 移除该组、以其为落点的规则,以及其余组候选中对它的引用, + # 并清理因此不再被任何 RULE-SET 引用的规则集。 + for name in (overlay.get("disabled_by_default") or []): + spans = _stash_group_spans(lines) + if name not in spans: + raise ValueError(f"{label}: disabled_by_default 引用了不存在的分组 {name!r}") + s, e = spans[name] + # 组前的注释行一并移除 + while s > 0 and lines[s - 1].lstrip().startswith("#"): + s -= 1 + del lines[s:e] + lines = [l for l in lines + if not (l.startswith(" - ") and l.rstrip().endswith(name)) + and not (l.strip().startswith("- ") and l.strip().strip("-").strip().strip("'\"") == name)] + notes.append(f"移除分组 {name}(含其规则与候选引用)") + + # 清理不再被引用的规则集 + used = {m.group(1) for l in lines if (m := re.match(r"^ - RULE-SET,([^,]+),", l))} + rp = next((i for i, l in enumerate(lines) if re.match(r"^rule-providers:", l)), -1) + rp_end = next((i for i in range(rp + 1, len(lines)) + if lines[i] and not lines[i].startswith((" ", "#"))), len(lines)) if rp >= 0 else -1 + if rp >= 0: + kept, i, dropped = [], rp + 1, [] + while i < rp_end: + m = re.match(r"^ (['\"]?)([^:'\"]+)\1:\s*$", lines[i]) + if m: + nm = m.group(2).strip() + j = i + 1 + while j < rp_end and (not lines[j].strip() or lines[j].startswith(" ")): + j += 1 + if nm not in used: + dropped.append(nm) + else: + kept.extend(lines[i:j]) + i = j + continue + kept.append(lines[i]) + i += 1 + if dropped: + lines[rp + 1:rp_end] = kept + notes.append(f"清理无引用规则集 {', '.join(dropped)}") + + for n in notes: + print(f" · {n}") + return lines + + +def _sync_stash_overlays(base_lines: list[str]) -> None: + """为声明了 stash_output 的 overlay 各产出一份 Stash 定制版覆写。""" + enhanced = REPO_ROOT / ".github" / "scripts" / "sync-config" / "Enhanced" + for path in sorted(enhanced.glob("*.overlay.json")): + overlay = json.loads(path.read_text(encoding="utf-8")) + target = overlay.get("stash_output") + if not target: + continue + print(f" ── overlay: {path.name} → {target} ──") + lines = _stash_apply_overlay(base_lines, overlay, path.name) + body = "\n".join(lines).rstrip() + "\n" + changed = _write_stamped_if_changed(REPO_ROOT / target, body) + print(f" {'✓ ' + target + ' 已更新' if changed else '✓ ' + target + ' 无变化'}") + + # --------------------------------------------------------------------------- # 主函数 # --------------------------------------------------------------------------- @@ -4131,6 +4581,7 @@ def main() -> None: _GENERAL_INJECT = _build_general_inject(general_lines) _sync_clash(config, proxy_lines, group_lines, rule_lines) + _sync_stash(config) # 依赖 _sync_clash 的产物,必须排在其后 _sync_loon(config, proxy_lines, group_lines, rule_lines, surge_mitm_lines) _sync_qx(config, proxy_lines, group_lines, rule_lines, surge_mitm_lines) _sync_surfboard(config, proxy_lines, group_lines, rule_lines, general_lines, surge_src) diff --git a/.github/scripts/sync-config.txt b/.github/scripts/sync-config.txt index 68d0be932..b1024bfce 100644 --- a/.github/scripts/sync-config.txt +++ b/.github/scripts/sync-config.txt @@ -60,6 +60,11 @@ rule-providers: private => Private geoip/cn => China IP +# Stash +# 覆写文件:由上面的 Clash 产物二次转换(同 Mihomo.yaml 定位),整份逐行转录, +# 仅改写 Stash 与 mihomo 的差异点。无 Builtin 段,内容全部由脚本推导。 +>> Clash/Script/Stash.stoverride + # Quantumult X >> Quantumult/Sample.conf # > Builtin diff --git a/.github/scripts/sync-config/Enhanced/myscript.overlay.json b/.github/scripts/sync-config/Enhanced/myscript.overlay.json index 82ef157d2..7667fb2b9 100644 --- a/.github/scripts/sync-config/Enhanced/myscript.overlay.json +++ b/.github/scripts/sync-config/Enhanced/myscript.overlay.json @@ -3,6 +3,8 @@ "output": "Clash/Script/MyScript.js", + "stash_output": "Clash/Script/MyStash.stoverride", + "disabled_by_default": ["📬 Telegram"], "rules_insert": [ diff --git a/.github/workflows/sync-config.yml b/.github/workflows/sync-config.yml index abe1706d3..0252d3698 100644 --- a/.github/workflows/sync-config.yml +++ b/.github/workflows/sync-config.yml @@ -38,6 +38,6 @@ jobs: if git diff --cached --quiet; then echo "No changes to commit" else - git commit -m "chore: sync Profile.conf → Clash / Loon / QX / Surfboard / sing-box [auto]" + git commit -m "chore: sync Profile.conf → Clash / Stash / Loon / QX / Surfboard / sing-box [auto]" git push fi diff --git a/Clash/Mihomo.yaml b/Clash/Mihomo.yaml index bbcba99fc..9c8c6220d 100644 --- a/Clash/Mihomo.yaml +++ b/Clash/Mihomo.yaml @@ -2,7 +2,7 @@ # Date: 2026-08-06 19:38:00 # Author: @HotKids # -# 自动生成(sync-config.py 从 Clash/Sample.yaml 转译),请勿手改;改内容请改 Surge/Profile.conf。 +# 自动生成(sync-config.py 从 Clash/Sample.yaml 转译),请勿手动修改;如需调整请修改 Surge/Profile.conf。 # ── 通用设置 ── diff --git a/Clash/Script/MyClashBox.js b/Clash/Script/MyClashBox.js index 62c0af894..58a077b7f 100644 --- a/Clash/Script/MyClashBox.js +++ b/Clash/Script/MyClashBox.js @@ -5,9 +5,9 @@ * (如 https://sub.hotkids.me)动态套用与本仓库 Surge/Profile.conf 等效的 * 策略组、分流规则与基础设置,不必依赖机场自带配置。 * - * 自动生成,请勿手改:由 sync-config.py 从 Surge/Profile.conf(经 + * 自动生成,请勿手动修改:由 sync-config.py 从 Surge/Profile.conf(经 * Clash/Mihomo.yaml)叠加 sync-config/Enhanced/clashbox.overlay.json(私人差异声明) - * 而来,直接改本文件会在下次同步时被覆盖。公共部分请改 Surge/Profile.conf; + * 而来,直接修改本文件将在下次同步时被覆盖。公共部分请修改 Surge/Profile.conf; * 私人差异(改名 / 换图标 / 额外分组 / 分组类型 / 候选节点 / 默认开关等) * 请改 clashbox.overlay.json。 * diff --git a/Clash/Script/MyScript.js b/Clash/Script/MyScript.js index df60502d1..cb3289285 100644 --- a/Clash/Script/MyScript.js +++ b/Clash/Script/MyScript.js @@ -5,9 +5,9 @@ * (如 https://sub.hotkids.me)动态套用与本仓库 Surge/Profile.conf 等效的 * 策略组、分流规则与基础设置,不必依赖机场自带配置。 * - * 自动生成,请勿手改:由 sync-config.py 从 Surge/Profile.conf(经 + * 自动生成,请勿手动修改:由 sync-config.py 从 Surge/Profile.conf(经 * Clash/Mihomo.yaml)叠加 sync-config/Enhanced/myscript.overlay.json(私人差异声明) - * 而来,直接改本文件会在下次同步时被覆盖。公共部分请改 Surge/Profile.conf; + * 而来,直接修改本文件将在下次同步时被覆盖。公共部分请修改 Surge/Profile.conf; * 私人差异(改名 / 换图标 / 额外分组 / 分组类型 / 候选节点 / 默认开关等) * 请改 myscript.overlay.json。 * diff --git a/Clash/Script/MyScriptColor.js b/Clash/Script/MyScriptColor.js index 9c39a8e50..1cdc1251d 100644 --- a/Clash/Script/MyScriptColor.js +++ b/Clash/Script/MyScriptColor.js @@ -5,9 +5,9 @@ * (如 https://sub.hotkids.me)动态套用与本仓库 Surge/Profile.conf 等效的 * 策略组、分流规则与基础设置,不必依赖机场自带配置。 * - * 自动生成,请勿手改:由 sync-config.py 从 Surge/Profile.conf(经 + * 自动生成,请勿手动修改:由 sync-config.py 从 Surge/Profile.conf(经 * Clash/Mihomo.yaml)叠加 sync-config/Enhanced/myscriptcolor.overlay.json(私人差异声明) - * 而来,直接改本文件会在下次同步时被覆盖。公共部分请改 Surge/Profile.conf; + * 而来,直接修改本文件将在下次同步时被覆盖。公共部分请修改 Surge/Profile.conf; * 私人差异(改名 / 换图标 / 额外分组 / 分组类型 / 候选节点 / 默认开关等) * 请改 myscriptcolor.overlay.json。 * diff --git a/Clash/Script/MyStash.stoverride b/Clash/Script/MyStash.stoverride new file mode 100644 index 000000000..f6cceaed6 --- /dev/null +++ b/Clash/Script/MyStash.stoverride @@ -0,0 +1,713 @@ +# Date: 2026-08-28 21:23:19 + +name: MyStash for Android +desc: 自动生成(sync-config.py 从 Clash/Sample.yaml 转译,叠加 myscript.overlay.json),请勿手动修改;如需调整请修改 Surge/Profile.conf。 +author: '@HotKids' +icon: "https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Want.png" + +# 通用设置 + +# 代理模式:rule(规则)/ global(全局)/ direct(直连) +mode: rule + +# 日志等级:silent / error / warning / info / debug +log-level: info + +# ── Hosts ── + +# 静态域名映射,优先级高于 DNS 解析 +hosts: #!replace + 'localhost': 127.0.0.1 + +# ── DNS ── + +dns: #!replace + # DNS 查询跟随规则出站(mihomo 用 nameserver 的 #RULES 后缀表达, + # Stash 为全局开关)。官方提示多数场景无需开启:DNS 经代理转发可能 + # 破坏云服务商 CDN 优化并轻微增加延迟;如需 DNS 直连,将其改为 false。 + # 下方 proxy-server-nameserver 已为代理服务器域名提供独立解析, + # 满足官方要求的前置条件之一(避免递归查询)。 + follow-rule: true + + # 引导 DNS:仅用于解析 nameserver 服务器的域名 + # 只能填纯 IP 地址 + default-nameserver: + - 223.5.5.5 + - 119.29.29.29 + fake-ip-filter: + # 本地域名 + - "*.lan" + - "+.lan" + - "*.local" + - "*.localdomain" + - "*.home.arpa" + - "*.localhost" + - "WORKGROUP" + # NTP 时间同步 + - "time.*.com" + - "time.*.gov" + - "time.*.apple.com" + - "ntp.*.com" + - "+.pool.ntp.org" + - "*.ntp.org.cn" + # STUN / TURN(WebRTC / 游戏打洞) + - "+.stun.*" + - "*.stun.*.*" + - "*.turn.twilio.com" + - "*.stun.twilio.com" + - "stun.syncthing.net" + # 游戏平台 + - "*.srv.nintendo.net" + - "xbox.*.microsoft.com" + - "xbox.*.*.microsoft.com" + - "*.xboxlive.com" + # Steam + - "*.cm.steampowered.com" + - "*.steamcontent.com" + # Battle.net / Blizzard + - "*.battlenet.com.cn" + - "*.battlenet.com" + - "*.blzstatic.cn" + - "*.battle.net" + # 网络连通性探测(多平台) + - "*.msftncsi.com" + - "*.msftconnecttest.com" + - "connectivitycheck.gstatic.com" + - "connectivitycheck.android.com" + - "connectivitycheck.platform.hicloud.com" + - "connect.rom.miui.com" + - "captive.apple.com" + - "network-test.debian.org" + - "detectportal.firefox.com" + - "lens.l.google.com" + # 推送通知 + - "+.push.apple.com" + - "+.market.xiaomi.com" + # VPN 覆盖网络 + - "*.tailscale.com" + - "*.zerotier.com" + # 媒体服务 + - "*.spotify.com" + - "+.music.126.net" + - "*.mcdn.bilivideo.cn" + # 其他需要真实 IP 解析的域名 + - "localhost.*.qq.com" + + # 主 DNS:经代理查询干净结果,防止境外域名请求泄露至国内 DNS 服务商 + nameserver: + - "https://1.1.1.1/dns-query" + + # 分域名 DNS 策略:按声明顺序匹配,先窄后宽 + nameserver-policy: + # 内网/本地域名交给系统解析器(路由器/DHCP 下发的 DNS),可解析局域网设备名 + "geosite:private": + - system + # NTP 校时域名用裸 IP UDP 解析:时间偏差会让 DoH 证书验证失败,校时链路须独立于任何 TLS/代理 + "time.*.com": + - 223.5.5.5 + - 119.29.29.29 + "time.*.gov": + - 223.5.5.5 + - 119.29.29.29 + "time.*.apple.com": + - 223.5.5.5 + - 119.29.29.29 + "ntp.*.com": + - 223.5.5.5 + - 119.29.29.29 + "+.pool.ntp.org": + - 223.5.5.5 + - 119.29.29.29 + "*.ntp.org.cn": + - 223.5.5.5 + - 119.29.29.29 + # 国内域名直接用国内 DoH 就近解析,不绕代理(geosite 数据规则段已在用,零额外开销) + "geosite:cn": + - https://doh.pub/dns-query + - https://dns.alidns.com/dns-query + + # 代理节点域名解析:国内 DoH,节点 IP 不会被 CN DNS 污染,速度更快 + proxy-server-nameserver: + - https://doh.pub/dns-query + - https://dns.alidns.com/dns-query + + +# 服务器订阅配置 +# proxy-providers: +# Server: +# path: ./Provider/Proxy/Server.yaml +# url: https://sub.hotkids.me +# interval: 3600 +# proxy: DIRECT +# headers: +# User-Agent: +# - "Clash/v1.18.0" +# - "mihomo/1.18.3" +# health-check: +# enable: true +# url: 'http://cp.cloudflare.com/generate_204' +# interval: 600 +# timeout: 5000 +# expected-status: 204 + +proxy-groups: #!replace + # Global + # name: "GLOBAL" + # type: select + # icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Inbound.png + # include-all: true + + # Proxy + - name: "🔰 Proxy" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Outbound.png + proxies: + - 🇭🇰 Hong Kong + - 🇨🇳 Taiwan + - 🇸🇬 Singapore + - 🇯🇵 Japan + - 🇺🇸 America + - 🇬🇧 England + - 🇩🇪 Germany + - 🇺🇳 Server + - 🔘 DIRECT + + # Streaming Global + - name: "🎬 Streaming" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Streaming.png + proxies: + - 🔰 Proxy + - 🇭🇰 Hong Kong + - 🇨🇳 Taiwan + - 🇸🇬 Singapore + - 🇯🇵 Japan + - 🇺🇸 America + - 🇬🇧 England + - 🇩🇪 Germany + - 🇺🇳 Server + + # CNTV APAC + - name: "📺 CNTV" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/StreamingCN.png + proxies: + - 🔘 DIRECT + - 🇨🇳 Taiwan + - 🇭🇰 Hong Kong + + # Apple + # > Apple Services + - name: "🍎 Apple" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Apple.png + proxies: + - 🔘 DIRECT + - 🔰 Proxy + - 🇺🇸 America + - 🇯🇵 Japan + + # Google + - name: "🔍 Google" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Google.png + proxies: + - 🇺🇸 America + - 🔰 Proxy + + # Microsoft + # > OneDrive + - name: "☁️ OneDrive" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/OneDrive.png + proxies: + - 🔘 DIRECT + - 🔰 Proxy + + # > Microsoft Services + - name: "Ⓜ️ Microsoft" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Microsoft.png + proxies: + - 🔘 DIRECT + - 🔰 Proxy + + - name: "🤖 AIGC" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/ChatGPT.png + proxies: + - 🇺🇸 America + - 🇸🇬 Singapore + - 🔰 Proxy + + # Crypto + - name: "🪙 Crypto" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Cryptocurrency_3.png + proxies: + - 🇩🇪 Germany + - 🇺🇸 America + - 🔰 Proxy + - 🔘 DIRECT + + # Finance + - name: "💳 Finance" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Finance.png + proxies: + - 🇺🇸 America + - 🇩🇪 Germany + - 🔰 Proxy + - 🔘 DIRECT + + # Mail + - name: "📧 Mail" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Email.png + proxies: + - 🔰 Proxy + - 🔘 DIRECT + + # Speedtest + - name: "⏱️ Speedtest" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Speed.png + include-all: true + + # Adblock + - name: "🚧 AdGuard" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Block.png + proxies: + - 🔘 DIRECT + - ⛔️ REJECT + - 📛 REJECT-DROP + + # DIRECT + - name: "🔘 DIRECT" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Direct.png + hidden: true + proxies: + - DIRECT + + # REJECT + - name: "⛔️ REJECT" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Reject.png + hidden: true + proxies: + - REJECT + + # REJECT-DROP + - name: "📛 REJECT-DROP" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Reject.png + hidden: true + proxies: + - REJECT-DROP + + # Nodes + - name: "🇸🇱 Relay" + type: url-test + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Loop.png + hidden: true + include-all: true + tolerance: 50 + filter: '(?i)^(?=.*(?:GoMaMi|Neburst|Pro))' + + - name: "🇭🇰 HK Relay" + type: fallback + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Loop.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:HK|HKG)\d*\b)(?=.*(?:GoMaMi|Pro))' + + - name: "🇨🇳 TW Relay" + type: fallback + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Loop.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:TW|TWN)\d*\b)(?=.*Neburst)' + + - name: "🇯🇵 JP Relay" + type: fallback + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Loop.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:JP|JPN)\d*\b)(?=.*Pro)' + + - name: "🇺🇸 US Relay" + type: fallback + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Loop.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:US|USA)\d*\b)(?=.*(?:GoMaMi|Pro))' + + - name: "🇺🇳 Server" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Club.png + include-all: true + + # Area + - name: "🇭🇰 Hong Kong" + type: fallback + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Flags/HK.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:HK|HKG)\d*\b)(?!.*GoMaMi)(?!.*Pro)' + + - name: "🇨🇳 Taiwan" + type: fallback + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Flags/TW.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:TW|TWN)\d*\b)(?!.*Neburst)(?!.*Pro)' + + - name: "🇸🇬 Singapore" + type: fallback + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Flags/SG.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:SG|SGP)\d*\b)(?!.*Neburst)(?!.*Pro)' + + - name: "🇯🇵 Japan" + type: fallback + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Flags/JP.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:JP|JPN)\d*\b)(?!.*Pro)' + + - name: "🇺🇸 America" + type: fallback + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Flags/US.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:US|USA)\d*\b)(?!.*GoMaMi)(?!.*Pro)' + + +# 关于 Rule Provider 请查阅:https://wiki.metacubex.one/en/config/rule-providers/ + + - name: "🇬🇧 England" + type: fallback + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Flags/UK.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:UK|GBR)\d*\b)' + + - name: "🇩🇪 Germany" + type: fallback + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Flags/DE.png + hidden: true + include-all: true + filter: '(?i)^(?=.*\b(?:DE|DEU)\d*\b)' + +rule-providers: #!replace +# name: # Provider 名称 +# type: http # http 或 file +# behavior: classical # 或 ipcidr、domain +# path: # 文件路径 +# url: # 只有当类型为 HTTP 时才可用,您不需要在本地空间中创建新文件。 +# interval: # 自动更新间隔,仅在类型为 HTTP 时可用 + Bypass: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Bypass.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Bypass.yaml + interval: 86400 + + Reroute: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Reroute.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Reroute.yaml + interval: 86400 + + Private: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Private.mrs + url: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geosite/private.mrs + interval: 86400 + + HTTPDNS: + behavior: classical + format: yaml + path: ./Provider/RuleSet/HTTPDNS.yaml + url: https://fastly.jsdelivr.net/gh/VirgilClyne/GetSomeFries@main/ruleset/HTTPDNS.Block.yaml + interval: 86400 + + Reject: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Reject.mrs + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Reject.mrs + interval: 86400 + + AdBlock: + behavior: classical + format: yaml + path: ./Provider/RuleSet/AdBlock.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Block.yaml + interval: 86400 + + Phishing: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Phishing.mrs + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Phishing.mrs + interval: 86400 + + Bogus: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Bogus.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Bogus.yaml + interval: 86400 + + Streaming_TW: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Streaming_TW.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Streaming_TW.yaml + interval: 86400 + + Streaming_JP: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Streaming_JP.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Streaming_JP.yaml + interval: 86400 + + Streaming_US: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Streaming_US.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Streaming_US.yaml + interval: 86400 + + Streaming: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Streaming.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Streaming.yaml + interval: 86400 + + CNTV: + behavior: classical + format: yaml + path: ./Provider/RuleSet/CNTV.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/CNTV.yaml + interval: 86400 + + Google AI Studio: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Google_AI_Studio.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Gemini.yaml + interval: 86400 + + AIGC: + behavior: classical + format: yaml + path: ./Provider/RuleSet/AIGC.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/GenAI.yaml + interval: 86400 + + Apple CN: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Apple_CN.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Apple%20CN.yaml + interval: 86400 + + Apple: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Apple.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Apple.yaml + interval: 86400 + + Google: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Google.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Google.yaml + interval: 86400 + + OneDrive: + behavior: classical + format: yaml + path: ./Provider/RuleSet/OneDrive.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/OneDrive.yaml + interval: 86400 + + Microsoft: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Microsoft.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Microsoft.yaml + interval: 86400 + + Crypto: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Crypto.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Crypto.yaml + interval: 86400 + + Finance: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Finance.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Finance.yaml + interval: 86400 + + Spark: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Spark.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Spark.yaml + interval: 86400 + + Speedtest: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Speedtest.mrs + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Speedtest.mrs + interval: 86400 + + Global: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Global.mrs + url: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geosite/geolocation-!cn.mrs + interval: 86400 + + China: + behavior: domain + format: mrs + path: ./Provider/RuleSet/China.mrs + url: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geosite/cn.mrs + interval: 86400 + + China ASN: + behavior: classical + format: yaml + path: ./Provider/RuleSet/China_ASN.yaml + url: https://fastly.jsdelivr.net/gh/VirgilClyne/GetSomeFries@main/ruleset/ASN.China.yaml + interval: 86400 + + China IP: + behavior: ipcidr + format: mrs + path: ./Provider/RuleSet/China_IP.mrs + url: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geoip/cn.mrs + interval: 86400 + + LAN: + behavior: ipcidr + format: mrs + path: ./Provider/RuleSet/LAN.mrs + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/lancidr.mrs + interval: 86400 + +# 规则 +rules: #!replace + # 境外 QUIC(UDP 443)拦截回退 TCP、国内放行(Surge PROTOCOL,QUIC 转 Clash 无直接等价,用 mihomo 逻辑规则表达) + - AND,((NETWORK,UDP),(DST-PORT,443),(NOT,((OR,((GEOSITE,cn),(GEOIP,CN)))))),⛔️ REJECT + + # 标准 SSH 端口 + - AND,((DST-PORT,22),(NETWORK,TCP)),🔘 DIRECT + + # Unbreak 后续规则修正,修复因规则拦截或分流不当导致的功能异常 + - RULE-SET,Bypass,🔘 DIRECT + - RULE-SET,Reroute,🔰 Proxy + + # Private 私有网络 + - RULE-SET,Private,🔘 DIRECT + + # HTTPDNS 请求/流量阻止 + - RULE-SET,HTTPDNS,🚧 AdGuard + + # Advertising 广告 + - RULE-SET,Reject,🚧 AdGuard + - RULE-SET,AdBlock,🚧 AdGuard + + # Phishing 钓鱼网站 + - RULE-SET,Phishing,🚧 AdGuard + + # Bogus IP NXDOMAIN 劫持/僵尸网络 C2 + - RULE-SET,Bogus,📛 REJECT-DROP,no-resolve + + # Global Area Network + # > Streaming by Region + # >> Streaming TW + - RULE-SET,Streaming_TW,🇨🇳 Taiwan + # >> Streaming JP + - RULE-SET,Streaming_JP,🇯🇵 Japan + # >> Streaming US + - RULE-SET,Streaming_US,🇺🇸 America + + # > Streaming + - RULE-SET,Streaming,🎬 Streaming + + # > CNTV(适用于 iQIYI Intl,WeTV,Bilibili 等大陆在港台东南亚提供服务的流媒体服务) + - RULE-SET,CNTV,📺 CNTV + + # Global 全球代理规则 + # > AIGC + - RULE-SET,Google AI Studio,🔍 Google + - RULE-SET,AIGC,🤖 AIGC + + # > Apple + # >> Apple Services + - RULE-SET,Apple CN,🔘 DIRECT + - RULE-SET,Apple,🍎 Apple + + # > Google + - RULE-SET,Google,🔍 Google + + # > Microsoft + - RULE-SET,OneDrive,☁️ OneDrive + - RULE-SET,Microsoft,Ⓜ️ Microsoft + + # > Telegram + - IP-ASN,44907,🇸🇬 Singapore,no-resolve + - IP-ASN,62014,🇸🇬 Singapore,no-resolve + - IP-ASN,59930,🇺🇸 America,no-resolve + - IP-ASN,62041,🇩🇪 Germany,no-resolve + - IP-ASN,211157,🇩🇪 Germany,no-resolve + - IP-CIDR,5.28.192.0/18,🇩🇪 Germany,no-resolve + - IP-CIDR,109.239.140.0/24,🇩🇪 Germany,no-resolve + + # > Crypto + - RULE-SET,Crypto,🪙 Crypto + + # > Finance + - RULE-SET,Finance,💳 Finance + + # > Mail + - RULE-SET,Spark,📧 Mail + + # > Speedtest + - RULE-SET,Speedtest,⏱️ Speedtest + + # Global (DNS Cache Pollution) / (IP Blackhole) / (Region-Restricted Access Denied) / (Network Jitter) + - RULE-SET,Global,🔰 Proxy + + # China Area Network + - RULE-SET,China,🔘 DIRECT + - RULE-SET,China ASN,🔘 DIRECT,no-resolve + - RULE-SET,China IP,🔘 DIRECT,no-resolve + + # Local Area Network + - RULE-SET,LAN,🔘 DIRECT,no-resolve + + # GeoIP + - GEOSITE,cn,🔘 DIRECT + - GEOIP,CN,🔘 DIRECT,no-resolve + - GEOSITE,geolocation-!cn,🔰 Proxy + + # Final + - MATCH,🔰 Proxy diff --git a/Clash/Script/Script.js b/Clash/Script/Script.js index fc04830ff..9d53b91aa 100644 --- a/Clash/Script/Script.js +++ b/Clash/Script/Script.js @@ -5,9 +5,9 @@ * (如 https://sub.hotkids.me)动态套用与本仓库 Surge/Profile.conf 等效的 * 策略组、分流规则与基础设置,不必依赖机场自带配置。 * - * 自动生成,请勿手改:由 sync-config.py 从 Surge/Profile.conf(经 - * Clash/Mihomo.yaml)转译而来,直接改本文件会在下次同步时被覆盖; - * 要改内容请改 Surge/Profile.conf。 + * 自动生成,请勿手动修改:由 sync-config.py 从 Surge/Profile.conf(经 + * Clash/Mihomo.yaml)转译而来,直接修改本文件将在下次同步时被覆盖; + * 如需调整请修改 Surge/Profile.conf。 * * 本地唯一可临时修改的是下方 ruleOptionsEnable 的取值,用于按需开关某个分组。 * diff --git a/Clash/Script/Stash.stoverride b/Clash/Script/Stash.stoverride new file mode 100644 index 000000000..854719533 --- /dev/null +++ b/Clash/Script/Stash.stoverride @@ -0,0 +1,663 @@ +# Date: 2026-08-28 21:23:19 + +name: Stash for Android +desc: 自动生成(sync-config.py 从 Clash/Sample.yaml 转译),请勿手动修改;如需调整请修改 Surge/Profile.conf。 +author: '@HotKids' +icon: "https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Want.png" + +# 通用设置 + +# 代理模式:rule(规则)/ global(全局)/ direct(直连) +mode: rule + +# 日志等级:silent / error / warning / info / debug +log-level: info + +# ── Hosts ── + +# 静态域名映射,优先级高于 DNS 解析 +hosts: #!replace + 'localhost': 127.0.0.1 + +# ── DNS ── + +dns: #!replace + # DNS 查询跟随规则出站(mihomo 用 nameserver 的 #RULES 后缀表达, + # Stash 为全局开关)。官方提示多数场景无需开启:DNS 经代理转发可能 + # 破坏云服务商 CDN 优化并轻微增加延迟;如需 DNS 直连,将其改为 false。 + # 下方 proxy-server-nameserver 已为代理服务器域名提供独立解析, + # 满足官方要求的前置条件之一(避免递归查询)。 + follow-rule: true + + # 引导 DNS:仅用于解析 nameserver 服务器的域名 + # 只能填纯 IP 地址 + default-nameserver: + - 223.5.5.5 + - 119.29.29.29 + fake-ip-filter: + # 本地域名 + - "*.lan" + - "+.lan" + - "*.local" + - "*.localdomain" + - "*.home.arpa" + - "*.localhost" + - "WORKGROUP" + # NTP 时间同步 + - "time.*.com" + - "time.*.gov" + - "time.*.apple.com" + - "ntp.*.com" + - "+.pool.ntp.org" + - "*.ntp.org.cn" + # STUN / TURN(WebRTC / 游戏打洞) + - "+.stun.*" + - "*.stun.*.*" + - "*.turn.twilio.com" + - "*.stun.twilio.com" + - "stun.syncthing.net" + # 游戏平台 + - "*.srv.nintendo.net" + - "xbox.*.microsoft.com" + - "xbox.*.*.microsoft.com" + - "*.xboxlive.com" + # Steam + - "*.cm.steampowered.com" + - "*.steamcontent.com" + # Battle.net / Blizzard + - "*.battlenet.com.cn" + - "*.battlenet.com" + - "*.blzstatic.cn" + - "*.battle.net" + # 网络连通性探测(多平台) + - "*.msftncsi.com" + - "*.msftconnecttest.com" + - "connectivitycheck.gstatic.com" + - "connectivitycheck.android.com" + - "connectivitycheck.platform.hicloud.com" + - "connect.rom.miui.com" + - "captive.apple.com" + - "network-test.debian.org" + - "detectportal.firefox.com" + - "lens.l.google.com" + # 推送通知 + - "+.push.apple.com" + - "+.market.xiaomi.com" + # VPN 覆盖网络 + - "*.tailscale.com" + - "*.zerotier.com" + # 媒体服务 + - "*.spotify.com" + - "+.music.126.net" + - "*.mcdn.bilivideo.cn" + # 其他需要真实 IP 解析的域名 + - "localhost.*.qq.com" + + # 主 DNS:经代理查询干净结果,防止境外域名请求泄露至国内 DNS 服务商 + nameserver: + - "https://1.1.1.1/dns-query" + + # 分域名 DNS 策略:按声明顺序匹配,先窄后宽 + nameserver-policy: + # 内网/本地域名交给系统解析器(路由器/DHCP 下发的 DNS),可解析局域网设备名 + "geosite:private": + - system + # NTP 校时域名用裸 IP UDP 解析:时间偏差会让 DoH 证书验证失败,校时链路须独立于任何 TLS/代理 + "time.*.com": + - 223.5.5.5 + - 119.29.29.29 + "time.*.gov": + - 223.5.5.5 + - 119.29.29.29 + "time.*.apple.com": + - 223.5.5.5 + - 119.29.29.29 + "ntp.*.com": + - 223.5.5.5 + - 119.29.29.29 + "+.pool.ntp.org": + - 223.5.5.5 + - 119.29.29.29 + "*.ntp.org.cn": + - 223.5.5.5 + - 119.29.29.29 + # 国内域名直接用国内 DoH 就近解析,不绕代理(geosite 数据规则段已在用,零额外开销) + "geosite:cn": + - https://doh.pub/dns-query + - https://dns.alidns.com/dns-query + + # 代理节点域名解析:国内 DoH,节点 IP 不会被 CN DNS 污染,速度更快 + proxy-server-nameserver: + - https://doh.pub/dns-query + - https://dns.alidns.com/dns-query + + +# 服务器订阅配置 +# proxy-providers: +# Server: +# path: ./Provider/Proxy/Server.yaml +# url: https://sub.hotkids.me +# interval: 3600 +# proxy: DIRECT +# headers: +# User-Agent: +# - "Clash/v1.18.0" +# - "mihomo/1.18.3" +# health-check: +# enable: true +# url: 'http://cp.cloudflare.com/generate_204' +# interval: 600 +# timeout: 5000 +# expected-status: 204 + +proxy-groups: #!replace + # Global + # name: "GLOBAL" + # type: select + # icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Inbound.png + # include-all: true + + # Proxy + - name: "🔰 Proxy" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Outbound.png + proxies: + - 🇭🇰 Hong Kong + - 🇨🇳 Taiwan + - 🇸🇬 Singapore + - 🇯🇵 Japan + - 🇺🇸 America + - 🇺🇳 Server + - 🔘 DIRECT + + # Streaming Global + - name: "🎬 Streaming" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Streaming.png + proxies: + - 🔰 Proxy + - 🇭🇰 Hong Kong + - 🇨🇳 Taiwan + - 🇸🇬 Singapore + - 🇯🇵 Japan + - 🇺🇸 America + - 🇺🇳 Server + + # CNTV APAC + - name: "📺 CNTV" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/StreamingCN.png + proxies: + - 🔘 DIRECT + - 🇨🇳 Taiwan + - 🇭🇰 Hong Kong + + # Apple + # > Apple Services + - name: "🍎 Apple" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Apple.png + proxies: + - 🔘 DIRECT + - 🔰 Proxy + - 🇺🇸 America + - 🇯🇵 Japan + + # Google + - name: "🔍 Google" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Google.png + proxies: + - 🇺🇸 America + - 🔰 Proxy + + # Microsoft + # > OneDrive + - name: "☁️ OneDrive" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/OneDrive.png + proxies: + - 🔘 DIRECT + - 🔰 Proxy + + # > Microsoft Services + - name: "Ⓜ️ Microsoft" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Microsoft.png + proxies: + - 🔘 DIRECT + - 🔰 Proxy + + # Telegram + - name: "📬 Telegram" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Telegram.png + proxies: + - 🔰 Proxy + - 🇸🇬 Singapore + - 🔘 DIRECT + + # AIGC + - name: "🤖 AIGC" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/ChatGPT.png + proxies: + - 🇺🇸 America + - 🇸🇬 Singapore + - 🔰 Proxy + + # Crypto + - name: "🪙 Crypto" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Cryptocurrency_3.png + proxies: + - 🇺🇸 America + - 🔰 Proxy + - 🔘 DIRECT + + # Finance + - name: "💳 Finance" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Finance.png + proxies: + - 🇺🇸 America + - 🔰 Proxy + - 🔘 DIRECT + + # Mail + - name: "📧 Mail" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Email.png + proxies: + - 🔰 Proxy + - 🔘 DIRECT + + # Speedtest + - name: "⏱️ Speedtest" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Speed.png + include-all: true + + # Adblock + - name: "🚧 AdGuard" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Block.png + proxies: + - 🔘 DIRECT + - ⛔️ REJECT + - 📛 REJECT-DROP + + # DIRECT + - name: "🔘 DIRECT" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Direct.png + hidden: true + proxies: + - DIRECT + + # REJECT + - name: "⛔️ REJECT" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Reject.png + hidden: true + proxies: + - REJECT + + # REJECT-DROP + - name: "📛 REJECT-DROP" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Reject.png + hidden: true + proxies: + - REJECT-DROP + + # Nodes + - name: "🇺🇳 Server" + type: select + icon: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Quantumult/X/Images/Color/Club.png + include-all: true + + # Area + - name: "🇭🇰 Hong Kong" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Hong_Kong.png + include-all: true + filter: '(?i)(?:🇭🇰|香港|Hong Kong|\b(?:HK|HKG)\d*\b)' + + - name: "🇨🇳 Taiwan" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Taiwan.png + include-all: true + filter: '(?i)(?:🇨🇳|🇹🇼|台湾|Taiwan|\b(?:TW|TWN)\d*\b)' + + - name: "🇸🇬 Singapore" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Singapore.png + include-all: true + filter: '(?i)(?:🇸🇬|新加坡|Singapore|\b(?:SG|SGP)\d*\b)' + + - name: "🇯🇵 Japan" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/Japan.png + include-all: true + filter: '(?i)(?:🇯🇵|日本|Japan|\b(?:JP|JPN)\d*\b)' + + - name: "🇺🇸 America" + type: select + icon: https://fastly.jsdelivr.net/gh/Koolson/Qure@master/IconSet/Color/United_States.png + include-all: true + filter: '(?i)(?:🇺🇸|美国|United States|\b(?:US|USA)\d*\b)' + + +# 关于 Rule Provider 请查阅:https://wiki.metacubex.one/en/config/rule-providers/ + +rule-providers: #!replace +# name: # Provider 名称 +# type: http # http 或 file +# behavior: classical # 或 ipcidr、domain +# path: # 文件路径 +# url: # 只有当类型为 HTTP 时才可用,您不需要在本地空间中创建新文件。 +# interval: # 自动更新间隔,仅在类型为 HTTP 时可用 + Bypass: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Bypass.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Bypass.yaml + interval: 86400 + + Reroute: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Reroute.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Reroute.yaml + interval: 86400 + + Private: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Private.mrs + url: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geosite/private.mrs + interval: 86400 + + HTTPDNS: + behavior: classical + format: yaml + path: ./Provider/RuleSet/HTTPDNS.yaml + url: https://fastly.jsdelivr.net/gh/VirgilClyne/GetSomeFries@main/ruleset/HTTPDNS.Block.yaml + interval: 86400 + + Reject: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Reject.mrs + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Reject.mrs + interval: 86400 + + AdBlock: + behavior: classical + format: yaml + path: ./Provider/RuleSet/AdBlock.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Block.yaml + interval: 86400 + + Phishing: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Phishing.mrs + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Phishing.mrs + interval: 86400 + + Bogus: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Bogus.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Bogus.yaml + interval: 86400 + + Streaming_TW: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Streaming_TW.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Streaming_TW.yaml + interval: 86400 + + Streaming_JP: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Streaming_JP.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Streaming_JP.yaml + interval: 86400 + + Streaming_US: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Streaming_US.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Streaming_US.yaml + interval: 86400 + + Streaming: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Streaming.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Streaming.yaml + interval: 86400 + + CNTV: + behavior: classical + format: yaml + path: ./Provider/RuleSet/CNTV.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/CNTV.yaml + interval: 86400 + + Google AI Studio: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Google_AI_Studio.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Gemini.yaml + interval: 86400 + + AIGC: + behavior: classical + format: yaml + path: ./Provider/RuleSet/AIGC.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/GenAI.yaml + interval: 86400 + + Apple CN: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Apple_CN.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Apple%20CN.yaml + interval: 86400 + + Apple: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Apple.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Apple.yaml + interval: 86400 + + Google: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Google.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Google.yaml + interval: 86400 + + OneDrive: + behavior: classical + format: yaml + path: ./Provider/RuleSet/OneDrive.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/OneDrive.yaml + interval: 86400 + + Microsoft: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Microsoft.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Microsoft.yaml + interval: 86400 + + Telegram: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Telegram.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Telegram.yaml + interval: 86400 + + Crypto: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Crypto.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Crypto.yaml + interval: 86400 + + Finance: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Finance.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Finance.yaml + interval: 86400 + + Spark: + behavior: classical + format: yaml + path: ./Provider/RuleSet/Spark.yaml + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Spark.yaml + interval: 86400 + + Speedtest: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Speedtest.mrs + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/Speedtest.mrs + interval: 86400 + + Global: + behavior: domain + format: mrs + path: ./Provider/RuleSet/Global.mrs + url: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geosite/geolocation-!cn.mrs + interval: 86400 + + China: + behavior: domain + format: mrs + path: ./Provider/RuleSet/China.mrs + url: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geosite/cn.mrs + interval: 86400 + + China ASN: + behavior: classical + format: yaml + path: ./Provider/RuleSet/China_ASN.yaml + url: https://fastly.jsdelivr.net/gh/VirgilClyne/GetSomeFries@main/ruleset/ASN.China.yaml + interval: 86400 + + China IP: + behavior: ipcidr + format: mrs + path: ./Provider/RuleSet/China_IP.mrs + url: https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@meta/geo/geoip/cn.mrs + interval: 86400 + + LAN: + behavior: ipcidr + format: mrs + path: ./Provider/RuleSet/LAN.mrs + url: https://fastly.jsdelivr.net/gh/HotKids/Rules@master/Clash/RuleSet/lancidr.mrs + interval: 86400 + +# 规则 +rules: #!replace + # 境外 QUIC(UDP 443)拦截回退 TCP、国内放行(Surge PROTOCOL,QUIC 转 Clash 无直接等价,用 mihomo 逻辑规则表达) + - AND,((NETWORK,UDP),(DST-PORT,443),(NOT,((OR,((GEOSITE,cn),(GEOIP,CN)))))),⛔️ REJECT + + # 标准 SSH 端口 + - AND,((DST-PORT,22),(NETWORK,TCP)),🔘 DIRECT + + # Unbreak 后续规则修正,修复因规则拦截或分流不当导致的功能异常 + - RULE-SET,Bypass,🔘 DIRECT + - RULE-SET,Reroute,🔰 Proxy + + # Private 私有网络 + - RULE-SET,Private,🔘 DIRECT + + # HTTPDNS 请求/流量阻止 + - RULE-SET,HTTPDNS,🚧 AdGuard + + # Advertising 广告 + - RULE-SET,Reject,🚧 AdGuard + - RULE-SET,AdBlock,🚧 AdGuard + + # Phishing 钓鱼网站 + - RULE-SET,Phishing,🚧 AdGuard + + # Bogus IP NXDOMAIN 劫持/僵尸网络 C2 + - RULE-SET,Bogus,📛 REJECT-DROP,no-resolve + + # Global Area Network + # > Streaming by Region + # >> Streaming TW + - RULE-SET,Streaming_TW,🇨🇳 Taiwan + # >> Streaming JP + - RULE-SET,Streaming_JP,🇯🇵 Japan + # >> Streaming US + - RULE-SET,Streaming_US,🇺🇸 America + + # > Streaming + - RULE-SET,Streaming,🎬 Streaming + + # > CNTV(适用于 iQIYI Intl,WeTV,Bilibili 等大陆在港台东南亚提供服务的流媒体服务) + - RULE-SET,CNTV,📺 CNTV + + # Global 全球代理规则 + # > AIGC + - RULE-SET,Google AI Studio,🔍 Google + - RULE-SET,AIGC,🤖 AIGC + + # > Apple + # >> Apple Services + - RULE-SET,Apple CN,🔘 DIRECT + - RULE-SET,Apple,🍎 Apple + + # > Google + - RULE-SET,Google,🔍 Google + + # > Microsoft + - RULE-SET,OneDrive,☁️ OneDrive + - RULE-SET,Microsoft,Ⓜ️ Microsoft + + # > Telegram + - RULE-SET,Telegram,📬 Telegram + + # > Crypto + - RULE-SET,Crypto,🪙 Crypto + + # > Finance + - RULE-SET,Finance,💳 Finance + + # > Mail + - RULE-SET,Spark,📧 Mail + + # > Speedtest + - RULE-SET,Speedtest,⏱️ Speedtest + + # Global (DNS Cache Pollution) / (IP Blackhole) / (Region-Restricted Access Denied) / (Network Jitter) + - RULE-SET,Global,🔰 Proxy + + # China Area Network + - RULE-SET,China,🔘 DIRECT + - RULE-SET,China ASN,🔘 DIRECT,no-resolve + - RULE-SET,China IP,🔘 DIRECT,no-resolve + + # Local Area Network + - RULE-SET,LAN,🔘 DIRECT,no-resolve + + # GeoIP + - GEOSITE,cn,🔘 DIRECT + - GEOIP,CN,🔘 DIRECT,no-resolve + - GEOSITE,geolocation-!cn,🔰 Proxy + + # Final + - MATCH,🔰 Proxy diff --git a/README.md b/README.md index 7fd337bb0..2d0e04087 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ 自用规则、模块和代理工具集合。 `Surge/Profile.conf` 与 `Surge/RULE-SET/` 是唯一手动维护的配置 / 规则来源, -其他平台产物全部由 `.github/scripts/` 下的同步脚本自动生成——**改内容请改源头,直接改生成产物会被下次同步覆盖**。 +其他平台产物全部由 `.github/scripts/` 下的同步脚本自动生成——**如需调整请修改源头;直接修改生成产物将在下次同步时被覆盖**。 | 目录 | 说明 | |---|---| | [`Surge/`](Surge/) | **单一来源**:托管配置 `Profile.conf`、规则源 `RULE-SET/`、sgmodule 模块 | -| [`Clash/`](Clash/) | 自动生成:`Sample.yaml` / `Mihomo.yaml`(锚点版)/ 规则集 / Enhance Script | +| [`Clash/`](Clash/) | 自动生成:`Sample.yaml` / `Mihomo.yaml`(锚点版)/ 规则集 / Enhance Script 与 `Script/Stash.stoverride`(Stash 覆写) | | [`Quantumult/`](Quantumult/) | 自动生成:QX 配置与规则;手动维护:图标库 `X/Images/` | | [`sing-box/`](sing-box/) | 自动生成:完整配置 `config.json`、规则集(`source/` 源码 + `rule-set/` 二进制) | | [`subconverter/`](subconverter/) | subconverter 订阅转换远程配置(已弃用,仅维护 emoji) | diff --git a/Surge/Module/Scripts/friday-checkin.js b/Surge/Module/Scripts/friday-checkin.js index a806824e7..636f76e94 100644 --- a/Surge/Module/Scripts/friday-checkin.js +++ b/Surge/Module/Scripts/friday-checkin.js @@ -4,7 +4,7 @@ * 背景:签到 Bearer(accessToken) 仅 3 小时;fet-token 只在 token/getv2(刷新时)出现, * 光开 App 常撞不到。故改混合策略: * - 开 App 时(member/get 必触发)抓当前 accessToken → 当场签到(保证开 App 即签) - * - 顺手在 token/getv2 出现时存 fet-token + 设备字段 → 给 cron 做自动续期 + * - 并在 token/getv2 出现时存 fet-token + 设备字段 → 给 cron 做自动续期 * cron:有 fet-token 就刷新 accessToken 自动签;否则回退用最近存的 accessToken(3h 内有效)。 * * 维护:每天开下 App 必签到;若抓到过 fet-token,则数天内不开 App 也能自动签。 diff --git a/Surge/Module/Scripts/ip-security.js b/Surge/Module/Scripts/ip-security.js index 880e72daf..7554dbdfb 100644 --- a/Surge/Module/Scripts/ip-security.js +++ b/Surge/Module/Scripts/ip-security.js @@ -831,7 +831,7 @@ function sendNetworkChangeNotification({ localZh, policy, localIP, outIP, entran } // 先并行发起 geo/risk/流量 API 请求,确保 ip.sb/ipinfo/ip-api 请求完成后再查策略 - // DNS 泄露检测需要走代理策略,必须等拿到 policy 后再执行 + // DNS 泄露检测需要走代理策略,必须在取得 policy 后执行 const [riskInfo, ipTypeResult, localSbRaw, localBaiduRaw, outGeoRaw, outOrgRaw, trafficResult] = await Promise.all([ getRiskScore(outIP), // 0: 风险评分 getIPType(outIP), // 1: IP 类型 diff --git a/Surge/Module/Scripts/komari-traffic.js b/Surge/Module/Scripts/komari-traffic.js index 296bc4dae..ded7cf91a 100644 --- a/Surge/Module/Scripts/komari-traffic.js +++ b/Surge/Module/Scripts/komari-traffic.js @@ -354,7 +354,7 @@ if (!base) { changes.push(online ? `🟢 ${n.name} 恢复上线` : `🔴 ${n.name} 离线`); } - // 重启检测:uptime 明显回落(补离线推送因刷新间隔漏掉的快速重启) + // 重启检测:uptime 明显回落(补离线推送因刷新间隔遗漏的快速重启) const up = rec && rec.uptime > 0 ? rec.uptime : 0; if (up) next.uptime[n.uuid] = up; const prevUp = prev.uptime ? prev.uptime[n.uuid] : 0; diff --git a/sing-box/README.md b/sing-box/README.md index da6309e1e..e6d14ae80 100644 --- a/sing-box/README.md +++ b/sing-box/README.md @@ -2,7 +2,7 @@ ## 完整配置 `config.json` -对齐 sing-box **1.12+ 新格式**(typed DNS server、route rule action、`default_domain_resolver`、remote binary rule-set)的完整客户端配置。由 `sync-config.py` 从 `Surge/Profile.conf` 自动生成(静态基座见 `.github/scripts/sync-config/sing-box.ini`),策略组 / 路由与其他平台一致,直接改动会被下次同步覆盖。 +对齐 sing-box **1.12+ 新格式**(typed DNS server、route rule action、`default_domain_resolver`、remote binary rule-set)的完整客户端配置。由 `sync-config.py` 从 `Surge/Profile.conf` 自动生成(静态基座见 `.github/scripts/sync-config/sing-box.ini`),策略组 / 路由与其他平台一致,直接修改将在下次同步时被覆盖。 > ⚠️ 由脚本自动生成,仅做过 `sing-box check` 结构校验(CI `lint.yml`),**未实际跑过 sing-box 内核联网测试**,正式使用前请自行验证可用性。 @@ -19,7 +19,7 @@ | `source/*.json` | 源码 | `source` | 人类可读、可 diff 审查 | | `rule-set/*.srs` | 二进制 | `binary` | 由 CI 用官方 `sing-box rule-set compile` 编译,体积更小、加载更快 | -两者内容等价,均声明 `version: 2`(需 sing-box ≥ 1.10)。`.srs` 由 `source/` 下同名 `.json` 自动编译,直接改动会被 CI 重新编译覆盖;规则内容改动提交到 `Surge/RULE-SET/`(经 `sync-rules.py` 同步)。 +两者内容等价,均声明 `version: 2`(需 sing-box ≥ 1.10)。`.srs` 由 `source/` 下同名 `.json` 自动编译,直接修改将被 CI 重新编译覆盖;规则内容改动提交到 `Surge/RULE-SET/`(经 `sync-rules.py` 同步)。 ## 引用示例