脚本开发文档2.2

发布于 22 小时前  13 次阅读


Cat Browser 脚本开发文档 v2.2

目录

  1. 概述
  2. 快速开始
  3. 环境检测
  4. GM API 标准兼容
    · 4.1 存储类
    · 4.2 样式注入
    · 4.3 网络请求
    · 4.4 其他方法
  5. 特权对象:cyc_privileges
    · 5.1 UI 交互
    · 5.2 资源嗅探(阶段 B)
    · 5.3 视频悬浮窗(阶段 C)
    · 5.4 下载能力(阶段 D)
    · 5.5 网络调试(阶段 A)
  6. 特权对象:cyc_http
  7. 特权对象:cyc_monitor
  8. 完整脚本示例
    · 8.1 万能视频小窗播放器
    · 8.2 网络请求实时监控面板
    · 8.3 批量下载页面图片
    · 8.4 B站视频下载(带格式转换)
  9. 限制与注意事项

  1. 概述

Cat Browser 为油猴脚本(UserScript)提供了一个增强的运行时环境。除了兼容标准 GM_* API 外,还额外提供三个特权对象,让脚本可以调用 App 的原生能力,实现普通浏览器做不到的功能。

三个特权对象:

对象名 用途 注入条件
cyc_privileges UI 交互、资源嗅探、视频悬浮窗、下载、调试开关 仅 http:// / https://
cyc_http 原生跨域 HTTP 请求 仅 http:// / https://
cyc_monitor 页面网络请求监控 仅 http:// / https://

兼容性承诺:
所有特权对象在普通浏览器(Chrome + Tampermonkey 等)中均为 undefined,脚本只需做一次存在性检查即可实现降级兼容。

支持的标准:
Cat Browser 兼容主流 GM_* API,无需在 @grant 中声明即可直接使用 GM_setValue、GM_xmlhttpRequest 等全部方法。


  1. 快速开始

最小可运行脚本模板:// ==UserScript== // @name 我的脚本 // @namespace my-script // @version 1.0.0 // @description 脚本描述 // @author 作者名 // @match *://*/* // @grant none // ==/UserScript== (function() { 'use strict'; // 环境检测 const isCyc = typeof __cyc_privileges__ !== 'undefined'; if (isCyc) { __cyc_privileges__.toast('Cat Browser 增强环境已就绪'); } else { console.log('普通浏览器环境,部分功能不可用'); } // 你的脚本逻辑... })();

关键说明:

· @match :///* 让脚本在所有 HTTP/HTTPS 页面运行
· @grant none 表示不申请额外权限(Cat Browser 已默认开放所有 GM API)
· 所有代码包裹在 IIFE (function(){ … })() 中,避免污染全局作用域
· 'use strict' 启用严格模式,减少潜在错误


  1. 环境检测

3.1 检测是否在 Cat Browser 中运行const isCatBrowser = typeof __cyc_privileges__ !== 'undefined';

3.2 检测是否在 HTTP/HTTPS 页面

特权对象仅在 HTTP/HTTPS 页面注入,file:// 或 about:blank 不可用。const isHttpPage = window.location.protocol === 'http:' || window.location.protocol === 'https:';

3.3 完整的环境信息const env = { isCatBrowser: typeof __cyc_privileges__ !== 'undefined', isHttpPage: /^https?:/.test(window.location.href), hasPrivileges: typeof __cyc_privileges__ !== 'undefined', hasHttp: typeof __cyc_http__ !== 'undefined', hasMonitor: typeof __cyc_monitor__ !== 'undefined', hasGMApi: typeof GM_setValue === 'function', url: window.location.href, gmInfo: typeof GM_info !== 'undefined' ? GM_info : null }; console.log('环境信息:', JSON.stringify(env, null, 2));


  1. GM API 标准兼容

Cat Browser 兼容主流 GM_* API,无需在 @grant 中声明即可直接使用。

4.1 存储类

