scriptcat-userscript
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWriting ScriptCat userscripts
编写 ScriptCat 用户脚本
ScriptCat runs Tampermonkey-style userscripts and adds two kinds of its own: background scripts and scheduled
scripts, which run in the extension's background sandbox instead of a web page. Most Tampermonkey knowledge
carries over; the differences below are the ones that break scripts.
Authority. The API surface is — in this repository , otherwise
https://raw.githubusercontent.com/scriptscat/scriptcat/main/src/types/scriptcat.d.ts. Working examples live in
https://github.com/scriptscat/scriptcat/tree/main/example. The developer docs at https://docs.scriptcat.org/docs/dev/
can lag behind the types; when they disagree, trust the types.
scriptcat.d.tssrc/types/scriptcat.d.tsScriptCat 可以运行 Tampermonkey 风格的用户脚本,并新增两类自身特有的脚本:后台脚本和定时脚本。这两类脚本在扩展的后台沙箱中运行,而不是在网页中运行。大多数 Tampermonkey 知识都适用;以下差异才是会导致脚本失效的地方。
权威依据。 API 接口以 为准——在本仓库中位于 ,否则位于 https://raw.githubusercontent.com/scriptscat/scriptcat/main/src/types/scriptcat.d.ts。可运行的示例位于 https://github.com/scriptscat/scriptcat/tree/main/example。开发者文档 https://docs.scriptcat.org/docs/dev/ 可能落后于类型定义;二者发生冲突时,以类型定义为准。
scriptcat.d.tssrc/types/scriptcat.d.ts1. Pick the script type
1. 选择脚本类型
| The script must… | Type | Key metadata |
|---|---|---|
| Change, read or automate a web page | Page script | |
| Keep running while the browser is open, with no page (listeners, polling, a menu in the popup) | Background script | |
| Run on a schedule (check-in, sync, report, periodic fetch) | Scheduled script | |
Background and scheduled scripts have no web page: no of any site, no page, no
/. To touch a page from the background, open it with and let a page
script do the DOM work, or fetch data with instead of scraping a rendered page.
documentunsafeWindowGM_getTabGM_saveTabGM_openInTabGM_xmlhttpRequest| 脚本必须…… | 类型 | 关键元数据 |
|---|---|---|
| 更改、读取或自动化处理网页 | 页面脚本 | |
| 浏览器打开期间持续运行,即使没有页面(监听器、轮询、弹出窗口中的菜单) | 后台脚本 | |
| 按计划运行(签到、同步、报告、定期请求) | 定时脚本 | |
后台脚本和定时脚本没有网页:无法访问任何站点的 ,也没有页面的 ,并且不能使用 /。如需从后台操作页面,请使用 打开页面,再让页面脚本完成 DOM 操作;或者使用 获取数据,而不是抓取已渲染的页面。
documentunsafeWindowGM_getTabGM_saveTabGM_openInTabGM_xmlhttpRequest2. Start from the right skeleton
2. 从正确的骨架开始
These match the templates ScriptCat's own "new script" uses.
js
// ==UserScript==
// @name Example page script
// @namespace https://example.com/your-namespace
// @version 0.1.0
// @description What it does, in one line
// @author You
// @match https://example.com/*
// @grant none
// @noframes
// ==/UserScript==
(function () {
"use strict";
// ...
})();js
// ==UserScript==
// @name Example background script
// @namespace https://example.com/your-namespace
// @version 0.1.0
// @description What it does, in one line
// @author You
// @background
// @grant GM_xmlhttpRequest
// @grant GM_notification
// ==/UserScript==
return new Promise((resolve, reject) => {
// ... resolve() when the work is really finished, reject(err) on failure
});A scheduled script is the background skeleton with (or another expression) in place of
. Background and scheduled bodies are wrapped in a function by ScriptCat, so a top-level
is legal there.
@crontab * * once * *@backgroundreturn这些骨架与 ScriptCat 自身“新建脚本”所使用的模板一致。
js
// ==UserScript==
// @name Example page script
// @namespace https://example.com/your-namespace
// @version 0.1.0
// @description What it does, in one line
// @author You
// @match https://example.com/*
// @grant none
// @noframes
// ==/UserScript==
(function () {
"use strict";
// ...
})();js
// ==UserScript==
// @name Example background script
// @namespace https://example.com/your-namespace
// @version 0.1.0
// @description What it does, in one line
// @author You
// @background
// @grant GM_xmlhttpRequest
// @grant GM_notification
// ==/UserScript==
return new Promise((resolve, reject) => {
// ... resolve() when the work is really finished, reject(err) on failure
});定时脚本使用后台脚本骨架,但以 (或其他表达式)代替 。ScriptCat 会将后台脚本和定时脚本的主体包装在一个函数中,因此这些脚本的顶层 是合法的。
@crontab * * once * *@backgroundreturn3. Rules that break scripts when ignored
3. 忽略后会导致脚本出问题的规则
Identity and updates
- +
@nameidentify a script. Keep both stable once the user has installed it; changing either makes a different script. Bump@namespaceon every change you hand back.@version - A page script cannot become a background or scheduled script (or the reverse) under the same +
@name: ScriptCat rejects that install with a script type mismatch. Give a temporary script of the other type its own@namespace.@name
Permissions
- Every API needs a line (
@grant, …). An ungranted API is simply undefined at runtime. Granting@grant GM_xmlhttpRequestalso exposesGM_fooand vice versa — only when the names match exactly:GM.fooneeds its ownGM.xmlHttpRequest(@grant GM.xmlHttpRequestdoes not expose it).@grant GM_xmlhttpRequest - needs no grant.
GM_infoneeds no grant either, but only exists in the sandbox (any grant other thanunsafeWindow, or nononeline at all);@grantis harmless.@grant unsafeWindow - runs the script without the sandbox:
@grant noneis the page's own window, and the only API iswindow/GM_info— no other GM API and noGM.info(referencing it throws aunsafeWindow). Use it only for scripts that need nothing but the page.ReferenceError - With any other grant — and also with no line at all, unlike Tampermonkey — the script is sandboxed: its
@grantis not the page's. Reach page globals (a site's own JS objects, variables it defines) throughwindow.unsafeWindow - Declare every host you request with (it also covers the host's subdomains;
@connect <host>covers subdomains of the page's host; requests to exactly the page's host need nothing). For a host that is not covered,@connect selfis refused ("This domain is not a part of the @connect list") as soon as the script has anyGM_xmlhttpRequestline, unless the user allowed that host before; only a script with no@connectat all asks the user instead.@connectin native mode always asks, andGM_downloadthrows for a host outsideGM_cookie.@connect
Page timing and SPAs
- :
@run-at(as early as possible),document-start(DOM parsed),document-end(the default; everything loaded),document-idle(wait untildocument-bodyexists),<body>(run when the user picks the script from the page's right-click menu). Only the first value counts, and an unknown value silently falls back to the default.context-menu - Add together with
@early-startwhen the script must beat the page's own scripts, and wait fordocument-startbefore relying on the full API.CAT_scriptLoaded() - Single-page apps change the URL without reloading, so a page script runs once and then misses later views.
Listen with and
@grant window.onurlchange, and use awindow.addEventListener("urlchange", e => …)for content that renders late. Never assume an element exists at injection time — wait for it.MutationObserver - keeps the script out of iframes; without it, a
@noframesmatch also runs inside every iframe.*://*/* - runs in the content-script world: unaffected by the page's CSP, but
@inject-into contentis then not the page'sunsafeWindow. The default iswindow.page - and
GM_addStylebypass the page's CSP; prefer them over creatingGM_addElement/<style>by hand. Exception: on Firefox a<script>injected with<script>is still subject to the page's CSP.GM_addElement
Storage
- stores JSON-like data only (no class instances). Setting
GM_setValuedeletes the key (Tampermonkey stores it instead).undefined - Writes are asynchronous. Before or the end of a background run,
window.close()/await GM.setValue(...)so the write is not lost.await GM.setValues(...) - lets several scripts share one value store;
@storageName <name>sees writes from the other scripts (and from other tabs) — a simple cross-script channel.GM_addValueChangeListener
Background and scheduled scripts
- Return a and settle it after the last asynchronous step. Once it resolves or rejects, ScriptCat treats the run as finished and later GM calls from that run are not guaranteed to work. ScriptCat wraps the body in an
Promisefunction, so a body that returns nothing is marked complete as soon as its synchronous part (and any top-levelasync) finishes — pending callbacks and timers do not keep the run open.await - is logged as success,
resolve(value)as an error; the script list then shows the run status as error, and the message is in the log.reject(err) - To retry, (or a
reject(new CATRetryError("reason", seconds))).Dateis undefined underCATRetryError; it exists with any other grant or with no@grant noneline. The retry queue is checked every 5 seconds, so a retry fires up to 5 seconds late; keep run time plus retry delay shorter than the cron interval so runs do not overlap.@grant - Write exactly one line. Every line is scheduled at runtime, but installation validates and the script list shows only the first, and one invalid extra line stops all of the script's jobs. Use 5 fields (
@crontab); a 6-field form with seconds exists but is not recommended. Themin hour day month weekdayextension limits a field's period to one run, counted when the run starts — a failed run uses up the period too, so retry failures withonce:CATRetryError= at most once a day,* * once * *= at most once an hour,* once * * *= once in each hour from 9 to 17. Full cron and* once(9-17) * * *rules: references/background.md.once - Log with : it lands in ScriptCat's log, the only place a background script's output shows up — ScriptCat's Logs page (filter by the script's
GM_log(message, "debug" | "info" | "warn" | "error")orname), oruuid. The script list shows only the run status (running / error / stopped) and a scheduled script's next run time. Release builds do not storesctl logs, so log anything someone must read atdebugor above.info
User configuration
- A YAML block after the header gives the script a settings form in ScriptCat. Read a value with
/* ==UserConfig== … ==/UserConfig== */. Format: references/userconfig.md.GM_getValue("group.key")
身份标识与更新
- 和
@name共同标识一个脚本。用户安装后应保持两者不变;修改其中任何一个都会被识别为另一个脚本。每次交回修改后的脚本时,都应递增@namespace。@version - 在 和
@name保持不变的情况下,页面脚本不能变为后台脚本或定时脚本,反之亦然;ScriptCat 会因脚本类型不匹配而拒绝安装。另一类型的临时脚本应使用独立的@namespace。@name
权限
- 每个 API 都需要一行 (
@grant等)。未授权的 API 在运行时将直接是@grant GM_xmlhttpRequest。授权undefined也会公开GM_foo,反之亦然——但仅限名称完全一致的情况:GM.foo需要自己的GM.xmlHttpRequest(@grant GM.xmlHttpRequest不会公开它)。@grant GM_xmlhttpRequest - 无需授权。
GM_info也无需授权,但它仅存在于沙箱中(使用除unsafeWindow以外的任何 grant,或者完全不写none行);@grant不会造成影响。@grant unsafeWindow - 使用 时,脚本在无沙箱环境中运行:
@grant none是页面自身的window,唯一可用的 API 是window/GM_info——其他任何 GM API 都不可用,而且没有GM.info(引用它会抛出unsafeWindow)。仅将其用于完全不需要页面之外能力的脚本。ReferenceError - 使用任何其他 grant 时——与 Tampermonkey 不同,完全不写 行时也会进入沙箱——脚本的
@grant并不是页面的window。应通过window访问页面全局对象(网站自身定义的 JS 对象和变量)。unsafeWindow - 使用 声明每一个要请求的主机(它也涵盖该主机的子域;
@connect <host>涵盖页面主机的子域;请求与页面主机完全相同的主机则无需声明)。对于未被涵盖的主机,只要脚本存在任何@connect self行,@connect就会立即被拒绝(“This domain is not a part of the @connect list”),除非用户此前已允许该主机;只有完全没有GM_xmlhttpRequest行的脚本才会改为询问用户。原生模式下的@connect始终会询问用户,而GM_download对GM_cookie范围之外的主机会抛出异常。@connect
页面执行时机与 SPA
- :
@run-at(尽早执行)、document-start(DOM 已解析)、document-end(默认;所有内容均已加载)、document-idle(等待document-body存在)、<body>(当用户从页面右键菜单中选择该脚本时运行)。仅第一个值生效,未知值会静默回退到默认值。context-menu - 当脚本必须早于页面自身的脚本运行时,请在 的同时添加
document-start,并在依赖完整 API 前等待@early-start。CAT_scriptLoaded() - 单页应用会在不重新加载页面的情况下更改 URL,因此页面脚本运行一次后就会错过后续视图。请使用 和
@grant window.onurlchange监听变化,并使用window.addEventListener("urlchange", e => …)处理延迟渲染的内容。切勿假定元素在注入时已经存在——应等待它出现。MutationObserver - 可防止脚本在 iframe 中运行;如果没有它,
@noframes匹配规则还会使脚本在每个 iframe 中运行。*://*/* - 会在 content-script world 中运行:不受页面 CSP 影响,但此时
@inject-into content并不是页面的unsafeWindow。默认值为window。page - 和
GM_addStyle可以绕过页面的 CSP;应优先使用它们,而不是手动创建GM_addElement/<style>。例外情况:在 Firefox 上,通过<script>注入的GM_addElement仍然受页面 CSP 约束。<script>
存储
- 只能存储类 JSON 数据(不能存储类实例)。设置
GM_setValue会删除对应键(Tampermonkey 则会存储该值)。undefined - 写入操作是异步的。在调用 或后台运行结束之前,应执行
window.close()/await GM.setValue(...),以免写入内容丢失。await GM.setValues(...) - 可让多个脚本共享同一个值存储;
@storageName <name>能监听其他脚本(以及其他标签页)的写入——这是一个简单的跨脚本通信通道。GM_addValueChangeListener
后台脚本和定时脚本
- 返回一个 ,并确保它在最后一个异步步骤完成后才结束。Promise 一旦 resolve 或 reject,ScriptCat 就会将本次运行视为完成,此后该次运行中的 GM 调用不再保证可用。ScriptCat 会将主体包装在一个
Promise函数中,因此没有返回值的脚本会在其同步部分(以及任何顶层async)结束时立即被标记为完成——待处理的回调和计时器不会让运行保持开启。await - 会记录为成功,
resolve(value)会记录为错误;随后脚本列表会将运行状态显示为错误,错误消息则出现在日志中。reject(err) - 如需重试,请使用 (或传入一个
reject(new CATRetryError("reason", seconds)))。在Date下,@grant none是 undefined;使用任何其他 grant 或完全不写CATRetryError行时,它才存在。重试队列每 5 秒检查一次,因此重试最多可能延迟 5 秒;应确保运行时长加重试延迟短于 cron 间隔,以免各次运行发生重叠。@grant - 必须且只能写一行 。虽然每一行都会在运行时被调度,但安装时会进行校验,脚本列表中也只显示第一行;多出的任何无效行都会停止该脚本的所有任务。使用 5 个字段(
@crontab);也存在带秒的 6 字段形式,但不推荐。min hour day month weekday扩展会将某个字段的周期限制为仅运行一次,并以运行开始时计数——运行失败也会占用该周期,因此应使用once重试失败的运行:CATRetryError= 每天最多运行一次,* * once * *= 每小时最多运行一次,* once * * *= 每天 9 点至 17 点期间每小时运行一次。完整的 cron 和* once(9-17) * * *规则请参阅 references/background.md。once - 使用 记录日志:日志会进入 ScriptCat 的日志系统,这是后台脚本输出唯一可见的位置——可在 ScriptCat 的“日志”页面(按脚本的
GM_log(message, "debug" | "info" | "warn" | "error")或name过滤),或使用uuid查看。脚本列表只显示运行状态(运行中 / 错误 / 已停止)以及定时脚本的下次运行时间。发布版本不会存储sctl logs级别,因此需要用户阅读的内容应以debug或更高级别记录。info
用户配置
- 在脚本头之后添加 YAML 块,可为脚本提供 ScriptCat 设置表单。使用
/* ==UserConfig== … ==/UserConfig== */读取值。格式请参阅 references/userconfig.md。GM_getValue("group.key")
4. API lookup
4. API 查询
references/api.md lists every GM and CAT API ScriptCat implements, with the grant name, the
cases where ScriptCat differs from Tampermonkey, and which APIs are unavailable in the background. Read it
before using an API you are unsure about, then check the exact signature in .
scriptcat.d.tsreferences/patterns.md has ready-made building blocks: waiting for an element,
single-page-app route changes, a persistent menu switch, cross-origin requests, styles, localisation, and a
background run with retry.
references/api.md 列出了 ScriptCat 实现的每个 GM 和 CAT API,包括对应的 grant 名称、ScriptCat 与 Tampermonkey 存在差异的场景,以及在后台脚本中不可用的 API。使用不确定的 API 前应先阅读该文档,然后检查 中的准确签名。
scriptcat.d.tsreferences/patterns.md 提供了现成的构建模块:等待元素、单页应用路由变更、持久菜单开关、跨源请求、样式、本地化,以及带重试的后台运行。
5. Install and verify
5. 安装与验证
Writing the file is half the job; the other half is seeing it run.
First run the static check bundled with this skill (Node 18+, no dependencies; paths are relative to this
skill's directory):
sh
node scripts/check.mjs my-script.user.js # exit 1 = errors that will break the script at runtimeIt reports : APIs used without their , GM APIs or
under , a page script without , invalid expressions, request hosts
missing from , and similar. Fix every error; read each warning and fix it or say why it does not apply.
It is a static check — it cannot tell whether the script does what the user asked.
error|warn <rule> <file>:<line> <message>@grantunsafeWindow@grant none@match@crontab@connect- sctl is available (shows ScriptCat connected): install it into the user's ScriptCat and verify it in their browser, following references/dev-loop.md. It also covers scouting the target page before you write code, iterating on a local file without an approval per change, and running background and scheduled scripts and reading their logs (
sctl status/sctl run). That loop runs inside the user's real browser, so the sctl skill's rules apply too — read it first if it is installed.sctl logs - No sctl: save the code as and tell the user to install it — drag the file onto the ScriptCat dashboard, or create a new script in ScriptCat and paste the code. Say which page to open (page script) or where to look (background: the run status in the script list, and the script's output on ScriptCat's Logs page).
<name>.user.js
Whichever path you take, report what you actually observed: "installed and the button appears on
example.com" is a result; "should work" is not.
编写文件只是完成了一半;另一半是确认它实际运行。
首先运行此 skill 附带的静态检查(需要 Node 18+,无依赖;路径相对于此 skill 的目录):
sh
node scripts/check.mjs my-script.user.js # exit 1 = errors that will break the script at runtime它会报告 :未声明 就使用 API、在 下使用 GM API 或 、页面脚本缺少 、无效的 表达式、请求主机未列入 等。必须修复每个 error;逐一阅读每个 warning,并修复它或说明为何不适用。这只是静态检查——无法判断脚本是否实现了用户要求的功能。
error|warn <rule> <file>:<line> <message>@grant@grant noneunsafeWindow@match@crontab@connect- sctl 可用(显示 ScriptCat 已连接):按照 references/dev-loop.md 将脚本安装到用户的 ScriptCat,并在其浏览器中验证。该流程还涵盖在编写代码前勘察目标页面、无需每次修改都获得批准即可迭代本地文件,以及运行后台脚本和定时脚本并读取其日志(
sctl status/sctl run)。该流程会在用户的真实浏览器中运行,因此 sctl skill 的规则也适用——如果已安装,请先阅读它。sctl logs - 没有 sctl:将代码保存为 ,并告知用户如何安装——把文件拖到 ScriptCat 仪表板上,或在 ScriptCat 中新建脚本并粘贴代码。同时说明应打开哪个页面(页面脚本),或者去哪里查看结果(后台脚本:脚本列表中的运行状态,以及 ScriptCat“日志”页面中的脚本输出)。
<name>.user.js
无论采用哪种路径,都应报告实际观察到的结果:“已安装,且按钮出现在 example.com 上”属于验证结果;“应该可以运行”则不属于。