DSH 扫榜插件:多站小说榜单采集 + 本地变化分析 + 面板;零运行时依赖
35.7/100
综合分
1
GitHub 星标
2,128
npm 周下载
—/5
用户评分
数据截至 2026/10/27 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-rank-scannpm 源 · dsh-rank-scan 的发布者账号已校验与本仓库一致
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · other
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 9 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-rank-scan @ 0.6.3
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/2 06:37:47
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 93 处,展示前 78 条)
- 读取本地文件lib/index.js:174
return JSON.parse(readFileSync(path, 'utf8')); - 写入本地文件lib/index.js:181
mkdirSync(dirname(path), { recursive: true }); - 写入本地文件lib/index.js:183
writeFileSync(tmp, JSON.stringify(value, null, 2) + '\n', 'utf8'); - 写入本地文件lib/index.js:184
renameSync(tmp, path); - 写入本地文件lib/index.js:860
mkdirSync(dir, { recursive: true }); - 写入本地文件lib/index.js:861
appendFileSync(join(dir, 'timer.log'), '[' + new Date().toISOString() + '] ' + msg + '\n'); - 发起网络请求lib/index.js:3544
const r = await fetch('http://' + host + market.endpoints.updates + '?name=' + encodeURIComponent(own.name), { - 发起网络请求lib/index.js:3561
const r = await fetch('http://' + host + market.endpoints.updateOperation + '?operationId=' + encodeURIComponent(opId), { - 发起网络请求lib/index.js:3600
const r = await fetch('http://' + host + market.endpoints.restart, { - 发起网络请求lib/index.js:3626
const r = await fetch('http://' + host + market.endpoints.updates, { - 读取本地文件lib/index.js:3870
if (existsSync(dir)) - 删除本地文件递归lib/index.js:3871
rmSync(dir, { recursive: true, force: true }); - 读取本地文件lib/index.js:3872
return send(200, { ok: true, dropped: date, gone: !existsSync(dir) }); - 写入本地文件lib/index.js:4278
mkdirSync(dest, { recursive: true }); - 读取本地文件lib/index.js:4280
for (let i = 2; existsSync(file) && i < 1000; i++) - 读取本地文件lib/index.js:4295
if (!existsSync(dir)) - 执行外部命令来自配置/变量lib/index.js:4299
const child = spawn(cmd, [dir], { detached: true, stdio: 'ignore' }); - 发起网络请求lib/client.js:32
const jget = (p) => fetch(API + p, { headers: { Accept: 'application/json' } }).then(readResponse) - 发起网络请求lib/client.js:38
fetch(API + '/overview', { headers: { Accept: 'application/json' }, signal: controller.signal }).then(readResponse), - 发起网络请求lib/client.js:47
const jpost = (p, body) => fetch(API + p, { - 写入剪贴板lib/client.js:2111
h('button', { className: 'rs-btn', onClick: function () { try { navigator.clipboard.writeText(diag()); toast('success', '诊断信息已复制(不含凭证)') } catch (e) { - 写入剪贴板lib/client.js:2914
navigator.clipboard.writeText(text).then(function () { toast('success', '已复制链接') }).catch(fail) - 写入本地文件lib/scan/store.js:65
mkdirSync(dirname(path), { recursive: true }); - 写入本地文件lib/scan/store.js:67
writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8'); - 写入本地文件lib/scan/store.js:68
renameSync(tmp, path); - 读取本地文件lib/scan/store.js:72
return JSON.parse(readFileSync(path, 'utf8')); - 读取本地文件lib/scan/store.js:80
if (!existsSync(p)) - 读取本地文件lib/scan/store.js:96
return statSync(join(this.root, dataDate, boardId + '.json')).mtimeMs; - 删除本地文件lib/scan/store.js:125
rmSync(this.lockPath(), { force: true }); - 读取本地文件lib/scan/store.js:153
isUntrusted(dataDate, boardId) { return existsSync(this.untrustedPath(dataDate, boardId)); } - 删除本地文件lib/scan/store.js:162
rmSync(this.untrustedPath(dataDate, boardId), { force: true }); - 读取本地文件lib/scan/store.js:169
if (!existsSync(dir)) - 读取本地文件lib/scan/store.js:174
names = readdirSync(dir); - 读取本地文件lib/scan/fontmap.js:20
if (!existsSync(f)) - 读取本地文件lib/scan/fontmap.js:22
const j = JSON.parse(readFileSync(f, 'utf8')); - 写入本地文件lib/scan/fontmap.js:37
mkdirSync(root, { recursive: true }); - 写入本地文件lib/scan/fontmap.js:38
writeFileSync(tmp, JSON.stringify(map, null, 0) + '\n', 'utf8'); - 写入本地文件lib/scan/fontmap.js:39
renameSync(tmp, p); - 读取本地文件lib/scan/bookmark.js:78
const why = existsSync(this.file) ? '主文件损坏,已从备份恢复:' : '主文件缺失,已从备份恢复:'; - 读取本地文件lib/scan/bookmark.js:82
if (!existsSync(this.file)) - 读取本地文件lib/scan/bookmark.js:88
return JSON.parse(readFileSync(p, 'utf8')); - 写入本地文件lib/scan/bookmark.js:96
mkdirSync(dirname(this.file), { recursive: true }); - 读取本地文件lib/scan/bookmark.js:97
if (existsSync(this.file)) { - 读取本地文件lib/scan/bookmark.js:100
if (existsSync(src)) { - 写入本地文件lib/scan/bookmark.js:102
copyFileSync(src, this.file + '.' + i); - 写入本地文件lib/scan/bookmark.js:108
copyFileSync(this.file, this.file + '.1'); - 写入本地文件lib/scan/bookmark.js:113
writeFileSync(tmp, JSON.stringify({ ...f, bookmarksVersion: BOOKMARKS_VERSION, savedAt: new Date().toISOString() }, null, 2) + '\n', 'utf8'); - 写入本地文件lib/scan/bookmark.js:117
renameSync(tmp, this.file); - 发起网络请求lib/scan/bounded-fetch.js:45
const response = await fetch(url, { headers: opts.headers, signal: ctl.signal }); - 读取本地文件lib/scan/update.js:36
const j = JSON.parse(readFileSync(join(base, rel, 'package.json'), 'utf8')); - 读取本地文件lib/scan/files.js:19
if (!existsSync(root)) - 读取本地文件lib/scan/files.js:21
return readdirSync(root, { withFileTypes: true }) - 读取本地文件lib/scan/files.js:35
if (!existsSync(path)) - 读取本地文件lib/scan/files.js:38
for (const e of readdirSync(path, { withFileTypes: true })) { - 读取本地文件lib/scan/files.js:44
total += statSync(p).size; - 删除本地文件递归lib/scan/files.js:58
rmSync(p, { recursive: true, force: true }); - 写入本地文件lib/scan/files.js:70
mkdirSync(dest, { recursive: true }); - 读取本地文件lib/scan/files.js:71
for (const e of readdirSync(src, { withFileTypes: true })) { - 写入本地文件lib/scan/files.js:77
copyFileSync(s, d); - 写入本地文件lib/scan/files.js:114
mkdirSync(destDir, { recursive: true }); - 写入本地文件lib/scan/files.js:130
mkdirSync(join(destDir, 'reports'), { recursive: true }); - 写入本地文件lib/scan/files.js:131
copyFileSync(join(reports, f), join(destDir, 'reports', f)); - 写入本地文件lib/scan/files.js:139
mkdirSync(dir, { recursive: true }); - 删除本地文件lib/scan/files.js:227
rmSync(p, { force: true }); - 读取本地文件lib/scan/qidian-cache.js:7
if (!existsSync(p)) - 读取本地文件lib/scan/qidian-cache.js:10
const j = JSON.parse(readFileSync(p, 'utf8')); - 写入本地文件lib/scan/qidian-cache.js:55
mkdirSync(dir, { recursive: true }); - 写入本地文件lib/scan/qidian-cache.js:58
writeFileSync(tmp, JSON.stringify({ version: VERSION, books: cur.books }, null, 1) + '\n', 'utf8'); - 写入本地文件lib/scan/qidian-cache.js:59
renameSync(tmp, p); - 读取本地文件lib/scan/qidian-cache.js:71
if (!existsSync(p)) - 删除本地文件lib/scan/qidian-cache.js:73
rmSync(p, { force: true }); - 读取本地文件lib/scan/book-cache.js:10
if (!existsSync(p)) - 读取本地文件lib/scan/book-cache.js:13
const j = JSON.parse(readFileSync(p, 'utf8')); - 写入本地文件lib/scan/book-cache.js:69
mkdirSync(dir, { recursive: true }); - 写入本地文件lib/scan/book-cache.js:72
writeFileSync(tmp, JSON.stringify({ version: VERSION, entries: cur.entries }, null, 1) + '\n', 'utf8'); - 写入本地文件lib/scan/book-cache.js:73
renameSync(tmp, p); - 读取本地文件lib/scan/book-cache.js:85
if (!existsSync(p)) - 删除本地文件lib/scan/book-cache.js:87
rmSync(p, { force: true });
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
最近更新
在 GitHub 查看全部 →v0.6.32026-10-30
抓取时刻复核后的一次修正版:两个站的默认时刻调整、旧版本快照的判定变严、跑失败的站当天就能补上。升级只改「仍是旧默认值」的时刻,自己调过的不动。
· 修正
· 默认抓取时刻:菠萝包 09:30 → 14:30,起点图 12:40 → 21:30;起点 01:30、番茄 08:30、刺猬猫 10:40 不变。
- 菠萝包首页是一份「整代」对象:凌晨到上午常还是上一代(实测 02:15 仍旧代、13:48 已见新代;03:00–07:45 连点六次内容全同),09:30 抓会拿到旧一代,变化列出现假持平。
- 起点图首订榜抓的是前一天整批,抓早抓晚名单一样,但首订数值要到首V后 24 小时才收口:固定 12:40 抓平均四成的书还没收口,21:30 抓降到一成左右。
- 迁移只改仍是旧默认值的站;自己调过的时刻原样保留,之后也不会被再次改写。
· 旧版本复用判据:看过最近 7 次可信快照,只要与其中任一次逐条相同、且属于另一个周期(日更榜跨日 / 周榜跨周 / 期榜跨批)就判「内容不可信」,只出说明不出条目。原判据只比上一次,隔天复用回来的旧版会漏;同一周 / 同一期内不变仍算正常。
· 跑失败不必干等到兜底:某站当天第一次没跑成(站点不可达、电脑刚醒)时,冷却 30 分钟后补跑一次,不再一律压到 22:30;仍是每站每天最多补一次。
· 配置版本号只升不降:修掉「定时跑完把架构版本写回旧值 ⇒ 每次启动重跑一遍迁移」的隐患,并新增「每站最近一次尝试时刻」(成功与失败都记),冷却据此计算。
· 安装
dsh plugin --profile desktop add github:kilo-s/dsh-rank-scan#v0.6.3
或从本页 Assets 下载 dsh-rank-scan-0.6.3.tgz,完全退出 DSH 后执行 dsh plugin --profile desktop add 。详见 README「安装 / 更新」。
v0.6.22026-10-29
· 修正
· 「每榜条数」不再有假设置(0.6.0 只治了起点,这次一次扫干净):宿主 catalog() 的 canDepth 改成按适配器是否真读 depth 判,而不是按「站点理论上能给多少条」:
- 起点(移动端单页固定 20)、起点图(整页给出,适配器不读 depth)、刺猬猫「首订(约)」(第三方整批)、番茄派生榜(本地算、候选池全量)、菠萝包(首页 SSR 固定 10)⇒ canDepth: false + 各自的原因文案;
- 只有番茄分类榜、刺猬猫两个榜单保持可调(src/sites/*.ts 里真按 depth 翻页/截断)。
- config 里已有的旧值原样保留,只是不再允许修改。
· 条数不可调的榜,整块「本榜设置」直接不出现(使用者:「改不了的话是不是可以直接删了?显示在那里也没用啊」):原来是把输入框置灰,现在是控件整块移除。
· 操作条不写长句(使用者:「有必要用这么长的文字说明吗,要考虑 ui 观感」):那一行对所有榜统一回一句「只抓当前这一榜;全局扫描/停止在概览」,「条数为什么不能改」挂在该行的 hover 提示里;派生榜没有扫描按钮,只给「本地派生 · 不发请求」7 个字(完整说明在 hover)。
· 派生榜另外不再出现「扫描本榜」(它不发请求,点了只会拿到一条警告)。
· 保留可调的只有番茄分类榜与刺猬猫两个榜单(src/sites/*.ts 里真按 depth 翻页/截断),它们的「每榜取多少条 + 保存 + 保存并扫描本榜」原样保留。
· 安装
dsh plugin --profile desktop add github:kilo-s/dsh-rank-scan#v0.6.2
或从本页 Assets 下载 dsh-rank-scan-0.6.2.tgz,完全退出 DSH 后执行 dsh plugin --profile desktop add 。详见 README「安装 / 更新」。
v0.6.12026-10-29
· 修正
· 全站统一去掉「最近更新 / 更新时间」列(使用者定稿):撤回 0.6.0 给刺猬猫加的「最近更新」,番茄早先删掉的「更新时间」也不再回来,榜单行的展开详情里那条「更新 …」一并移除 —— 两站口径一致。字段本身(extra.update / extra.lastChapterUpdateTime)仍在快照里,只是不占列;顺带清掉 updated 的列优先级/列宽死配置。
· 番茄「在读周榜」不再空表:派生榜原来拿全局最新数据日建池,碰上「今天的目录只有起点、番茄还没抓」就会 0 行,而说明文字照常输出「已结算 N/7 天」,看起来像面板卡住。现在改用最新一个番茄真有快照的日期算池子,并在说明里写清回落到了哪天(例:番茄最新快照是 2026-10-28(2026-10-29 还没有番茄快照),本榜按 2026-10-28 算);「已结算 N 天 / 有数据的日期」也只统计番茄真有快照的日期,不再把没抓的日子算进去。
· 安装
dsh plugin --profile desktop add github:kilo-s/dsh-rank-scan#v0.6.1
或从本页 Assets 下载 dsh-rank-scan-0.6.1.tgz,完全退出 DSH 后执行 dsh plugin --profile desktop add 。详见 README「安装 / 更新」。
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。
README
由 DeepSeek 最新模型翻译生成dsh-rank-scan
DeepSeek Harness 扫榜插件:定时 + 按需采集多站小说榜单,算变化,在一个面板里挑着看。
只读站点公开榜单与书页 —— 不登录、不抓正文、不推送;全部数据留在本地数据目录。
本插件不注册任何 DSH 工具。对外能力只有两样:面板 UI(左侧栏「扫榜」入口 + 浮窗)与同源 HTTP 路由 /api/rank-scan/。
装配入口是 cordis.patch.yml(由 package.json 的 dsh.bundle.patch 指向)。
works on DSH 0.2.0.x(桌面版 0.2.0 线;package.json 的 engines.dsh 写的是 >=0.1.0 <1,但面板与路由只在这条线上验过)。
安装 / 更新
任选一条。三种方式都要求先完全退出 DSH 桌面版(配置档由桌面应用独占管理),装完重开 DSH 即可。
A. 一条命令(推荐)
dsh plugin --profile desktop add github:kilo-s/dsh-rank-scan
钉版本:github:kilo-s/dsh-rank-scan#v0.3.0
仓库不含编译产物,这条命令会在安装时现场构建 lib/。
展开完整文档(README 剩余 1 节)
dsh-rank-scan
DeepSeek Harness 扫榜插件:定时 + 按需采集多站小说榜单,算变化,在一个面板里挑着看。
只读站点公开榜单与书页 —— 不登录、不抓正文、不推送;全部数据留在本地数据目录。
本插件不注册任何 DSH 工具。对外能力只有两样:面板 UI(左侧栏「扫榜」入口 + 浮窗)与同源 HTTP 路由 /api/rank-scan/。
装配入口是 cordis.patch.yml(由 package.json 的 dsh.bundle.patch 指向)。
works on DSH 0.2.0.x(桌面版 0.2.0 线;package.json 的 engines.dsh 写的是 >=0.1.0 <1,但面板与路由只在这条线上验过)。
安装 / 更新
任选一条。三种方式都要求先完全退出 DSH 桌面版(配置档由桌面应用独占管理),装完重开 DSH 即可。
A. 一条命令(推荐)
dsh plugin --profile desktop add github:kilo-s/dsh-rank-scan
钉版本:github:kilo-s/dsh-rank-scan#v0.3.0
仓库不含编译产物,这条命令会在安装时现场构建 lib/。首次可能被 pnpm 拦下构建脚本(它会打印要放行的键,加进 allowBuilds 后重跑即可),细节见 docs/DEVELOPMENT.md。
B. 离线:装 Release 附件
从 Releases 下载 dsh-rank-scan-.tgz,完全退出 DSH 桌面版后:
dsh plugin --profile desktop add "<tgz 的完整路径>"
需要脚本化安装 / 卸载(install.ps1、uninstall.ps1)或想知道包里为什么不带 scripts/,见 docs/DEVELOPMENT.md。
C. 从 npm registry 装(免现场构建)
dsh plugin --profile desktop add dsh-rank-scan
钉版本:dsh-rank-scan@0.3.0
npm 上那份是 CI 从源码构建的(包内自带 lib/,发布带 --provenance 签名)——装的时候不用再编译,也不会被 pnpm 的 allowBuilds 拦下。面板的「检查更新」读的就是 npm 上的版本号。
验证:重开 DSH 后左侧栏会话列表下方应出现「扫榜」入口;或访问 http://127.0.0.1:19387/api/rank-scan/health,返回 {ok:true,...} 即通。
更新:重跑安装命令即可(链接直装取最新 main,registry 装取最新发布版;本版本为 0.3.0);面板「设置」页底部会读 npm 上的版本号提示有没有新版,装了 dsh-market 的话还能就地更新。
卸载:dsh plugin --profile desktop remove dsh-rank-scan。数据目录 ~/.dsh/data/rank-scan 会保留,要彻底清理自行删除;脚本化卸载与 patch 安全口径见 docs/DEVELOPMENT.md。
能做什么
| 能力 | 说明 |
| 五站采集 | 起点签约榜 × 15 分类(默认轻小说;其余分类勾了才抓,移动端口径)· 起点图首订榜 / 三江·推荐效果(周)· 刺猬猫点击榜(周) / 新书榜(周) / 首订(约) · 番茄男频新书榜 × 8 分类 · 菠萝包新书榜(点击) / 轻小说热榜(周) / VIP 热销榜(周) |
| 派生榜 | 番茄「在读周榜(候选池内)」由已落盘快照本地计算,不发请求;不足 7 天标 N/7 |
| 变化集 | 新入榜 / 回榜 / 上升 / 下降 / 退榜分开列;在榜天数;基准日;无基准时一律「—」,不报 0 |
| 周期榜换期 | 起点图三江当期无数据自动回退上一期(最多 2 期);换期当天变化列判不可比并标注「新期」 |
| 批次榜 | 刺猬猫「首订(约)」每周一期、起点图「首订榜」每天一批:按批展示(第 N 期 / 上架日 …),不出变化列与涨/新/退计数,变化页明确列为「不参与变化」 |
| 状态五态 | ok / missing / stale / login_required / truncated;缺失一律 null,绝不补 0 |
| 按需补全 | 起点/起点图三江精确字数(+起点收藏)、番茄干净书名/作者、刺猬猫与菠萝包字数与收藏;结果就地覆盖当天快照,可重复补(幂等) |
| 补全后台化 | 抓榜一结束就返回,补全在后台逐榜跑,面板顶部显示「补全中 N/M」;通用书页缓存(起点/刺猬猫/菠萝包/番茄共用,默认 7 天)命中即不再进书页 |
| 请求间隔 | 真实请求之间的最小间隔可配:面板「设置」页可选 200ms / 500ms / 1000ms / 2000ms / 5000ms 或「不限速(仅本地测试)」,默认 1000ms 不变,改完立即生效 |
| 落盘纪律 | 按数据日期落快照,原子写、同日覆盖即幂等;精简索引 / 保留清理 / 导出 |
| 定时扫描 | 按站分时,默认开启,安装当天不跑;面板可改时刻与开关,改动跨重载保留 |
| 面板记忆 | 浮窗位置与尺寸、收起状态、当前页签、正在看哪天,都在刷新后恢复 |
| 版本与更新 | 面板显示当前版本;已发布到 npm,装了 dsh-market 可查新版本并「一键更新」;npm 查不到时面板会明确提示,回退 Release 附件 / 仓库链接;没装 dsh-market 则只读 |
| 防卡死 | 单请求 15s / 单榜 90s / 单站连败 3 次熔断 / 120s 无进展看门狗 / 扫描锁 + 心跳(心跳过期可接管) |
| 书签 | 面板内收藏作品、备注夹、回收站、导出 |
用法
面板(四个页签)
| 页签 | 做什么 |
| 概览 | 采集摘要、需要处理的问题与今天的变化;全部榜单可展开,按站查看 |
| 榜单 | 选择站、榜、日期(批次榜为「批」)和排序后查看作品;采集条数与补全放在更多操作中;只有首订榜提供按日回看 |
| 书签 | 紧凑卡片与搜索筛选;备注及书签夹显式保存;批量选择、导出、添加与回收站按需展开 |
| 设置 | 抓取范围、扫描计划、数据与性能、关于与诊断;表单修改需保存,即时开关明确反馈结果 |
浮窗标题栏可拖动、右下角把手可缩放,位置与尺寸本地记忆。面板改完客户端记得刷新页面(客户端 bundle 由页面加载)。
定时扫描
- 默认开启,但安装当天不自动跑(次日按时刻跑);面板「设置」页可关掉或改时刻,关掉后重载仍保留。
- 按站分时:每个站可以有自己的抓取时刻,兜底时刻 22:30。新安装的菠萝包默认时刻为 09:30;已有安装保留已保存的时刻,升级不覆盖。
- 同一时刻只有一轮扫描(定时与手动共用一把锁,冲突时手动请求返回 409)。
- 诊断日志:<dataDir>/state/timer.log(接线 / 迁移 / 每轮开始结束各一行)。
HTTP 路由(同源、只读本地快照,渲染不联网)
| 路由 | 说明 |
| GET /api/rank-scan/health | 自检:路由是否挂上(端口 / 数据目录 / 数据日期列表) |
| GET /api/rank-scan/overview | 概览:数据日期 / 基准日 / 五态计数 / 隐藏榜数 / 每站每榜状态与涨新退 |
| GET /api/rank-scan/board?boardId=&date=&sort= | 当前榜单:列随榜、缺失显示「—」、当天无快照时回落最近可用并标注日期 |
| GET /api/rank-scan/changes | 今天的变化:按站编组,组内固定 新入榜/回榜/上升/下降/退榜 |
| GET /api/rank-scan/catalog | 可选榜清单:站 → 榜种 → 分类 + 是否已勾选 + 各榜深度 + 定时状态 |
| POST /api/rank-scan/scan cancel retry | 立刻扫一次(可带 {boards} 只扫指定榜)/ 取消 / 只重跑当天非 ok 的榜 |
| POST /api/rank-scan/select depth timer | 抓取集与显示集 / 每榜取数(1–100)/ 定时开关与时刻 |
| POST /api/rank-scan/enrich?boardId= enrich-all | 按需补全某榜或抓取集内全部分类榜的字段 |
| GET/POST /api/rank-scan/bookmarks bookmark bookmark/sync bookmark/export bookmark/trash | 书签列表 / 单条增删改 / 同步 / 导出 / 回收站 |
| GET /api/rank-scan/version update-check,POST /api/rank-scan/update update-restart | 版本读数与一键更新(只走 dsh-market 公开的更新 API v1;宿主不支持重启时明确拒绝并提示自行重启) |
来源闸(宿主对 /api/rank-scan/* 不做鉴权,插件自己加了一道):请求带了 Origin 或 Referer、且其 host 与本次请求的 Host 不同源 ⇒ 403;Origin: null(沙箱 iframe / file://)也拒;两个头都不带 ⇒ 放行。最后这条是有意为之:curl、PowerShell 脚本、你自己机器上的进程本来就能直接操作系统,这道闸门防的是「浏览器里的第三方页面」,不是本地可控进程。
数据与隐私
- 只读站点公开榜单与书页;不登录、不抓正文、不推送任何地方。
- 采集纪律:限速默认 1s/请求(面板「设置」页可下调到 200ms,或显式设 0 = 关闭限速、仅建议本地测试)、每榜每日 1 次、失败重试 ≤2 次。
- 时间口径:落盘时间戳是绝对时刻、面板显示成本地时刻,但非 +08:00 时区未测(测试与 CI 只跑 Asia/Shanghai)。
- 数据目录默认 ~/.dsh/data/rank-scan/(插件配置 dataDir 可改):
<dataDir>/<数据日期>/<boardId>.json 快照(rows + metric) <dataDir>/<数据日期>/scan.json 本次扫描元信息(attempt / outcome / progress / 每榜状态) <dataDir>/state/scan.lock.json 扫描锁(含心跳时间) <dataDir>/state/retries.json 各榜当日重试次数 <dataDir>/state/cancel.json 取消标志 <dataDir>/state/timer.log 定时诊断 <dataDir>/state/qidian-book-cache.json 起点书页缓存(TTL 内不重复下载) <dataDir>/font-map.json 番茄字体映射(私用区码点 → 真字)
数据只在本地,插件不写工作区内任何文件。
排障
左侧栏没有「扫榜」入口(按顺序看):
1. 先确认装到了你正在跑的那个 profile:安装脚本结束时打印「本次装到 profile 「x」」,跟你实际启动的 profile 对不上就会静默不出现(不报错);可核对 ~/.dsh/profiles/<你跑的 profile>/node_modules/dsh-rank-scan 是否存在;
2. DSH 启动日志里搜 did not activate 与 pending (waiting for services:前者 = 面板 bundle 没加载成功,后者 = 缺 timer / webServer 服务;
3. 访问 /api/rank-scan/health:能返回 JSON ⇒ host 侧活着,问题在面板;连不上 ⇒ 插件没装上;
4. 再装一次:pwsh -File scripts\install.ps1 -Spec "<tgz>"(省略 -Spec 就走目录直装,要求仓库目录已经构建出 lib/index.js,否则脚本会直接报错退出)。
面板点不开 / 点了没反应:先确认你点的是哪颗按钮 —— DSH 的侧栏会把插件入口包进它自己的一行 ,插件入口嵌在里面(所以 DOM 里有两颗文本相同的 button,外层那颗不归插件处理)。
某个榜显示 missing / 一列都是「—」:先看是不是数据源本身就没有这个字段(例如站点榜单页不给字数与收藏,需要点「补全」进书页取);本插件对缺失一律显示「—」,不会编造 0。
DSH 起不来 / 日志提示 patch 解析失败怎么办(must be a top-level YAML array of loader entries、failed to parse overlay):
profile 的 cordis.patch.yml 被改坏了(手动编辑、或旧版卸载脚本删条目留下的孤儿行)。宿主自己修不了 —— 它写 patch 之前先要求「顶层是 YAML 数组」,文件已经不合法时就卡死,别指望在 GUI 里点回去。用救援脚本(只碰这一个文件,先备份):
pwsh -File scripts\repair-patch.ps1 -DryRun # 先看判定,不写盘 pwsh -File scripts\repair-patch.ps1 # 只有不可解析才动手:备份 → 回写成 [](空 overlay) pwsh -File scripts\repair-patch.ps1 -Restore # 想退回:用最近的备份还原
macOS / Linux:bash scripts/repair-patch.sh desktop -DryRun。脚本只认 ~/.dsh/profiles/<profile>/cordis.patch.yml 这一个文件,用 -Profile <名字> 换 profile。
那个文件每一行是什么:
- id: dsh-rank-scan # 一条 loader 条目:顶层数组的一项,「- 」必须从第 1 列开始 disabled: true # 宿主「禁用插件」时写的字段;留着合法,删掉也行
- insert: # 另一种合法形态:把下面的条目插进某个父节点
- id: dsh-rank-scan
disabled: true
只有注释 / 空行,或者出现不以上述 - 开头、却顶在第 1 列的散行(例如孤零零一行 disabled: true)—— 这两种都会让宿主启动失败;[] 是最小合法文件(= 没有 overlay)。修完这样验证:dsh --profile <profile> --dump-config 退出码 0,再重开 DSH。
为什么卸载脚本不动它?自动改写 YAML 恰恰是把 patch 改坏的主要来源(删一行就留孤儿)。所以卸载脚本只「读 + 打印」,宁可留一条 disabled 残留,也不冒把文件改坏的风险 —— 本插件不承诺「patch 零残留」。
站点改版:先跑夹具测试(node tests/qidian-fixture.test.mjs 等),再看 tests/fixtures/ 里的响应快照哪里变了。
开发
构建、测试、设计约束、目录结构、面板与宿主路由结构、验收闸门与依赖纪律,见 docs/DEVELOPMENT.md;版本变更历史见 CHANGELOG.md。
许可证
BSD-3-Clause,见 LICENSE。预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
kilo-s/dsh-rank-scan
DSH 扫榜插件:多站小说榜单采集 + 本地变化分析 + 面板;零运行时依赖
相关插件 · 其他
模型破甲清洗YuJunZhiXue/dsh-purge
⚔️ DeepSeek Harness 网络安全红队工具dsh-purge
★ 16,148已汉化DeepSeek 客户端
73
女娲蒸馏alchaincyf/nuwa-skill
输入名人名字,提炼其思维模型与决策方式供你调用
★ 37,308已汉化DSH
72
逆向分析桥接Fanqi89/dsh-rea
REA(Reverse Engineer Anything)MCP 桥接,用来把 REA 逆向工具接入 DSH 插件
★ 51已汉化DSH
72
语音女友beiyege-01/dsh-voice-ai-girlfriend
语音 AI 女友Voice AI girlfriend for DeepSeek Harness:Whisper…
★ 888已汉化DSH
71.6
红队越狱测试套件Minglink/dsh-infinite-gen-5
DeepSeek v4.1 flash 逆向工具无限五代 求 Star 收藏 ⭐欢迎大家提交项目的改进
★ 3,759已汉化DSH
71.4
协同办公空间dream-num/univer-workspace
一个让人类与 AI Agent 共同创作、协作和审阅的开源 Office 工作空间。
★ 2,266已汉化DSH
70.7
这是你的仓库?认领条目