方法 参数 返回值 说明
GM_setValue(key, value) key: 字符串, value: 任意类型(自动序列化) 无 持久存储键值对
GM_getValue(key, defaultValue) key: 字符串, defaultValue: 任意类型 存储的值或默认值 读取存储的值
GM_deleteValue(key) key: 字符串 无 删除存储的键
GM_listValues() 无 字符串数组 获取所有存储的键名// 存储复杂对象 GM_setValue('myKey', { name: 'test', count: 42 }); // 读取(带默认值) const data = GM_getValue('myKey', { name: '', count: 0 }); console.log(data.name, data.count); // "test", 42 // 删除 GM_deleteValue('myKey'); // 列出所有键 const keys = GM_listValues(); console.log('已存储的键:', keys);

4.2 样式注入

方法 参数 说明
GM_addStyle(css) css: CSS 字符串 向页面注入样式GM_addStyle(` .my-highlight { background: yellow !important; border: 2px solid red; } `);

4.3 网络请求

方法 参数 说明
GM_xmlhttpRequest(details) details: 配置对象 发起跨域请求(兼容标准 GM API)

details 配置项:

属性 类型 必填 说明
url string 是 请求地址
method string 否 请求方法,默认 GET
headers object 否 请求头
data string 否 请求体
timeout number 否 超时毫秒数
onload function 否 成功回调
onerror function 否 失败回调GM_xmlhttpRequest({ url: 'https://api.example.com/data', method: 'GET', headers: { 'X-Custom': 'value' }, timeout: 10000, onload: function(response) { console.log('状态:', response.status); console.log('响应:', response.responseText); console.log('响应头:', response.responseHeaders); }, onerror: function(error) { console.error('请求失败:', error); } });

4.4 其他方法

方法 说明
GM_log(message) 输出日志到 App 控制台
GM_setClipboard(text) 复制文本到系统剪贴板
GM_openInTab(url, background) 在新标签页打开 URL(background: true 后台打开)
GM_notification(details, onclick) 显示浏览器通知
GM_info 获取脚本管理器信息对象
GM_registerMenuCommand(caption, callback) 注册菜单命令
GM_getResourceText(name) 获取 @resource 定义的文本资源内容
GM_getResourceURL(name) 获取 @resource 定义的资源 URL// 日志 GM_log('调试信息'); // 复制到剪贴板 GM_setClipboard('已复制的文本'); // 通知 GM_notification({ title: '标题', text: '内容' }); // 脚本管理器信息 console.log(GM_info.scriptHandler); // "Cat Browser" console.log(GM_info.version); // "1.0.0" // 菜单命令 GM_registerMenuCommand('我的功能', function() { alert('菜单被点击'); });


  1. 特权对象:cyc_privileges

注入条件: 仅在 http:// 和 https:// 页面可用。

5.1 UI 交互

toast(message)

· 参数: message — 字符串,要显示的文本
· 返回值: 无
· 说明: 弹出 Android 原生短 Toast(约 2 秒),线程安全

toastLong(message)

· 参数: message — 字符串,要显示的文本
· 返回值: 无
· 说明: 弹出 Android 原生长 Toast(约 3.5 秒),线程安全__cyc_privileges__.toast('操作完成'); __cyc_privileges__.toastLong('这是一条较长的提示信息');

5.2 资源嗅探(阶段 B)

getSniffedVideos()

· 参数: 无
· 返回值: JSON 字符串,解析后为数组 [{url, mimeType, size}, …]
· 说明: 获取当前页面已检测到的视频资源

getSniffedAudios()

· 参数: 无
· 返回值: JSON 字符串,解析后为数组
· 说明: 获取当前页面已检测到的音频资源

getSniffedImages()

· 参数: 无
· 返回值: JSON 字符串,解析后为数组
· 说明: 获取当前页面已检测到的图片资源

返回值对象结构:

字段 类型 说明
url string 资源 URL
mimeType string MIME 类型(如 video/mp4)
size number 文件大小(字节,-1 表示未知)const videos = JSON.parse(__cyc_privileges__.getSniffedVideos()); console.log('检测到 ' + videos.length + ' 个视频'); videos.forEach(function(v, i) { console.log('视频 ' + (i+1) + ':', v.url, v.mimeType, v.size + '字节'); });

资源更新事件:window.__cyc_on_resource_update

· 回调参数:
· type — 字符串,资源类型:"video" | "audio" | "image"
· urls — 字符串数组,新增的资源 URL
· 说明: 当页面新加载了匹配的资源时自动触发window.__cyc_on_resource_update = function(type, urls) { console.log('新发现 ' + type + ': ' + urls.length + ' 个'); if (type === 'video') { urls.forEach(function(url) { console.log('  ' + url); }); } };

