# 模块文件格式

模块是一份纯文本。开头几行是元数据，之后按 `[段名]` 分段。

## 最小例子

```ini
#!name=YouTube 去广告
#!desc=拦截 YouTube 的广告接口与播放埋点。需开启 HTTPS 解密。

[Rule]
AND,((DOMAIN-SUFFIX,googlevideo.com),(PROTOCOL,UDP)),REJECT

[URL Rewrite]
^https?:\/\/(?:www|s)\.youtube\.com\/api\/stats\/ads _ reject-200

[MITM]
hostname = %APPEND% *.googlevideo.com, s.youtube.com
```

## 行的规则

- 空行忽略。
- `#` 或 `;` 开头的**整行**是注释。**例外**:`#!` 开头的是元数据指令，会被解析。
- **不支持行尾注释。** `DOMAIN-SUFFIX,a.com,DIRECT # 走直连` 中的 `DIRECT # 走直连` 会被完整解析为出口名。该名称未定义时，规则会使用「默认」出口，且不会显示错误。注释必须独占一行并写在规则上一行。
- `[段名]` 单独占一行，开始一个新段。段名大小写不敏感。
- 一条规则必须写在一行里，没有续行语法。
- 段的先后顺序不影响解析，同名段出现多次时内容累加。

## 元数据头

| 指令 | 作用 |
|---|---|
| `#!name=模块名` | 模块显示名 |
| `#!desc=一句话说明` | 模块描述 |
| `#!arguments=A:默认值,B:默认值` | 定义占位符 `{{{A}}}` 的默认值 |

其余 `#!` 指令(`#!system`、`#!category`、`#!managed-config` 等)不解析，写了也不报错。

占位符 `{{{A}}}` 在解析末尾展开，可用在模块名、描述、`[MITM]` 的 hostname、以及所有改写和脚本字段里。**`[Rule]`、`[Proxy Group]`、`[Host]` 三段不展开占位符**，写在那里就是字面的 `{{{A}}}`。客户端没有参数编辑界面，**永远使用 `#!arguments=` 里写的默认值**。所以除非要保留对其它客户端的兼容，直接把值写死更省事。

> 从文件导入的模块，列表里显示的是**文件名**(去掉扩展名)，不是 `#!name=`。取个好文件名。

## 段落清单

| 段 | 作用 | 语法详见 |
|---|---|---|
| `[Rule]` | 分流规则：什么流量走什么出口 | [routing.md](./routing.md) |
| `[Proxy Group]` | 策略组：出口本身怎么组织 | [routing.md](./routing.md) |
| `[Host]` | 域名的解析方式：静态映射，或指定用哪台 DNS 去查 | [routing.md](./routing.md) |
| `[General]` | **只认 `dns-server` 一个键**:改直连域名用哪台 DNS | [routing.md](./routing.md) |
| `[Remote Rule]` | 外部规则集订阅(Loon 写法) | [routing.md](./routing.md) |
| `[Remote Filter]` | 按正则筛节点的具名分组(Loon 写法) | [routing.md](./routing.md) |
| `[MITM]` | 对哪些域名做 HTTPS 解密 | [mitm.md](./mitm.md) |
| `[URL Rewrite]` | 改写或拦截请求 URL | [mitm.md](./mitm.md) |
| `[Header Rewrite]` | 增删改请求/响应头 | [mitm.md](./mitm.md) |
| `[Body Rewrite]` | 改写请求/响应正文 | [mitm.md](./mitm.md) |
| `[Map Local]` | 用本地内容直接回一个响应 | [mitm.md](./mitm.md) |
| `[Script]` | 用 JavaScript 处理请求/响应 | [mitm.md](./mitm.md) |

**不在这张表里的段一律被忽略**，不报错、不影响其它段。常见的被忽略段：`[Proxy]`、`[Panel]`、`[Replica]`、`[SSID Setting]`，以及各家客户端的其它私有段。`[General]` 除了 `dns-server` 之外的键也在此列。所以模块里**不能**用 `[Proxy]` 定义节点 —— 节点在客户端里管理。

模块中的 DNS 配置只控制目标域名的解析方式：`[Host]` 可为特定域名指定 DNS，也可使用保留值 `server:fakeip` 分配虚拟 IP，使 `[Rule]` 决定其出口；`[General] dns-server` 可替换直连域名使用的解析器。**节点和入口域名的解析不受模块影响**，始终使用客户端内置的解析路径。

## 格式与扩展名

导入时按扩展名判断源格式，认不出再看内容。

| 扩展名 | 格式 |
|---|---|
| `.sgmodule` | Surge |
| `.plugin` | Loon |
| `.stoverride` | Stash(YAML) |
| `.snippet` | Quantumult X |
| `.conf` / `.module` | Shadowrocket |

**自己写就写 Surge 格式，存成 `.sgmodule`。** 本手册全部语法以 Surge 为准。Loon / Stash / Quantumult X 的模块可以直接导入，但它们各自的私有写法只做到「不报错」，不保证逐字段等价。

## 导入与优先级

两种导入方式：

- **从文件导入**:选本机的 `.sgmodule` 文件。
- **从链接导入**:填一个能公开访问的 URL，内容就是模块原文。之后可以按这个链接重新拉取更新。

同一个链接重复导入会原地覆盖，不会出现两份。

模块列表里**越靠上优先级越高**，可以拖动排序。多个模块的规则合在一起后，上面模块的规则排在下面模块之前。模块内部则是从上到下，先命中先生效。

模块的规则排在客户端自带的**分流规则**之前，所以模块可以刻意覆盖内置的国内直连之类的行为。

但客户端还有几条**前置规则**排在所有模块规则之前，模块盖不掉：

| 前置规则 | 后果 |
|---|---|
| 私网地址 → 直连 | 模块里针对 `192.168.x.x`、`10.x.x.x` 这类地址的规则永远不命中 |
| BitTorrent → 直连 | 模块管不了 BT 流量 |
| ICMP → 直连 | 模块不能修改 ping 流量的出口 |
| 「直连」/「全局」模式 | 用户切到这两个模式时，**所有模块规则整体失效**，流量一律按模式走 |

排查「规则写了没反应」时先想想是不是撞上了这几条。

## 独立规则列表

一份没有任何 `[段名]`、每行只有 `类型,值` 而没有出口列的纯规则文件(常见扩展名 `.list`)也能直接导入。导入时客户端会让你为**整份列表**选一个出口。

```
DOMAIN-SUFFIX,openai.com
DOMAIN-SUFFIX,chatgpt.com
DOMAIN-KEYWORD,openai
```

这种文件里的注释、`payload:` 头、以及 Clash YAML 的 `  - DOMAIN-SUFFIX,x` 列表写法都能识别。

**能用的类型比 `[Rule]` 少**:只认 [routing.md](./routing.md) 规则类型全表里的那些，`PROTOCOL`、`AND` / `OR`、`FINAL` 在这种文件里一律被忽略。`RULE-SET` 与 `[Remote Rule]` 拉回来的外部规则集内容同理。
