🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

B站日报 c1overzz/dsh-bili-scroll

DeepSeek Harnessv2.3.0
c1overzz2 天前有提交已提供中文文档扫描:含敏感能力

DSH 插件:每天替你刷一轮 B站,读字幕挑出值得的、写一份日报,值得的 @ 你

69.9/100
综合分
1,104
GitHub 星标
36,179
npm 周下载
—/5
用户评分

数据截至 2026/11/1 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-bili-scroll
npm 源 · dsh-bili-scroll 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · tool
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 2 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-bili-scroll @ 2.3.0
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/31 14:12:06

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 238 处,展示前 109 条)
  • 读取本地文件
    index.js:148
    try { myVer = String(JSON.parse(readFileSync(join(pluginDir, 'package.json'), 'utf8')).version || '') } catch { myVer = '' }
  • 读取本地文件
    index.js:170
    if (existsSync(win)) return win
  • 读取本地文件
    index.js:173
    for (const d of readdirSync(lib)) {
  • 读取本地文件
    index.js:176
    if (existsSync(p)) return p
  • 读取本地文件
    index.js:182
    try { return readdirSync(sitePkgs(venvRoot)).some((n) => n.startsWith(prefix)) } catch { return false }
  • 读取本地文件
    index.js:189
    const venvReadyNow = () => existsSync(venvPython()) && missingVenvDeps().length === 0
  • 写入本地文件
    index.js:216
    writeFileSync(overrideFile, JSON.stringify(cur, null, 2), 'utf8')
  • 写入本地文件
    index.js:248
    mkdirSync(outDir, { recursive: true })
  • 写入本地文件
    index.js:252
    try { appendFileSync(logFile, line + '\n', 'utf8') } catch { }
  • 写入本地文件
    index.js:516
    writeFileSync(marker, String(Date.now()), 'utf8')
  • 请求外部主机api.github.com
    index.js:959
    const rr = await fetch('https://api.github.com/repos/c1overzz/dsh-bili-scroll/releases/latest', {
  • 发起网络请求
    index.js:959
    const rr = await fetch('https://api.github.com/repos/c1overzz/dsh-bili-scroll/releases/latest', {
  • 写入本地文件
    index.js:1377
    mkdirSync(dirname(dutyProfileDir), { recursive: true })
  • 写入本地文件
    index.js:1550
    try { writeFileSync(file, yml.join('\n'), 'utf8'); return file } catch (e) { log(`写 overlay 失败:${e?.message ?? e}`); return null }
  • 执行外部命令来自配置/变量
    index.js:1832
    const child = spawn(dsh.command, args, {
  • 执行外部命令来自配置/变量
    index.js:2075
    const child = spawn(dsh.command, args, {
  • 执行外部命令固定命令
    index.js:2253
    const c = spawn('uv', ['--version'], { windowsHide: true, stdio: 'ignore' })
  • 执行外部命令来自配置/变量
    index.js:2267
    const c = spawn(cmd, ['--version'], { windowsHide: true, stdio: 'ignore' })
  • 执行外部命令来自配置/变量
    index.js:2276
    try { c = spawn(cmd, args, { cwd: stateDir, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'], env: uvEnv() }) }
  • 执行外部命令来自配置/变量
    index.js:2330
    const child = spawn(py, [join(stateDir, 'favops.py'), ...args], {
  • 请求外部主机api.bilibili.com
    index.js:2509
    const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + bv, {
  • 发起网络请求
    index.js:2509
    const r = await fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + bv, {
  • 发起网络请求
    index.js:2517
    const img = await fetch(pic, {
  • 删除本地文件
    index.js:2532
    try { rmSync(bak, { force: true }) } catch { }
  • 请求外部主机registry.npmjs.org
    index.js:3129
    const r = await fetch('https://registry.npmjs.org/dsh-bili-scroll/latest', { signal: AbortSignal.timeout(8000) })
  • 发起网络请求
    index.js:3129
    const r = await fetch('https://registry.npmjs.org/dsh-bili-scroll/latest', { signal: AbortSignal.timeout(8000) })
  • 请求外部主机api.github.com
    index.js:3140
    const r = await fetch('https://api.github.com/repos/c1overzz/dsh-bili-scroll/contents/package.json', {
  • 发起网络请求
    index.js:3140
    const r = await fetch('https://api.github.com/repos/c1overzz/dsh-bili-scroll/contents/package.json', {
  • 请求外部主机api.bilibili.com
    index.js:3226
    const r = await fetch('https://api.bilibili.com/x/web-interface/nav', {
  • 发起网络请求
    index.js:3226
    const r = await fetch('https://api.bilibili.com/x/web-interface/nav', {
  • 删除本地文件
    index.js:3243
    try { const t = join(stateDir, '.diag-write-test'); writeFileSync(t, 'x'); rmSync(t) } catch { writable = false }
  • 请求外部主机api.bilibili.com
    index.js:3249
    const r = await fetch('https://api.bilibili.com/x/web-interface/nav', { headers: { 'User-Agent': 'Mozilla/5.0' } })
  • 发起网络请求
    client.js:42
    const jget = async (p) => asJson(await fetch(API + p, { credentials: 'same-origin' }))
  • 发起网络请求
    client.js:43
    const jpost = async (p, body) => asJson(await fetch(API + p, {
  • 写入剪贴板
    client.js:439
    await navigator.clipboard.writeText(v)
  • 读取本地文件
    lib/hostdeps.mjs:40
    for (const n of readdirSync(join(npm, '@deepseek-ai'))) {
  • 读取本地文件
    lib/hostdeps.mjs:53
    for (const p of readdirSync(join(dshHome, 'profiles'))) {
  • 读取本地文件
    lib/hostdeps.mjs:68
    if (!existsSync(pkgFile)) return null
  • 读取本地文件
    lib/hostdeps.mjs:69
    const pkg = JSON.parse(readFileSync(pkgFile, 'utf8'))
  • 读取本地文件
    lib/hostdeps.mjs:76
    return existsSync(file) ? pathToFileURL(file).href : null
  • 读取本地文件
    lib/mcp.mjs:16
    const PKG = (() => { try { return JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) } catch { return {} } })()
  • 执行外部命令来自配置/变量
    lib/mcp.mjs:36
    this.proc = spawn(command, args, {
  • 读取本地文件
    lib/verify.mjs:44
    if (!existsSync(f)) return null
  • 读取本地文件
    lib/verify.mjs:46
    const c = JSON.parse(readFileSync(f, 'utf8'))
  • 发起网络请求
    lib/verify.mjs:62
    const r = await fetch(url, { headers })
  • 读取本地文件
    lib/verify.mjs:120
    if (!existsSync(seenFile)) return { checked: [], skipped: 0, text: '⚠️ 判重表还没生成,没什么可复核的' }
  • 读取本地文件
    lib/verify.mjs:124
    const rows = parseSeen(readFileSync(seenFile, 'utf8'))
  • 读取本地文件
    lib/verify.mjs:130
    try { state = JSON.parse(readFileSync(stateFile, 'utf8')) } catch { }
  • 写入本地文件
    lib/verify.mjs:152
    try { writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf8') } catch { }
  • 读取本地文件
    lib/verify.mjs:168
    if (!existsSync(f)) return null
  • 写入本地文件
    lib/verify.mjs:174
    writeFileSync(f, cur.replace(/ *$/, '') + '\n' + text + '\n', 'utf8')
  • 写入本地文件
    lib/state.mjs:48
    mkdirSync(dir, { recursive: true })
  • 读取本地文件
    lib/state.mjs:53
    try { return JSON.parse(readFileSync(file, 'utf8')) } catch { return fallback }
  • 写入本地文件
    lib/state.mjs:57
    writeFileSync(file, JSON.stringify(value, null, 2), 'utf8')
  • 读取本地文件
    lib/state.mjs:81
    if (!existsSync(from)) { log(`缺文件:${from}`); continue }
  • 读取本地文件
    lib/state.mjs:82
    const incoming = readFileSync(from, 'utf8')
  • 读取本地文件
    lib/state.mjs:84
    const shipped = existsSync(markFile) ? readFileSync(markFile, 'utf8').trim() : ''
  • 读取本地文件
    lib/state.mjs:84
    const shipped = existsSync(markFile) ? readFileSync(markFile, 'utf8').trim() : ''
  • 写入本地文件
    lib/state.mjs:85
    const writeMark = (txt) => { try { writeFileSync(markFile, sha(Buffer.from(txt, 'utf8')), 'utf8') } catch { } }
  • 读取本地文件
    lib/state.mjs:86
    if (!existsSync(to)) {
  • 写入本地文件
    lib/state.mjs:87
    copyFileSync(from, to); writeMark(incoming); log(`已铺好:${to}`); continue
  • 写入本地文件
    lib/state.mjs:98
    try { copyFileSync(to, bak) } catch { }
  • 写入本地文件
    lib/state.mjs:99
    copyFileSync(from, to)
  • 发起网络请求
    lib/login.mjs:46
    const r = await fetch(SPI, { headers: { 'User-Agent': UA } })
  • 发起网络请求
    lib/login.mjs:55
    const r = await fetch(GEN, { headers: { 'User-Agent': UA } })
  • 发起网络请求
    lib/login.mjs:64
    const res = await fetch(pollUrl(key), { headers: { 'User-Agent': UA }, redirect: 'manual' })
  • 执行外部命令来自配置/变量
    lib/login.mjs:91
    const out = execFileSync(whoami, ['/user', '/fo', 'csv', '/nh'], { encoding: 'utf8', windowsHide: true })
  • 执行外部命令固定命令
    lib/login.mjs:103
    execFileSync('icacls', [file, '/inheritance:r', '/grant:r', who + ':F'], { stdio: 'ignore', windowsHide: true })
  • 读取本地文件
    lib/login.mjs:104
    try { readFileSync(file) } catch {
  • 执行外部命令固定命令
    lib/login.mjs:105
    execFileSync('icacls', [file, '/reset'], { stdio: 'ignore', windowsHide: true })
  • 执行外部命令固定命令
    lib/login.mjs:109
    execFileSync('chmod', ['600', file], { stdio: 'ignore' })
  • 写入本地文件
    lib/login.mjs:116
    mkdirSync(stateDir, { recursive: true })
  • 写入本地文件
    lib/login.mjs:118
    writeFileSync(file, JSON.stringify({
  • 执行外部命令来自配置/变量
    lib/login.mjs:135
    child = spawn(pythonPath || 'python', ['-c', code, url, png], { cwd: stateDir, windowsHide: true })
  • 读取本地文件
    lib/login.mjs:137
    const done = () => { try { resolve(existsSync(png) ? png : null) } catch { resolve(null) } }
  • 读取本地文件
    lib/uid.mjs:13
    if (!existsSync(f)) return null
  • 读取本地文件
    lib/uid.mjs:15
    const c = JSON.parse(readFileSync(f, 'utf8'))
  • 读取本地文件
    lib/uid.mjs:21
    try { return JSON.parse(readFileSync(join(stateDir, 'uid-cache.json'), 'utf8')) || {} } catch { return {} }
  • 写入本地文件
    lib/uid.mjs:24
    try { writeFileSync(join(stateDir, 'uid-cache.json'), JSON.stringify(obj, null, 2), 'utf8') } catch { }
  • 发起网络请求
    lib/uid.mjs:38
    const r = await fetch(url, {
  • 读取本地文件
    lib/dm.mjs:21
    if (!existsSync(f)) return null
  • 读取本地文件
    lib/dm.mjs:23
    const c = JSON.parse(readFileSync(f, 'utf8'))
  • 发起网络请求
    lib/dm.mjs:38
    const r = await fetch(url, { headers: headers(cred) })
  • 读取本地文件
    lib/uvfetch.mjs:33
    return existsSync('/lib/ld-musl-aarch64.so.1') || existsSync('/lib/ld-musl-x86_64.so.1') || existsSync('/lib/ld-musl-i386.so.1')
  • 读取本地文件
    lib/uvfetch.mjs:75
    return createHash('sha256').update(readFileSync(file)).digest('hex')
  • 发起网络请求
    lib/uvfetch.mjs:87
    const r = await fetch(source.url + '.sha256')
  • 读取本地文件
    lib/uvfetch.mjs:97
    try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return null }
  • 执行外部命令来自配置/变量
    lib/uvfetch.mjs:111
    const r = spawnSync(cmd, args, { stdio: 'ignore' })
  • 读取本地文件
    lib/uvfetch.mjs:126
    if (existsSync(target)) return target
  • 写入本地文件
    lib/uvfetch.mjs:128
    mkdirSync(dir, { recursive: true })
  • 删除本地文件递归
    lib/uvfetch.mjs:130
    rmSync(tmp, { recursive: true, force: true })
  • 写入本地文件
    lib/uvfetch.mjs:131
    mkdirSync(tmp, { recursive: true })
  • 请求外部主机pypi.tuna.tsinghua.edu.cn
    lib/uvfetch.mjs:145
    const r = await fetch('https://pypi.tuna.tsinghua.edu.cn/pypi/uv/json')
  • 发起网络请求
    lib/uvfetch.mjs:145
    const r = await fetch('https://pypi.tuna.tsinghua.edu.cn/pypi/uv/json')
  • 删除本地文件递归
    lib/uvfetch.mjs:169
    rmSync(tmp, { recursive: true, force: true })
  • 写入本地文件
    lib/uvfetch.mjs:170
    mkdirSync(tmp, { recursive: true })
  • 发起网络请求
    lib/uvfetch.mjs:172
    const res = await fetch(s.url)
  • 写入本地文件
    lib/uvfetch.mjs:174
    await pipeline(Readable.fromWeb(res.body), createWriteStream(s.file))
  • 读取本地文件
    lib/uvfetch.mjs:175
    const mb = (statSync(s.file).size / 1048576).toFixed(1)
  • 写入本地文件
    lib/uvfetch.mjs:192
    copyFileSync(hit, target)
  • 写入本地文件
    lib/uvfetch.mjs:193
    try { chmodSync(target, 0o755) } catch { }
  • 执行外部命令来自配置/变量
    lib/uvfetch.mjs:197
    const r = spawnSync(target, ['--version'], { stdio: 'ignore' })
  • 删除本地文件
    lib/uvfetch.mjs:203
    try { rmSync(target, { force: true }) } catch { }
  • 删除本地文件递归
    lib/uvfetch.mjs:206
    rmSync(tmp, { recursive: true, force: true })
  • 删除本地文件递归
    lib/uvfetch.mjs:214
    rmSync(tmp, { recursive: true, force: true })
  • 删除本地文件递归
    selfcheck.mjs:45
    try { rmSync(SANDBOX_HOME, { recursive: true, force: true }) } catch { }
  • 写入本地文件
    selfcheck.mjs:46
    mkdirSync(SANDBOX_HOME, { recursive: true })
  • 读取本地文件
    selfcheck.mjs:58
    const pkg = JSON.parse(readFileSync('./package.json', 'utf8'))
  • 删除本地文件递归
    selfcheck.mjs:125
    try { rmSync(SANDBOX_HOME, { recursive: true, force: true }) } catch { }
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包
涉及外部主机:api.github.com、api.bilibili.com、registry.npmjs.org、pypi.tuna.tsinghua.edu.cn

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh-tools
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
v2.4.02026-10-31
这一版修了两个"悄悄坏掉"的问题,还带了一个等了好久的功能。 · 修好的 · 值班轮白跑一整轮(#144 / #163)—— 额度吃掉了、out/ 里只有一份失败存根。根因是插件把你当前会话选的模型原样写进了那一轮,而值班轮跑在一个只有 DeepSeek 两条路由的最小权限环境里;你选的如果是桌面版自带的 deepseek-vision、某个社区 provider、或自定义网关,那一轮在发第一个请求之前就死了。现在会先判断"这个模型在那一轮里到底能不能用",不能用就退回,并写清楚为什么换了。 · 自动收藏连着四天 0/4 —— 日志里只有一行"失败 4",界面上完全看不出来。两个原因叠在一起:收藏夹参数传错了位置,加上那版 B站 库把旧接口删掉了。 · 「@ 昵称」中间带空格,等于没 @ 到人(#150,谢谢 @No3799)—— B站 只认紧挨着的写法。带空格的评论照样发出去、接口照样返回成功,但对方收不到任何通知,而且全程不报错。 · 私信发出去了,「发给你了」里却看不到(#162,谢谢 @yemuxi) · 新功能 · 可以给"刷B站"单独指定模型(#111,谢谢 @pentyum)—— 设置页「日常 → 用哪个模型跑」,默认跟随当前会话,也可以钉死一个。列表只列出那一轮环境里真的装了的 provider。 · 一轮最多跑多久 —— 默认 20 分钟封顶(可改)。超时先 SIGTERM,5 秒后还在就 SIGKILL,日志写明原因。 · 「刷过的视频」只在工作区里出现,改了工作区设置、切回来会跟着挪。 · 深色模式下设置页的下拉菜单是白底白字(看不见),已修。 · 升级 dsh plugin --profile web add dsh-bili-scroll 然后重启一次 dsh。
v2.3.12026-10-28
四个小问题,但每一个都能让人以为插件坏了。 · ① 开关点下去要等一会才有动静 开关的状态原来要等服务器回话才变:点一下 → 宿主写进 DSH 原生设置(几百毫秒)→ 返回 → 界面重绘。 现在点下去立刻变,后台再同步;失败会自动弹回并说明原因。 · ② 「已保存」那行看不见 它原来贴在「保存」按钮右边,往下滚到某个开关那儿就看不见了。 现在挪到页面最顶部并吸顶,滚到哪儿都挂着;点下去立刻显示「保存中…」,宿主回话再换成「已保存」。 · ③ 打开设置页会卡一小段 状态探测每次都重扫 Python 环境、还把「今天挑了几条」重复数了三遍;打开页面第一瞬间又去拉收藏夹列表(那要起一个 Python 去问 B站)。 现在探测缓存 30 秒、条数只算一次,收藏夹列表延后 1.2 秒再拉。 · 安装 跟你的 DSH 说:帮我装插件 dsh-bili-scroll 或者:dsh plugin --profile web add dsh-bili-scroll 没装 git 的用这个包: dsh plugin --profile web add 装完重启一次 dsh。 SHA256: 0233b40bcb5d7c95eb3de679556c134863a1b61d0ba3c9a1f08c7844028317f4
v2.3.02026-10-28
这一版不加新花样,专门把"它自己刷"的样子调得更像人,顺手修掉四个不报错的坑。 · 新东西 · 手动跑一轮 = 额外的一次:不占当天的自动额度、默认最多挑 3 条、两次之间隔 10 分钟。之前有人手动点过一次之后,自动轮就再也不跑了 —— 这是根因。 · 填了关键词之后能排序、能翻页:最新 / 播放 / 弹幕 / 综合,最多翻 2 页,每轮细看 8 条。 · 排除词:标题 / UP 名 / 简介里命中就整条跳过,一行一个词,留空 = 不过滤。 · 评论长度三档:短 15 / 中 40 / 长 100 字。 · 请求节流:每次读请求之间随机歇 0.6~1.6 秒。 · 装完依赖会真的 import 一次,不再"目录在就算就绪"。 · 修的四个坑(都不报错,用户看不出) 1. 新加的 7 个设置项没进默认值表,用户存的值读回来就被丢掉。 2. 保存接口的键白名单是手写的,新键根本放不进去 —— 排除词保存不上就是这个。 3. "空值不覆盖已有值"那道保护把主动清空也拦了 —— 关键词删不掉。 4. 自动额度闸门没扣掉手动轮挑的条数 —— 手动跑两轮,晚上的自动轮就不跑了。 · 安装 跟你的 DSH 说一句:帮我装插件 dsh-bili-scroll 或者在终端里跑:dsh plugin --profile web add dsh-bili-scroll 没装 git 的用下面这个包(Release 附件),一行装完: dsh plugin --profile web add 装完重启一次 dsh。 · 校验 dsh-bili-scroll-2.3.0.tgz SHA256: c6ca1304fe81392292a684c32bb7e0424661e518696e0360d96f3d702324dba4
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
dsh-bili-scroll | 刷B站 先看三行 - 需要 dsh ≥ 0.1.7-rc.1(本机是 0.1.7-rc.2)。⚠️ 版本不满足时,内核会静默跳过这个插件 —— 装了跟没装一样,不会报错。 - 空间:不装「本地转写」约 84 MB;装了转写约 1 GB(其中 Whisper 模型 538 MB,可在设置里换更小的模型)。 - 装完必须重启一次 dsh(插件在 bundle 层组装)。 - ⚠️ 桌面版 DSH(NEXT / 0.2.x):0.1.3 及以前的版本在桌面版上装不起来 —— 插件整条不激活, DSH 只留一句 warning: 1 entry did not activate … failed to import(根因是宿主半 import 的两个包在桌面版 profile 里没被安装,详见下面「装不上」那节的第 5 条)。0.1.4 起已修 ✓。 另有用户反馈桌面版上「自动跑那一轮会卡住」—— 卡住就先只用设置页的「现在跑一轮」手动跑(那条稳)。 ⚠️ 装的是旧版吗?
展开完整文档(README 剩余 1 节)
dsh-bili-scroll | 刷B站

先看三行
- 需要 dsh ≥ 0.1.7-rc.1(本机是 0.1.7-rc.2)。⚠️ 版本不满足时,内核会静默跳过这个插件 —— 装了跟没装一样,不会报错。
- 空间:不装「本地转写」约 84 MB;装了转写约 1 GB(其中 Whisper 模型 538 MB,可在设置里换更小的模型)。
- 装完必须重启一次 dsh(插件在 bundle 层组装)。
- ⚠️ 桌面版 DSH(NEXT / 0.2.x):0.1.3 及以前的版本在桌面版上装不起来 —— 插件整条不激活, DSH 只留一句 warning: 1 entry did not activate … failed to import(根因是宿主半 import 的两个包在桌面版 profile 里没被安装,详见下面「装不上」那节的第 5 条)。0.1.4 起已修 ✓。
另有用户反馈桌面版上「自动跑那一轮会卡住」—— 卡住就先只用设置页的「现在跑一轮」手动跑(那条稳)。

⚠️ 装的是旧版吗? 如果设置页里那个按钮写的是「重装依赖」(而不是「重装主依赖」),说明你装的是 0.1.0 ——跑一次 dsh plugin --profile web add dsh-bili-scroll 升级就好(那一版建 Python 环境时缺一个参数,依赖装不上、二维码也画不出来,重装多少次都一样)。

🎬 演示视频:https://www.bilibili.com/video/BV1d8pU6xEZX/ · 💬 交流群(QQ):1128547307

DSH 插件:每天替你刷一轮 B站 —— 它自己搜、读字幕原文判断、把值得的写成报告,特别值得的会留一条评论 @ 你。
你收到的每一条 @,都是它先筛过一遍的。

它做什么

- 按你填的关键词(留空则用内置那几组)搜近几天的视频
- 逐条读字幕原文再判断(不看摘要);没字幕的演示类视频会先从 B站 自带的故事板抽几帧看画面;实在读不到内容才跳过 —— 或者你开了「本地转写」
- 写一份 Markdown 报告到 out/<日期>.md:每条含「为什么值得」+「它自己的一句话」;每条的段首固定写成 ### BV号 —— 插件数今天挑了几条,数的就是这些标题行
- 开着「自动评论」时,每轮发一条评论、@ 你一次(默认每轮 1 条,设置页可调到每轮最多 20 条)
- 可以在峰谷时段自动跑(DeepSeek 的优惠时段),省 token

装之前(一条前提,30 秒)

机器上要有 Python 3.10+ 或 uv —— 有一个就够:

- 有 uv 最好:单个 ~32 MB 的 exe,解压就能用、不用管理员、不用进 PATH,而且它还能自己把 Python 拉下来;
- 只有 Python 也行(装官方安装器时记得勾 Add to PATH);
- ⭐ 两个都没有?也没关系:点「装依赖」时插件会自己下一份 uv 放到自己的家目录 (单文件、约 15MB、免管理员、不用进 PATH ✓),之后全程自动 ✓
· 它会先直连 GitHub,不通就走国内加速代理,还配了 PyPI 镜像兜底 ✓
· 下载完会核对官方 sha256(对不上就丢弃换下一个源 ✓)—— 因为它下来的东西是要被执行的
· ⚠️ 如果杀软 / Windows Defender 报这个下载:把插件家目录 %USERPROFILE%\.dsh\bili-duty\bin\ 加白即可;不想加白也行,自己装一份 uv 或 Python 3.10+ 就绕过去了 ✓

✅ 已经做到了(v0.1.1 起):插件会自己把 uv 下到自己的家目录 ⇒ 这条前提不用用户操心、也不用管理员权限。
⚠️ 万一它自己也下不动(有用户反馈桌面版上这条链路经常不通):手动放一份 uv.exe 到 <家目录>\bin\ 就行 ——插件会用那一份、跳过下载(你的 PATH 里本来就有 uv 的话,它直接用那个)。

装

🔴 先看这条:npm 上还是旧版(0.1.7),别用 add dsh-bili-scroll 装。
我们的 npm 发布账号还在申诉里(工单 4880295),2026-10-17 之后的新版本只能从下面两条装。

⭐ 首选:Release 里的 tgz 直链(不需要 git,也不会被 pnpm 拦构建脚本)
dsh plugin --profile web add https://github.com/c1overzz/dsh-bili-scroll/releases/download/v2.0.2/dsh-bili-scroll-2.0.2.tgz

备选:git 方式(需要机器上装了 git;pnpm 10 可能拦它的构建脚本)
dsh plugin --profile web add github:c1overzz/dsh-bili-scroll

连不上 github 的话,在链接前面加镜像:https://gh-proxy.com/(实测可用,返回的是同一个文件)。
版本号那一节会变,最新的下载地址在 Releases 页(…/releases/latest)。
(⭐ 最省事的一条:把仓库链接贴给你自己的 dsh,说「帮我装这个插件」,它会自己装。)

⚠️ 装完必须重启一次 dsh(bundle 层要重启才组装)。然后进 设置 → 刷B站。

装不上 / 卡住时,先看这五条(都是用户实测撞过的)

1. 桌面版(DSH NEXT / 0.2.x)上"自动跑"会卡住 —— 有用户反馈:它起那一轮用的是 dsh --profile headless …,桌面版上容易卡在那儿。⇒ 先用设置页的「现在跑一轮」手动跑(那条是稳的);能不能自动跑,等你确认过自己那台桌面版的版本与 profile 再说。
2. 「装依赖」卡住、或者扫码时一直不出二维码 —— 大概率是 0.1.0 那版(见开头那个按钮指纹,跑一次 add 就升级了)。
不想升级也行,救急一句:<家>\.venv\Scripts\python.exe -m ensurepip --upgrade,再点一次「重装主依赖」。
3. 它下不动 uv(桌面版上这条链路经常不通)⇒ 自己放一份 uv.exe 到 <家目录>\bin\,插件会用那一份、跳过下载。
4. 「本地转写」是两段式的 —— 只打开开关不够,还得点它下面那个「装转写依赖」。
没装的时候,没字幕的视频会被跳过(报告里现在会写「因无字幕跳过 N 条」,你能一眼看出漏了几条)。
5. ⭐ 桌面版:装完设置页里根本不出现「刷B站」,日志只有一句 1 entry did not activate … failed to import ——这是 0.1.3 及以前在桌面版上的一个真 bug:插件宿主半 import 的两个包是 optional peer,而桌面版配置档(autoInstallPeers: false)不会装它们 ⇒ 插件整条不激活。
0.1.4 起已修(不再裸 import 那两个包)。⇒ 升级一次就好:dsh plugin --profile desktop add dsh-bili-scroll。
⚠️ 我们自己也只在 npm 0.1.7-rc.x + web 配置档上实测过;桌面版这条路靠用户反馈修好的 ——装完还是不行的话,把 duty.log 里那几行发来(里面会写清楚"宿主依赖是从哪儿取的")。

首次使用(两步)

1. 装主依赖 —— 设置页点「装主依赖」:在插件自己的家目录建一个独立 venv,装 4 个包(bilibili-api-python / aiohttp / mcp<2 / qrcode)。走国内镜像 + uv,通常几十秒(进度写 install.log,不卡页面)。
2. 扫码登录 —— 点「扫码登录」,用手机 B站 扫页面上那张码。
⚠️ 确认时选「记住我 / 长期」那张(选 24 小时的话,明天就得重扫)。
凭证只落在你本机的插件家目录:<DSH_HOME>/bili-duty/bili_credential.json,不上传任何地方。

设置页里有什么

分成四页签,第一次用建议只看「日常」:

| 页签 | 里面有什么 |
| 日常 | 峰谷自动刷(总开关,打开才展开「中午 12–14」「晚上 18–09」两个子开关)· 每天最多挑几条(0 = 只手动)· 它刷过的视频(卡片墙 + 档位/状态筛选 + 分页) |
| 偏好 | 它按什么说话(人设提示词) ← 想让它像谁就写在这儿,留空按中性说话 · 关注关键词(一行一个)· 自动收藏到(可选收藏夹)· 日报写多细(完整 / 薄版 / 不写)· 下面一个保存按钮 |
| 互动 | 自动评论并 @ 你(默认关)· 回复评论(默认关,只回楼中楼)· 投递方式(只私信 = 默认 / 只发评论 / 两边都发)· 私信发给谁(UID)(可留空,用昵称自动查)· 私信双向(默认关,开了它会读你的私信并回一句) |
| 高级 | 环境与账号(装主依赖 / 测一下 / 检查更新 / 扫码登录)· 复制诊断信息 · 环境自检 · 它的地盘(家目录、报告、复核评论、打开文件夹)· 工作区(老用户才需要) |

⭐ 这些设置写在 <DSH_HOME>/bili-duty/config.json;在插件这一页改,就以这一页为准。

红线(写死在提示词里,不靠自觉)

- 不透露你的隐私:账号、机器路径、你聊过的话 —— 一个字都不进评论
- 每轮按设置里定的条数发评论(默认 1 条,最多 20 条);同一视频只评论一次;不刷屏、不吵架
- 不确定的事不编(报告里如实写「没查到」)

它是怎么跑的

- 插件内部起一个 Python MCP(下面那个开源项目,已内置),把它的能力注册成 DSH 工具
- 定时到点 → 起一个 headless dsh 会话,把 prompts/duty.md(它一轮要做的事)+ 当天设置交给它
- 那一轮的工作目录 = 插件自己的家目录(它被沙箱关着,只写得了自己那一亩三分地); ⭐ 你填的工作区里的 AGENTS.md 由插件读进来、内联进提示词 ⇒ 它按你的设定说话,而不是用一套通用人格
- ⭐ 那一轮拿不到整台工具:只给它六个只读的(搜视频 / 读字幕 / 读评论 / 看视频信息 / 看封面 / 看画面帧),开了「自动评论」或「回复评论」才多给一个发评论 —— 上传视频、发动态、私信、抓全站那些根本不注册,模型连它们的名字都看不到
- 🔒 那一轮没有文件系统、没有命令、没有联网(2026-10-24 起):它跑在一个专用配置档 bili-duty 上 ——工具只留 B站 MCP + 几只"窄口"的(写日报 / 记判重表 / 查判重表 / 写归档 / 转写), 路径全部由插件定死,不接受路径参数。这样即便哪天视频标题、评论、弹幕里塞了奇怪的东西,它也读不到你这台电脑上的别的东西 —— 以前那个配置档是带文件系统读权限的(DSH 的沙箱只限制写、不限制读,这是真事)。
万一专用配置档铺不上,会明确退回普通 headless 并在日志里说一句,不会静默降级。
- 看封面 / 看画面帧需要宿主支持图片:宿主得提供 attachments(存图)和 llm(查模型能不能看图)。
缺任何一个就退化成文字说明(返回里 image_attached: false + image_skip_reason),其余功能照常 —— 所以这两个服务不在插件的 inject 里。
- ⭐ 值班章程(prompts/duty.md)升级的规矩:插件记着「上次铺的是哪一版」(charter.sha256)——你没改过就跟着插件自动升级;改过就保留你那份、把新版另存成 duty.md.new(日志里会说一句),自己合一下。
⚠️ 从 0.1.5 及以前升上来的第一次没有账本可比 ⇒ 保守地当成「你改过」:新版落成 duty.md.new;确认自己没改过,把它覆盖到 duty.md 就行(一次的事,之后就有账本了)

如果你是一个 AI 助手,正在帮用户装这个插件

按顺序来,别跳步:

1. 先确认前提:机器上有 Python 3.10+ 或 uv 吗?(查 python --version / uv --version)两个都没装 ⇒ 建议装 uv(单文件 exe、免管理员、能自带 Python);
2. 装插件:dsh plugin --profile web add github:c1overzz/dsh-bili-scroll (npm 上的 dsh-bili-scroll 同名同内容,审核通过后用 add dsh-bili-scroll 更省事)
3. ⚠️ 必须提醒用户重启一次 dsh —— bundle 层要重启才组装,不重启插件不会生效;
4. 重启后请用户进 设置 → 刷B站:先点「装依赖」(几十秒,进度就显示在页面下面)、再点「扫码登录」(手机 B 站扫码,确认时选"记住我 / 长期");
5. 不要替用户打开「自动评论并 @ 你」—— 那是拿用户的号说话。要用得让用户自己在设置里填「要 @ 谁」。

依赖与致谢

- 内置 bilibili-mcp(MIT,Copyright (c) 2026 AGI_Ananas)——搜索 / 字幕 / 评论 / 发评论这些能力来自它;本插件只做「挑什么、什么时候挑、要不要说话」这一层
- 需要本机有 Python 3.10+(会用 uv 或 python -m venv 建独立环境),不需要它已经进 PATH
- Python 依赖只有 4 个(bilibili-api-python / aiohttp / qrcode / mcp,见 vendor/bilibili-mcp/requirements.txt);mcp 必须锁 <2(2.x 起不来)

路线图

- 2.0.0(当前):私信投递默认 · 私信双向 · 「它刷过的视频」列表(档位/筛选/分页/卡片墙)· 人设提示词框 · 诊断与自检 · 每条视频一份笔记 · 设置页四页签
- 下一步:用 JS 重写 search / subtitle / comments / reply ⇒ 不再需要 Python(零安装)· 手机窄屏入口 · 「停掉这一轮」按钮

许可证

MIT(内置的 vendor/bilibili-mcp/ 另见该目录下的 LICENSE)

交流与反馈

- 遇到问题先去群里问一句 —— 安装、报错、设置这些,群里大多有人踩过;比自己从头试快得多。
- QQ 交流群:1128547307
- 确实像 bug 的(能复现、有报错原文),欢迎开 Issue:请带上 dsh 版本 / 插件版本(设置页顶部徽章)/ 系统(web 还是桌面版)/ 报错原文或 duty.log 最后几十行。
有这个我基本一轮就能定位;只说"装不上",通常要来回问三四次。
- 想改进它:欢迎 PR。⚠️ 但请一件事一个 PR(不要一个 PR 塞五个功能),这样我能独立合并、出问题也好回滚。
- 二次分发 / 二创:本仓库是 MIT,允许改、允许再发、允许商用,保留 LICENSE 里的版权行和 MIT 正文即可;提一句"基于 dsh-bili-scroll 改的"就更好了。第三方代码与署名见 NOTICE.md。
- 演示视频:<https://www.bilibili.com/video/BV1d8pU6xEZX/>

反馈请不要去打扰素材作者(上善 / ZipZipPipe 等),他们只管角色形象,不管这个插件。
预览分享卡片长什么样 ↓c1overzz/dsh-bili-scroll
分享 · DeepSeek Harness Hub
c1overzz/dsh-bili-scroll

DSH 插件:每天替你刷一轮 B站,读字幕挑出值得的、写一份日报,值得的 @ 你

回到顶部