5.3 视频悬浮窗(阶段 C)

getCurrentVideo()

· 参数: 无
· 返回值: 字符串,当前检测到的第一个视频 URL,无视频时返回空字符串 ""
· 说明: 获取页面上检测到的视频地址

playInFloatWindow(url)

· 参数: url — 视频 URL
· 返回值: 无
· 说明: 将指定视频在系统悬浮窗中播放(视频会以小窗口形式浮在其他 App 之上)const videoUrl = __cyc_privileges__.getCurrentVideo(); if (videoUrl) { __cyc_privileges__.playInFloatWindow(videoUrl); __cyc_privileges__.toast('已开启悬浮窗播放'); } else { __cyc_privileges__.toast('当前页面未检测到视频'); }

注意事项:

· 悬浮窗需要系统"悬浮窗权限",Cat Browser 通常会引导用户授权
· 视频格式需为系统播放器支持的格式(mp4、m3u8、ts 等)
· 如果页面有多个视频,getCurrentVideo() 返回第一个检测到的

5.4 下载能力(阶段 D)

download(url, filename, optionsJson)

· 参数:
· url — 下载地址
· filename — 保存的文件名
· optionsJson — 可选,JSON 字符串,包含额外选项:
· convertTo — 目标格式(不含点号),如 "mp4"、"mp3"、"mkv"、"png"。不传或为 null 则不转换
· 返回值: 无
· 说明: 触发原生下载管理器下载文件。若指定了 convertTo,下载完成后会自动使用 FFmpeg 转换为目标格式,转换成功后替换原文件。转换失败时保留原文件并提示。

batchDownload(urlsJson)

· 参数: urlsJson — JSON 字符串数组,要批量下载的 URL 列表
· 返回值: 无
· 说明: 批量触发下载,会弹出确认提示// 普通下载(不转换格式) __cyc_privileges__.download( 'https://example.com/video.m4s', '精彩视频.m4s' ); // 下载后自动转换为 MP4(视频) __cyc_privileges__.download( 'https://example.com/video.m4s', '精彩视频.mp4', JSON.stringify({ convertTo: 'mp4' }) ); // 下载后自动转换为 MP3(音频) __cyc_privileges__.download( 'https://example.com/audio.m4s', '背景音乐.mp3', JSON.stringify({ convertTo: 'mp3' }) ); // 图片格式转换 __cyc_privileges__.download( 'https://example.com/image.webp', '图片.png', JSON.stringify({ convertTo: 'png' }) ); // 批量下载 const urls = [ 'https://example.com/img1.jpg', 'https://example.com/img2.jpg', 'https://example.com/img3.jpg' ]; __cyc_privileges__.batchDownload(JSON.stringify(urls)); __cyc_privileges__.toast('开始下载 ' + urls.length + ' 个文件');

支持的转换格式:

类别 支持的输出格式
视频 mp4、mkv、avi、mov、webm、flv、3gp
音频 mp3、m4a、aac、ogg、opus、flac、wav、wma
图片 png、jpg、jpeg、webp、bmp、gif、tiff
其他 先尝试流复制,失败则使用通用编码器

转换策略:

  1. 第 1 次:流复制(-c copy),无损且极快
  2. 第 2 次:根据目标格式选择合适的编码器重新编码
  3. 第 3 次:降级保底参数(更低码率、更快预设),确保不超时

注意事项:

· optionsJson 必须使用 JSON.stringify() 序列化,不能直接传 JS 对象
· 转换在后台进行,用户会在下载列表中看到"转换中"状态
· 转换失败时原文件保留,任务显示在已完成列表并提示"格式转换失败,原文件已保留"
· 转换过程中不可暂停,但可取消

5.5 网络调试(阶段 A)

setRecordBody(enabled)

· 参数: enabled — 布尔值,是否开启响应体记录
· 返回值: 无
· 说明: 开启后,cyc_monitor 的回调中会包含 responseBody 字段// 开启响应体记录(会影响少量性能,调试完建议关闭) __cyc_privileges__.setRecordBody(true); // 关闭 __cyc_privileges__.setRecordBody(false);


  1. 特权对象:cyc_http

