脚本(Script)
Loon 3.5.1 (983) 起支持的新版 Script 语法统一了 Request、Response、Cron、Network Changed 和 Generic Script 的配置格式,并为 HTTP Script 增加了多条件匹配。
脚本内可用的 JavaScript 对象和方法没有改变,参见 Script API。
主配置 [Script]、[Remote Script] 和插件 [Script] 使用相同的语法、默认值与校验规则。插件 Script 还可以引用当前插件的 [Argument] 参数。
可以使用 Script 配置编辑器 生成新语法,或使用 Script 语法转换器 迁移旧版配置。
快速开始
所有类型都使用统一的 script(...) Action:
HTTP <request|response> if <condition> then script(<path> [, <argument>]) [with <options>]
Cron cron <cron-expression> then script(<path> [, <argument>]) [with <options>]
Network Changed network-changed then script(<path> [, <argument>]) [with <options>]
Generic generic then script(<path> [, <argument>]) [with <options>]
一个完整的 HTTP 示例:
request if ${url} ~= /^https:\/\/api\.example\.com/i && ${request.method} == "POST" then script("request.js", "source=profile") with tag="Request Script", timeout=20, requires_body=true
每条配置由三部分组成:
| 部分 | 作用 | 示例 |
|---|---|---|
| Trigger / Condition | 决定何时触发或是否命中 | request if ...、cron "..." |
| Script Action | 指定脚本路径和 $argument | script("request.js", "debug=true") |
with | 设置该条指令的属性 | with tag="Request", timeout=20 |
Script 类型
Request Script
在请求发出前匹配并执行:
request if ${url} ~= /\/api\// && ${request.method} == "POST" then script("request.js") with requires_body=true
- 省略
requires_body或设为false时,在 Request Header 阶段执行。 requires_body=true时,等待完整 Request Body 后执行。- 同一请求最多选择一条 Request Script,按最终配置顺序使用第一条完整命中的规则。
Response Script
根据请求和原始响应信息匹配:
response if ${url} ~= /\/api\// && ${response.status} == 200 && ${response.header['Content-Type']} ~= /application\/json/i then script("response.js") with requires_body=true
- 每条 Response Script 必须包含强制 URL Guard,详见 Response URL Guard。
- 省略
requires_body或设为false时,在 Response Header 阶段执行。 requires_body=true时,等待完整 Response Body 后执行。- 同一响应最多选择一条 Response Script,并保持原配置顺序。
Cron Script
按 Cron 表达式定时执行:
cron "0 8 * * *" then script("cron.js") with tag="Daily Task", timeout=300
支持五段或六段格式:
* * * * * 分 时 日 月 周
* * * * * * 秒 分 时 日 月 周
插件可以使用 String 类型参数提供动态 Cron:
cron ${cron} then script("cron.js", {${region}}) with enable=${enabled}, tag="Plugin Cron"
到期的 Cron Script 继续按照现有调度与并发规则执行,不使用 HTTP Script 的“第一条命中”规则。
Network Changed Script
在网络变化判定成立时触发:
network-changed then script("network.js") with tag="Network Changed", timeout=30
同一次网络变化事件会执行所有已启用的 Network Changed Script。
Generic Script
作为可从 App 或现有入口手动执行的脚本 :
generic then script("switch-node.js", "region=CN") with tag="Switch Node", img_url="arrow.triangle.swap.system", timeout=30
每条 Generic Script 都是独立操作,不会自动执行。
script(...) Action
方法声明
script(String[, String|RawString|PluginObject])
第一个参数是脚本路径,第二个参数可选并作为 $argument 传入脚本。
script("request.js")
script("request.js", "hello")
script("request.js", {${region}, ${level}})
一条规则只能包含一个 script(...),不支持 Rewrite 的 | Action 管道。
脚本路径
路径必须是非空的固定字符串,可以是本地文件、相对路径或远程 URL:
script("local.js")
script("folder/local.js")
script("https://example.com/script.js")
路径不能使用变量或模板:
# 无效
script(${scriptPath})
script("${region}.js")
本地与远程识别、下载、缓存和路径查找继续使用现有逻辑。
$argument 参数
第二个参数的写法直接决定脚本中 $argument 的类型。
| 配置 | $argument 类型 |
|---|---|
| 省略第二个参数 | null |
| String 或 Raw String | String |
| 插件对象参数 | Object |
无参数
request if ${url} ~= /api/ then script("request.js")
console.log($argument); // null
字符串参数
generic then script("tool.js", "region=CN&level=2")
脚本收到原始 String;Loon 不会自动解析 JSON、查询字符串或其他业务格式:
console.log(typeof $argument); // string
需要传递包含大量引号或换行的文本时,可以使用 Raw String:
generic then script("tool.js", `{"region":"CN","level":2}`)
const params = JSON.parse($argument);
插件对象参数
插件 Script 可以选择多个 [Argument] 参数,并让 $argument 成为 Object:
[Argument]
region = select,"CN","US",tag=地区
level = select,1,2,3,type=number,tag=等级
enabled = switch,true,tag=启用
[Script]
generic then script("plugin.js", {${region}, ${level}, ${enabled}})
脚本中得到:
$argument = {
region: "CN",
level: 2,
enabled: true
};
对象参数不是通用 JavaScript Object 字面量。花括号中只能填写当前插件已声明的 ${name}:
# 有效
script("plugin.js", {${region}, ${level}})
# 无效
script("plugin.js", {})
script("plugin.js", {"CN", 2})
script("plugin.js", {${url}})
script("plugin.js", {${region}, ${region}})
规则如下:
- Object 不能为空,同一个变量不能重复。
- 只能引用当前插件
[Argument]中声明的参数。 - Object Key 使用参数名,Value 保留插件参数的 String、Number 或 Boolean 类型。
- 本地 Script 和普通 Remote Script 没有插件参数作用域,不能使用对象参数。
- 字符串参数与插件对象参数互斥,不支持第三个参数。
with 指令属性
with 用于配置 Script 指令本身:
with <name>=<value> [, <name>=<value> ...]
request if ${url} ~= /api/ then script("request.js") with enable=true, tag="API Script", img_url="api.system", timeout=20, debug=true, requires_body=true, binary_body_mode=false
支持字段
| 字段 | 类型 | 默认值 | 适用范围 |
|---|---|---|---|
enable | Boolean / 插件 Boolean | true | 全部 Script |
tag | String | 从脚本路径派生 | 全部 Script |
img_url | String | 无 | 全部 Script |
timeout | Number | 保持各类型现有默认值 | 全部 Script |
debug | Boolean | false | 全部 Script |
requires_body | Boolean | false | Request / Response |
binary_body_mode | Boolean | false | Request / Response |
requires_body 决定是否等待完整 Body;binary_body_mode 只决定 Body 使用现有二进制表示方式,不会自动开启 requires_body。
Cron、Network Changed 和 Generic 没有 HTTP Body,不能设置 requires_body 或 binary_body_mode。
字段规则
- 没有字段时省略整个
with。 - 字段名区分大小写,并统一使用小写 snake_case。
- 字段不能重复,未知字段会导致当前规则无效。
enable、debug、requires_body和binary_body_mode必须是 Boolean。timeout必须是大于0的有限 Number。tag和img_url必须是 String。- 插件中的
enable可以引用 Boolean 类型参数,如${enabled}。 - 除动态
enable外,其他字段不接受变量或字符串模板。
HTTP 条件表达式
HTTP Script 的条件语法与新版 Rewrite 的条件表达式一致,但不支持命名捕获。
比较与逻辑操作符
| 操作符 | 说明 |
|---|---|
== | 类型一致的精确比较 |
~= | 正则查找匹配 |
&& | 并且 |
|| | 或者 |
() | 显式分组 |
优先级为:
比较 > && > ||
request if ${request.method} == "POST" && (${request.header['X-Region']} == "CN" || ${request.header['X-Region']} == "HK") then script("request.js")
逻辑表达式使用短路求值。建议在同时使用 && 和 || 时用括号明确业务意图。