注入条件: 仅在 http:// 和 https:// 页面可用。

发起完全不受浏览器同源策略、CORS 限制、SameSite Cookie 策略限制的 HTTP 请求。使用 OkHttp 原生引擎,性能优于 GM_xmlhttpRequest。

request(requestId, optionsJson)

· 参数:
· requestId — 整数,自定义请求 ID(用于在回调中区分不同请求)
· optionsJson — JSON 字符串,包含:
· url — 必填,请求地址
· method — 可选,GET/POST/PUT/DELETE 等,默认 "GET"
· headers — 可选,请求头对象,默认 {}
· body — 可选,请求体字符串(POST 时使用)
· timeout — 可选,超时毫秒数,默认 10000
· 返回值: 无(异步,通过回调获取结果)
· 回调: 需全局定义 window.__cyc_http_callback = function(requestId, success, resultJson)

回调参数说明:

参数 类型 说明
requestId number 请求 ID,对应 request() 调用时的 ID
success boolean 请求是否成功(网络层无异常)
resultJson string JSON 字符串,解析后包含 status、responseText 等

resultJson 解析后的字段:

字段 类型 说明
status number HTTP 状态码(0 表示网络错误)
statusText string 状态文本或错误信息
responseText string 响应体文本
responseHeaders object 响应头键值对// 1. 定义全局回调 window.__cyc_http_callback = function(requestId, success, resultJson) { const result = JSON.parse(resultJson); if (requestId === 1) { if (success && result.status === 200) { const data = JSON.parse(result.responseText); console.log('请求成功:', data); console.log('响应头:', result.responseHeaders); } else { console.error('请求失败:', result.status, result.statusText); } } }; // 2. 发起 GET 请求 __cyc_http__.request(1, JSON.stringify({ url: 'https://jsonplaceholder.typicode.com/posts/1', method: 'GET', headers: { 'X-Custom-Header': 'my-value' }, timeout: 10000 })); // 3. POST 请求示例 __cyc_http__.request(2, JSON.stringify({ url: 'https://api.example.com/submit', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'test', value: 123 }), timeout: 15000 }));

与 GM_xmlhttpRequest 的区别:

特性 cyc_http.request() GM_xmlhttpRequest()
引擎 OkHttp(原生层) WebView 内部
CORS 完全不受限制 受浏览器策略限制
Cookie 不受 SameSite 限制 受 Cookie 策略限制
回调方式 全局单回调函数 每个请求独立回调


  1. 特权对象:cyc_monitor

注入条件: 仅在 http:// 和 https:// 页面可用。

实时监控页面发出的所有网络请求,采用"订阅制"模式,脚本需先注册关心的 URL 模式。

7.1 订阅管理方法

subscribe(scriptId, subscriptionId, pattern)

· 参数:
· scriptId — 脚本 ID(整数,用于区分不同脚本,可自定义)
· subscriptionId — 订阅 ID(字符串,用于后续取消订阅)
· pattern — URL 匹配模式(字符串,忽略大小写,包含即匹配)
· 返回值: 无
· 限制: 每个脚本最多 10 个订阅,全局最多 100 个

unsubscribe(scriptId, subscriptionId)

· 参数: 与 subscribe 一致
· 返回值: 无

unsubscribeAll(scriptId)

· 参数: scriptId — 脚本 ID
· 返回值: 无
· 说明: 取消该脚本的所有订阅

7.2 回调:window.__cyc_on_request

回调参数: info 对象,包含以下字段:

字段 类型 说明
scriptId number 脚本 ID
subscriptionId string 匹配的订阅 ID
url string 请求 URL(敏感参数已擦除)
method string 请求方法(GET/POST 等)
status number 响应状态码(-1 表示仅请求阶段)
duration number 请求耗时(毫秒)
requestHeaders object 请求头键值对
responseHeaders object 响应头键值对
responseBody string 响应体文本(需开启 setRecordBody(true),仅文本类型,最大 50KB)// 1. 定义回调 window.__cyc_on_request = function(info) { console.log('══════════════════════════════'); console.log('请求方法:', info.method); console.log('请求 URL:', info.url); console.log('状态码:', info.status); console.log('耗时:', info.duration + 'ms'); console.log('请求头:', JSON.stringify(info.requestHeaders)); console.log('响应头:', JSON.stringify(info.responseHeaders)); if (info.responseBody) { console.log('响应体 (前100字符):', info.responseBody.substring(0, 100)); } }; // 2. 开启响应体记录(阶段 A) __cyc_privileges__.setRecordBody(true); // 3. 订阅特定 API 接口 __cyc_monitor__.subscribe(1, 'api-monitor', '/api/'); // 4. 订阅所有请求(调试用,注意性能) __cyc_monitor__.subscribe(1, 'all-requests', ''); // 5. 取消单个订阅 __cyc_monitor__.unsubscribe(1, 'api-monitor'); // 6. 取消全部订阅 __cyc_monitor__.unsubscribeAll(1);

敏感参数保护:
以下参数名会自动替换为 ***:
token, access_token, refresh_token, key, api_key, secret, password, auth, sign, signature 等 20+ 个常见敏感参数名。


  1. 完整脚本示例

8.1 万能视频小窗播放器// ==UserScript== // @name 万能视频小窗播放器 // @match *://*/* // @grant none // ==/UserScript== (function() { 'use strict'; if (typeof __cyc_privileges__ === 'undefined') return; // 监听资源更新 window.__cyc_on_resource_update = function(type, urls) { if (type !== 'video' || urls.length === 0) return; showFloatButton(urls[0]); }; // 页面加载后检查已有视频 setTimeout(function() { const videoUrl = __cyc_privileges__.getCurrentVideo(); if (videoUrl) showFloatButton(videoUrl); }, 2000); function showFloatButton(videoUrl) { if (document.getElementById('cyc-float-btn')) return; const btn = document.createElement('div'); btn.id = 'cyc-float-btn'; btn.textContent = '▶️ 小窗播放'; btn.style.cssText = 'position:fixed;bottom:20px;right:20px;z-index:99999;background:#ff6600;color:#fff;padding:12px 18px;border-radius:25px;cursor:pointer;font-size:15px;font-family:sans-serif;box-shadow:0 4px 15px rgba(0,0,0,0.3);'; btn.onclick = function() { __cyc_privileges__.playInFloatWindow(videoUrl); __cyc_privileges__.toast('已开启悬浮窗播放'); }; document.body.appendChild(btn); } })();

8.2 网络请求实时监控面板// ==UserScript== // @name 网络请求实时监控 // @match *://*/* // @grant none // ==/UserScript== (function() { 'use strict'; if (typeof __cyc_monitor__ === 'undefined') return; // 开启响应体记录 if (typeof __cyc_privileges__ !== 'undefined') { __cyc_privileges__.setRecordBody(true); } const panel = document.createElement('div'); panel.style.cssText = 'position:fixed;bottom:10px;right:10px;z-index:99999;background:rgba(0,0,0,0.9);color:#0f0;font-size:11px;font-family:monospace;padding:10px;border-radius:6px;max-width:400px;max-height:40vh;overflow-y:auto;min-width:250px;'; panel.innerHTML = ' 网络监控<br>'; document.body.appendChild(panel); const maxLines = 20; const lines = []; window.__cyc_on_request = function(info) { const color = info.status >= 400 ? '#f44' : info.status >= 200 ? '#4f4' : '#ff4'; const text = `<span style="color:${color}">${info.method.toUpperCase()} ${info.status} ${info.url.substring(0, 60)} ${info.duration}ms</span>`; lines.push(text); if (lines.length > maxLines) lines.shift(); panel.innerHTML = ' 网络监控<br>' + lines.join('<br>'); }; // 订阅所有请求 __cyc_monitor__.subscribe(999, 'ui-monitor', ''); })();

8.3 批量下载页面图片// ==UserScript== // @name 批量下载页面图片 // @match *://*/* // @grant none // ==/UserScript== (function() { 'use strict'; if (typeof __cyc_privileges__ === 'undefined') { alert('请使用 Cat Browser 运行此脚本'); return; } const images = JSON.parse(__cyc_privileges__.getSniffedImages()); if (images.length === 0) { __cyc_privileges__.toast('未检测到图片'); return; } const urls = images.map(function(img) { return img.url; }); __cyc_privileges__.batchDownload(JSON.stringify(urls)); __cyc_privileges__.toast('开始下载 ' + urls.length + ' 张图片'); })();

8.4 B站视频下载(带格式转换)// ==UserScript== // @name B站全格式下载(自动转码) // @match *://*.bilibili.com/video/* // @match *://bilibili.com/video/* // @grant none // ==/UserScript== (function() { 'use strict'; if (typeof __cyc_privileges__ === 'undefined') return; let currentCookie = document.cookie; function getBvid() { let m = location.href.match(/\/video\/(BV\w+)/i); return m ? m[1] : null; } function downloadFile(url, filename, type) { let convertTo = null; if (filename.endsWith('.m4s')) { if (type === 'video') { filename = filename.replace(/\.m4s$/, '.mp4'); convertTo = 'mp4'; } else { filename = filename.replace(/\.m4s$/, '.mp3'); convertTo = 'mp3'; } } if (convertTo) { __cyc_privileges__.download(url, filename, JSON.stringify({ convertTo: convertTo })); } else { __cyc_privileges__.download(url, filename); } } async function fetchStreams() { const bvid = getBvid(); const viewResp = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`, headers: { 'Referer': 'https://www.bilibili.com/', 'Cookie': currentCookie }, onload: resolve, onerror: reject }); }); const viewData = JSON.parse(viewResp.responseText); const cid = viewData.data.cid; const title = viewData.data.title; const streamResp = await new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: 'GET', url: `https://api.bilibili.com/x/player/playurl?bvid=${bvid}&cid=${cid}&qn=127&fnval=4048&fourk=1`, headers: { 'Referer': 'https://www.bilibili.com/', 'Cookie': currentCookie }, onload: resolve, onerror: reject }); }); const data = JSON.parse(streamResp.responseText); const dash = data.data.dash; return { videos: dash.video.map(v => ({ url: v.baseUrl, desc: `${v.height}p ${(v.bandwidth/1000).toFixed(0)}kbps ${v.codecs.split('.')[0]}` })), audios: dash.audio.map(a => ({ url: a.baseUrl, desc: `${(a.bandwidth/1000).toFixed(0)}kbps ${a.codecs.split('.')[0]}` })), title }; } // 悬浮球 UI 和面板逻辑... // (完整实现见之前的脚本) })();


  1. 限制与注意事项

9.1 注入限制

限制项 说明
协议限制 仅在 http:// 和 https:// 页面可用,file:// 或 about:blank 不可用
注入时机 特权对象在页面开始加载后注入
每页一次 每个页面只注入一次,刷新页面后重新注入

9.2 监控限制

限制项 说明
单脚本订阅数 最多 10 个 URL 模式
全局订阅数 总计最多 100 个
响应体大小 最大 50KB,超过部分截断
响应体类型 仅文本类型(HTML/JSON/XML/JS 等),二进制类型不包含
敏感参数保护 URL 中的 token、key、password 等自动擦除为 ***

9.3 下载与转换限制

限制项 说明
转换引擎 使用 FFmpeg Kit,支持主流音视频/图片格式
转换策略 优先流复制(无损快速),失败后降级重新编码
转换时长 视频重新编码可能较慢(取决于文件大小和设备性能)
转换失败 原文件保留,任务进入已完成列表并显示提示
optionsJson 必须使用 JSON.stringify() 序列化,不能直接传 JS 对象

9.4 最佳实践

实践 说明
环境检测 始终用 typeof cyc_privileges !== 'undefined' 检测
降级处理 在普通浏览器中提供替代方案或提示用户使用 Cat Browser
@grant none Cat Browser 已默认开放所有 GM API,不需要额外声明
资源释放 脚本禁用/卸载时建议调用 unsubscribeAll() 清理订阅
性能考虑 不要在页面高频事件(如 scroll、mousemove)中频繁调用特权方法
响应体调试 调试完成后记得 setRecordBody(false) 关闭响应体记录
作用域隔离 始终将脚本代码包裹在 (function(){ 'use strict'; … })() 中
JSON 序列化 download() 的第三个参数必须使用 JSON.stringify()
格式兼容 转换格式建议使用通用格式(mp4、mp3、png),避免小众格式

@require 仅支持 HTTPS URL。


此文档涵盖 Cat Browser 全部脚本开发能力,脚本作者可直接复制示例代码,替换 URL 和参数即可开发出可用的脚本。