dsh-douyin-scroll | 刷抖音
45.3/100
综合分
22
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/27 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
自动兼容检查有告警:未发布到 npm registry,仅可从源码安装。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add NEWJO123/dsh-douyin-scroll该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:已验证
是什么
dsh 原生插件 · tool
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 8 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意未发布到 npm registry,仅可从源码安装
展开查看检查明细 ↓
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✗npm 包dsh-douyin-scroll(未发布到 npm,仅可源码安装)
✓Node 引擎要求 >=22 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/31 16:32:32
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件写入剪贴板(不计风险)发起外部网络请求
查看扫描证据(共 264 处,展示前 110 条)
- 读取本地文件index.js:304
const mtime = statSync(pkgFile).mtimeMs - 读取本地文件index.js:306
myVer = String(JSON.parse(readFileSync(pkgFile, 'utf8')).version || '') - 写入本地文件index.js:319
mkdirSync(outDir, { recursive: true }) - 写入本地文件index.js:320
mkdirSync(inboxDir, { recursive: true }) - 写入本地文件index.js:329
appendFileSync(logFile, s, 'utf8') - 读取本地文件index.js:796
if (existsSync(win)) return win - 读取本地文件index.js:799
for (const d of readdirSync(lib)) { - 读取本地文件index.js:802
if (existsSync(p)) return p - 读取本地文件index.js:808
try { return readdirSync(sitePkgs(venvRoot)).some((n) => n.startsWith(prefix)) } catch { return false } - 写入本地文件index.js:817
const appendInstall = (s) => { try { appendFileSync(installLogFile, s, 'utf8') } catch { } } - 执行外部命令来自配置/变量index.js:823
c = spawn(cmd, args, { cwd, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'], env: pyEnv(env || {}) }) - 执行外部命令来自配置/变量index.js:834
const c = spawn(cmd, ['--version'], { windowsHide: true, stdio: 'ignore' }) - 执行外部命令来自配置/变量index.js:961
child = spawn(cmd, args, { env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }) - 写入本地文件index.js:1101
writeFileSync(sendGateFile, JSON.stringify(st, null, 2), 'utf8') - 写入本地文件index.js:1110
writeFileSync(sendGateFile, JSON.stringify(st, null, 2), 'utf8') - 执行外部命令固定命令index.js:2318
const probe = spawnSync('node', ['--version'], { encoding: 'utf8', windowsHide: true, timeout: 10_000 }) - 执行外部命令来自配置/变量index.js:2321
const which = spawnSync(process.platform === 'win32' ? 'where' : 'which', ['node'], - 删除本地文件index.js:2895
try { rmSync(bak, { force: true }) } catch { } - 执行外部命令来自配置/变量index.js:3160
const child = spawn(dsh.command, args, { - 删除本地文件index.js:4181
try { rmSync(join(stateDir, 'deskpet-source.json'), { force: true }) } catch { } - 发起网络请求index.js:4386
const res = await fetch(url, { signal: ac.signal, headers: { accept: 'application/json' } }) - 删除本地文件index.js:5169
try { if (existsSync(mine)) rmSync(mine) } catch { } - 发起网络请求client.js:33
const jget = async (p) => asJson(await fetch(API + p, { credentials: 'same-origin' })) - 发起网络请求client.js:34
const jpost = async (p, body) => asJson(await fetch(API + p, { - 写入剪贴板client.js:820
if (navigator.clipboard && navigator.clipboard.writeText) await navigator.clipboard.writeText(text) - 读取本地文件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/state.mjs:43
mkdirSync(dir, { recursive: true }) - 读取本地文件lib/state.mjs:62
const raw = readFileSync(file, 'utf8') - 写入本地文件lib/state.mjs:68
writeFileSync(file, JSON.stringify(value, null, 2), 'utf8') - 写入本地文件lib/state.mjs:76
writeFileSync(file, text, 'utf8') - 写入本地文件lib/state.mjs:77
try { if (process.platform !== 'win32') chmodSync(file, 0o600) } catch { } - 写入本地文件lib/state.mjs:114
mkdirSync(join(stateDir, 'inbox'), { recursive: true }) - 读取本地文件lib/state.mjs:115
if (existsSync(exFrom) && !existsSync(exTo)) { - 写入本地文件lib/state.mjs:116
copyFileSync(exFrom, exTo) - 读取本地文件lib/state.mjs:130
try { if (existsSync(home)) return home } catch { } - 读取本地文件lib/state.mjs:146
const readIf = (f) => { try { return readFileSync(f, 'utf8') } catch { return '' } } - 读取本地文件lib/state.mjs:148
if (!existsSync(shipped)) { log(`缺文件:${shipped}`); return } - 读取本地文件lib/state.mjs:150
const newText = readFileSync(shipped, 'utf8') - 读取本地文件lib/credential.mjs:94
if (!existsSync(cookiePath(stateDir))) return '' - 读取本地文件lib/credential.mjs:95
return readFileSync(cookiePath(stateDir), 'utf8').trim() - 读取本地文件lib/credential.mjs:113
bytes: (() => { try { return statSync(cookiePath(stateDir)).size } catch { return raw.length } })(), - 读取本地文件lib/credential.mjs:114
at: (() => { try { return statSync(cookiePath(stateDir)).mtime.toISOString() } catch { return null } })(), - 读取本地文件lib/credential.mjs:122
if (existsSync(f)) writeSecret(f, '') - 发起网络请求lib/notify.mjs:132
const res = await fetch(target, { - 写入本地文件lib/toast.mjs:55
mkdirSync(dir, { recursive: true }) - 写入本地文件lib/toast.mjs:57
writeFileSync(ps1, PS_SCRIPT, 'utf8') - 写入本地文件lib/toast.mjs:83
writeFileSync(jsonFile, JSON.stringify(payload), 'utf8') - 执行外部命令固定命令lib/toast.mjs:85
const r = spawnSync( - 删除本地文件lib/toast.mjs:99
try { rmSync(jsonFile, { force: true }) } catch { } - 读取本地文件lib/uvfetch.mjs:33
return createHash('sha256').update(readFileSync(file)).digest('hex') - 发起网络请求lib/uvfetch.mjs:45
const r = await fetch(source.url + '.sha256') - 读取本地文件lib/uvfetch.mjs:55
try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return null } - 执行外部命令来自配置/变量lib/uvfetch.mjs:69
const r = spawnSync(cmd, args, { stdio: 'ignore' }) - 读取本地文件lib/uvfetch.mjs:83
if (existsSync(target)) return target - 写入本地文件lib/uvfetch.mjs:85
mkdirSync(dir, { recursive: true }) - 删除本地文件递归lib/uvfetch.mjs:87
rmSync(tmp, { recursive: true, force: true }) - 写入本地文件lib/uvfetch.mjs:88
mkdirSync(tmp, { recursive: true }) - 请求外部主机pypi.tuna.tsinghua.edu.cnlib/uvfetch.mjs:100
const r = await fetch('https://pypi.tuna.tsinghua.edu.cn/pypi/uv/json') - 发起网络请求lib/uvfetch.mjs:100
const r = await fetch('https://pypi.tuna.tsinghua.edu.cn/pypi/uv/json') - 发起网络请求lib/uvfetch.mjs:124
const res = await fetch(s.url) - 写入本地文件lib/uvfetch.mjs:126
await pipeline(Readable.fromWeb(res.body), createWriteStream(s.file)) - 读取本地文件lib/uvfetch.mjs:127
const mb = (statSync(s.file).size / 1048576).toFixed(1) - 写入本地文件lib/uvfetch.mjs:144
copyFileSync(hit, target) - 写入本地文件lib/uvfetch.mjs:145
try { chmodSync(target, 0o755) } catch { } - 执行外部命令来自配置/变量lib/uvfetch.mjs:147
const r = spawnSync(target, ['--version'], { stdio: 'ignore' }) - 删除本地文件递归lib/uvfetch.mjs:150
rmSync(tmp, { recursive: true, force: true }) - 删除本地文件递归lib/uvfetch.mjs:158
rmSync(tmp, { recursive: true, force: true }) - 读取本地文件lib/memory.mjs:43
try { return JSON.parse(readFileSync(file, 'utf8')) } catch { return fallback } - 写入本地文件lib/memory.mjs:46
try { writeFileSync(file, JSON.stringify(value, null, 1), 'utf8'); return true } catch { return false } - 写入本地文件lib/memory.mjs:51
try { mkdirSync(d, { recursive: true }) } catch { } - 读取本地文件lib/memory.mjs:195
for (const f of readdirSync(d)) { - 读取本地文件lib/memory.mjs:199
try { out.bytes += readFileSync(join(d, f)).length } catch { } - 读取本地文件lib/memory.mjs:209
if (existsSync(f)) rmSync(f) - 删除本地文件lib/memory.mjs:209
if (existsSync(f)) rmSync(f) - 读取本地文件lib/memory.mjs:219
for (const f of readdirSync(d)) { - 删除本地文件lib/memory.mjs:221
try { rmSync(join(d, f)); n++ } catch { } - 读取本地文件check-client.mjs:16
if (env && existsSync(env)) return env - 读取本地文件check-client.mjs:20
if (existsSync(p)) return p - 读取本地文件check-client.mjs:28
for (const g of guesses) if (g && existsSync(g)) return g - 读取本地文件check-client.mjs:57
const st = statSync(join(PROF, 'node_modules', PKG)) - 读取本地文件check-client.mjs:68
const pkg = JSON.parse(readFileSync(pkgJson, 'utf8')) - 读取本地文件check-client.mjs:83
if (!existsSync(p)) bad('文件不存在:' + p, '重新拷贝插件目录') - 读取本地文件lib/chrome.mjs:58
try { if (p && existsSync(p)) return p } catch { } - 发起网络请求lib/chrome.mjs:73
try { return (await fetch(`http://127.0.0.1:${port}/json/version`)).ok } catch { return false } - 写入本地文件lib/chrome.mjs:130
mkdirSync(profile, { recursive: true }) - 读取本地文件lib/chrome.mjs:141
const hadLock = (() => { try { return existsSync(lockFile) } catch { return false } })() - 执行外部命令来自配置/变量lib/chrome.mjs:145
const p = spawn(browser, [ - 发起网络请求lib/chrome.mjs:190
const list = await (await fetch(`http://127.0.0.1:${usePort}/json/list`)).json() - 写入本地文件lib/paths.mjs:41
if (create) mkdirSync(dir, { recursive: true }) - 写入本地文件lib/paths.mjs:48
export const outDir = () => { const d = join(homeDir(), 'out'); mkdirSync(d, { recursive: true }); return d } - 读取本地文件lib/paths.mjs:53
try { return JSON.parse(readFileSync(runtimeFile(), 'utf8')) || {} } catch { return {} } - 写入本地文件lib/paths.mjs:59
try { writeFileSync(runtimeFile(), JSON.stringify(next, null, 2), 'utf8') } catch { } - 读取本地文件lib/paths.mjs:66
try { return existsSync(f) && readFileSync(f, 'utf8').trim().length > 0 } catch { return false } - 读取本地文件lib/paths.mjs:66
try { return existsSync(f) && readFileSync(f, 'utf8').trim().length > 0 } catch { return false } - 读取本地文件selfcheck.mjs:27
const readIf = (p) => { try { return readFileSync(p, 'utf8') } catch { return '' } } - 读取本地文件selfcheck.mjs:47
if (existsSync(join(here, f))) ok(f) - 读取本地文件selfcheck.mjs:54
const pkg = JSON.parse(readFileSync(join(here, 'package.json'), 'utf8')) - 读取本地文件selfcheck.mjs:66
const y = readFileSync(join(here, 'cordis.patch.yml'), 'utf8') - 执行外部命令来自配置/变量selfcheck.mjs:76
const r = spawnSync(process.execPath, ['--check', join(here, f)], { encoding: 'utf8' }) - 读取本地文件selfcheck.mjs:89
const c = readFileSync(join(here, 'client.js'), 'utf8') - 读取本地文件selfcheck.mjs:110
const s = readFileSync(join(here, 'tools', 'im-send.mjs'), 'utf8') - 执行外部命令固定命令selfcheck.mjs:378
const git = (args) => spawnSync('git', args, { cwd: here, encoding: 'utf8', timeout: 15_000 }) - 执行外部命令来自配置/变量selfcheck.mjs:557
const st = spawnSync(process.execPath, [mcpFile, '--selftest'], { - 执行外部命令来自配置/变量selfcheck.mjs:647
const r = spawnSync(process.execPath, [join(here, 'tools', 'panel-render-check.mjs')], { - 执行外部命令来自配置/变量selfcheck.mjs:702
const r = spawnSync(process.execPath, [join(here, 'tools', 'pet-relay-test.mjs')], { - 执行外部命令来自配置/变量selfcheck.mjs:1106
const t = spawnSync(process.execPath, [join(here, 'tools', 'memory-test.mjs')], { encoding: 'utf8', timeout: 60_000 })
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库
涉及外部主机:pypi.tuna.tsinghua.edu.cn
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh-tools给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
README
由 DeepSeek 最新模型翻译生成展开完整文档(README 剩余 1 节)
dsh-douyin-scroll | 刷抖音
[!WARNING]
用之前先想清楚(风险自担)
- 这个插件会把你自己的抖音登录凭证(Cookie)交给本机脚本,替你调用抖音的接口。这不是抖音官方功能 —— 属于个人学习性质的自动化,可能违反抖音的用户协议,账号风险由你自己承担。
- Cookie 等同于登录态:只存在本机($DSH_HOME/douyin-duty/,不进仓库、不上传任何服务器 —— 代码全是本地的,可自行审阅)。但请你别把 Cookie 贴给任何人,也别在公共电脑上用。
- 自动发送(私信 / 卡片 / 评论)默认关闭,且每一道都有 confirm 闸 —— 请在了解每一项开关做什么之后再打开;在别人视频下发评论是公开行为。
- 推荐流的筛选是启发式(互动比值 + 评论文本),不是内容理解 —— 它会误判,见「已知局限」。
- 仅供个人学习研究,请勿用于商业用途或大规模账号操作。
先看五行
- 它替你刷一轮抖音,把值得看的挑出来,写一份 Markdown 日报。
- 不看画面:不截图、不 OCR、不做图像识别、不做 ASR。判断只用文案 / 标签 / 作者 / 时长 / 互动数 / 高赞评论——全是 JSON 字段。
- 采集只要一份 Cookie:走不签名的只读接口(tab/feed + hot/search),不需要浏览器。
- 读评论 / 读通知要签名只读;发私信 / 发卡片 / 发评论才用无头 Chrome(屏幕上不出现窗口)。
- 默认不发任何评论。自动评论是可选功能,默认关,且「@ 自己」是否成立未经验证。
它做什么 / 不做什么
| ✅ 做 | ❌ 不做 |
| 用只读推荐流接口取候选(3 页 feed + 热榜) | 不看视频画面(无截图 / OCR / 视觉模型) |
| 代码层硬筛:没文案、时长 <12 秒、文案太短且评论少 → 直接跳过 | 不做语音识别(不装 Whisper) |
| 拉高赞评论作为判断依据(前 5 条) | 不下载视频(只取元数据) |
| 用「文案 + 标签 + 互动比值 + 高赞评论」判断值不值得点开 | 不做关键词搜索(抖音 web 没有可用搜索接口) |
| 写 Markdown 日报到 out/<日期>.md | 不做私信自动投递(没有稳定接口;发信走工具,且要你点头) |
| 可选:Windows 通知 / webhook | 不代你登录、不上传任何凭证 |
| 可选:私信、分享卡片、回评论(默认全关,且都要 confirm) | 不保证判断准确(见下面「已知局限」) |
三条路(2026-10-21 定型,别再自己加第四条)
| 干什么 | 怎么走 | 需要登录? |
| 取候选 / 出清单 | 不签名的只读接口 tab/feed + hot/search | 要(Cookie) |
| 读评论 / 读通知(含「评论区 @ 我」) | 签名只读(tools/dy-signer.mjs 出 a_bogus) | 要 |
| 发私信 / 发卡片 / 发评论 | 无头 Chrome 里跑抖音自己的前端 | 要 |
- ⚠️ 可见浏览器只在扫码登录时用一次;其余全是无头 Chrome 或纯 HTTP。
- ⚠️ 扫码 / 私信 / 卡片 / 评论这几个脚本只找 chrome.exe(不找 Edge)。
没装 Chrome 不影响「取候选 + 写日报」,但上面这几个动作会失败。
- ⚠️ 发任何东西前必须先问使用者(confirm: true 闸,不传只回草稿)。
判断是怎么做的(这是最核心的部分)
抖音没有字幕轨可白拿(B站 有),所以不能"读原文",只能读观众反应:
第 0 层 硬过滤(纯代码,0 token)没文案 ⇒ 跳过|时长 <12 秒 ⇒ 跳过|文案太短 且 评论少 ⇒ 跳过(两个阈值在设置页可调,默认 12 字 / 3 条;两个条件同时成立才丢) ⚠️ 太旧、带货、标题党 没有自动筛,交给模型逐条判断第 1 层 互动结构打分(纯数值,0 token)关键不是点赞绝对值,而是比值:收藏/点赞 高 ⇒ 有保存价值(教程/攻略/美食)转发/点赞 高 ⇒ 想转给别人(好笑/共鸣)评论/点赞 高 ⇒ 引发讨论 ⚠️ 播放量对别人的视频经常是 0 ⇒ 完全不参与计算第 2 层 语义判断(模型)输入是压缩过的证据包,不是原始 JSON:文案 / 作者 / 时长 / 发布时间 / 互动四件套 / 三个比值 / 高赞评论 top5 模型必须引用具体证据(哪条评论、哪个比值),禁止"感觉有趣" 第 3 层 不确定就不推证据不够 ⇒ 进「判断不了」区,不出现在推荐里
能区分的几类(各自判据不同):😂好笑 · 🛠有实用 · 💡涨知识 · 💗共鸣 · 🐱萌。
会主动降权:评论出现「被骗了/标题党」· 文案模板化 · 昵称带「测评/推荐」· 纯颜值 BGM(点赞高但评论空洞)。
⚠️ 诚实说:这套比 B站 版弱一档。B站 读字幕全文,抖音只能读观众反应。
它做不到"条条精准",但能做到"扫一眼报告,大部分值得点开"。策略是宽松召回 + 严格推荐:点错成本很低(划过去就行),漏掉才是真损失。
装
先跑预检(不用装 DSH;克隆下来后在本目录跑)
git clone https://github.com/NEWJO123/dsh-douyin-scroll.git
cd dsh-douyin-scroll
node selfcheck.mjs # 全部通过再装
安装(本地目录链接,开发/自用)
dsh plugin --profile web add link:<本目录>
⚠️ 装完必须重启一次 dsh(bundle 层要重启才组装)。然后进 设置 → 刷抖音。
已经装过的,怎么升级到新版本
cd dsh-douyin-scroll
git pull # 拉最新
node selfcheck.mjs # 确认预检全过
dsh plugin --profile web add link:<本目录绝对路径> # 重新指一次(幂等)
然后重启一次 dsh,设置页里「关于」那行版本号变了就是升上了。
配置和聊天记忆都放在插件家目录(不随仓库走),升级不动它们。
0.3.x 各版本的变化看 CHANGELOG.md。
首次使用(三步,约 2 分钟)
第 1 步:装主依赖
设置页点「装主依赖」。它会在插件家目录建一个独立 venv、装 curl_cffi + httpx(走国内镜像,通常 30 秒)。
进度看 install.log,不卡页面。
第 2 步:登录
两条路,任选一条:
A. 扫码(唯一会开可见浏览器的场景)
node tools/qr-login.mjs start
B. 设置页 → 抖音 Cookie / 登录 → 把浏览器里已登录抖音的 Cookie 请求头粘进去
Cookie 只落在本机 <家目录>/douyin_cookie.txt(Windows 上 ACL 收不紧,但目录在你自己 profile 下), 不进日志、不进报告、不上传。日志里只出现脱敏后的 key 名。
第 3 步:填「刷什么」
- 关注关键词:一行一个,例如 搞笑、美食、猫
- 推荐流分类:例如 游戏、游戏解说
- 指定作者:作者主页链接或 sec_uid(可选)
- 每天最多挑几条(默认 5)、每次最多筛选几个(默认 12)
⚠️ 关键词 / 分类在采集侧不起作用:抖音 web 没有可用的关键词搜索接口,这些词只会写进报告当标注。真正取候选的是推荐流 + 热榜。
然后点「现在跑一轮」。
报告长什么样
写到 <DSH_HOME>/douyin-duty/out/<日期>.md:
刷抖音 · 2026-10-21
本轮:证据包 10 条 → 逐条看过 10 条 → 推荐 4 条 · 顺带一提 4 条 · 判断不了 2 条因「没文案」跳过 2 条,因「时长<12 秒」跳过 2 条 ⚠️ 太旧、带货、标题党这些没有自动筛,靠逐条判断
值得看
7401234567890123401
加班到凌晨三点回家,猫以为我死了 · @两只猫的铲屎官 · 12 秒 · 8 小时前 🔗 https://www.douyin.com/video/7401234567890123401 📊 赞 12.8万 · 评 8600 · 藏 9000 · 转 2.1万为什么值得:高赞评论 3 条都在说"笑死/绷不住",转发/点赞 0.16 明显偏高 ⇒ 是那种会想转给朋友看的段子。
一句话:养猫的人看了会心一笑。
顺带一提(证据不够,没算进推荐)
- 7401234567890123456 · 某个美食教程 · 收藏比不错但评论太少,先记着
没把握 / 判断不了
- 7401234567890123457 · 文案只有「嗯」,评论 1 条 —— 没有判断依据,不硬猜
⚠️ ### <纯数字视频ID> 这一行是宿主数配额用的 —— 这一行数 = 今天挑了几条。
「顺带一提」「判断不了」里不要加这种标题,加了会吃掉当天额度。
我需要你提供什么(清单)
| 项 | 必须? | 怎么给 |
| 抖音 Cookie | 必须 | 设置页粘贴,或 node tools/qr-login.mjs start 扫码(不上传) |
| 关键词 / 分类 | 建议填 | 设置页,一行一个(只当报告标注,不参与采集) |
| 运行时间 | 可选 | 开「峰谷时段自动刷」→ 午间 12:00–14:00 / 夜间 18:00–09:00 |
| 每天几条 | 可选 | 默认 5,填 0 = 不自动刷(仍可手动跑) |
| 通知方式 | 可选,默认 Windows 通知 | 默认就会弹;想换群/机器人就 notifyMode=webhook + 你的 webhook 地址 |
| 要 @ 谁 | 只在开评论时需要 | 设置页填抖音昵称;留空 = 不发评论 |
你不需要提供:任何 API key、账号密码、抖音开发者资质、bb-browser、Playwright。
如何测试
① 不装 DSH 就能跑的判断层自检
python tools/smoke.py
→ 会打印"硬筛原因 + 证据包 + 粗分类",你能直接看出它怎么判断的
② 全套本地预检(不联网、不起浏览器)
node selfcheck.mjs
③ 装进 DSH 后设置页「测一下」= 查会话 + 试取一次候选 → 回答"能不能用"
设置项
| 项 | 默认 | 说明 |
| 每天最多挑几条 | 5 | 全天总量;0 = 不自动刷 |
| 每次最多筛选几个 | 12 | 证据包上限(传给 forward-pick.py --limit,最多 30) |
| 中午谷时 / 晚上谷时 | 开 / 开 | 12:00–14:00 / 18:00–次日 09:00 |
| 自定义时段 | 空 | 想加几个加几个(09:00–11:00、跨零点的 23:00–02:00 都行);跟上面两个叠加,任意一个命中就跑,每个时段每天一次 |
| 关键词 / 分类 / 作者 | 空 | 一行一个;只当报告标注,采集侧不按词搜 |
| 文案太短阈值 / 评论少阈值 | 12 / 3 | 两个条件同时成立才跳过 |
| 自动评论并 @ | 关 | 危险,默认关 |
| 每轮 / 每天最多评论 | 1 / 0 | commentPerDay=0 ⇒ 一条都不会发 |
| 通知方式 | toast | toast / file / webhook / both |
| 日报写多细 | full | full / brief(薄)/ off(不写) |
| pythonPath / dshBin | 自动 | 一般不用改 |
| 发私信给谁(昵称 / 稳定 ID) | 空 | dmTo 只显示;dmToId 才决定发给谁 |
| 更新地址 updateUrl | 空 | 留空 = 自动查 npm / GitHub;填了就只查这一个 |
2026-10-21 删掉了「采集来源」和「工作区」两项:前者只剩一条路(只读推荐流),后者是「读外部 AGENTS.md 当人设」的口子,已封死 —— 人设只读插件自带的 prompts/persona.md。
消息怎么到你手上
一轮跑完,结果要真的推到你面前,不能只是躺在文件夹里。默认走 Windows 通知,零配置:
| 方式 | 行为 | 要准备什么 |
| toast(默认) | 屏幕右下角弹一条:挑到几条 + 前两条值不值得看;留在通知中心(Win+N) | 什么都不用 |
| file | 不额外提醒,报告落在 out/<日期>.md | — |
| webhook | 推到企业微信 / 飞书 / 钉钉群,或任意 URL(custom) | 一个机器人 webhook 地址 |
| both | 弹通知 + 推 webhook | 同上 |
设置页「通知 → 发一条测试消息」点一下就知道通不通 —— 通了会当场弹到你屏幕上。
为什么不做「抖音私信发给我」:发私信要签名和登录态,且风险高;插件里虽然有 douyin_dm / douyin_share_card,但它们只在会话里由模型调用,且必须你点头,不会自动把日报私信给你。
发评论 / 私信 / 卡片(默认全关)
评论:三重保障,任何一重关着都不会发
1. 设置里「自动评论」关着(默认)→ 工具层直接拒绝
2. 评论执行器 = off(默认)→ 即使开关开着也不会发
3. 「要 @ 谁」留空 → 不会发(评论的意义就是那一下 @)
| 执行器 | 行为 | 状态 |
| off | 不发(默认) | ✅ |
| builtin | 走抖音评论提交接口 | ❌ 不可用(要验签票据)——调用会如实报错 |
| browser | 在真实页面上填评论,但不点发送 | ⚠️ 半自动(刻意保守) |
| http | 转发给你自己部署的服务 | ✅ 你有自己通道时用 |
🔴 风险(必须知道):
- 抖音评论区「@ 自己」是否成立未经验证;
- 评论以你本人账号名义发出;
- 频繁评论会触发风控/限流,可能影响你正常刷抖音。
私信 / 分享卡片
- 🔴 两个工具(douyin_dm / douyin_share_card)都必须传 confirm: true,否则只回草稿。
- 🔴 先卡片,后文字:既要发卡片又要发文字时,永远先卡片、隔几秒再文字。
douyin_share_card 给了 reason 会自动按这个顺序办(卡片 → 回读 → 隔 N 秒 → 理由), 不要再补一次 douyin_dm。
- ⚠️ 纯文本网址在抖音里点不开,只有分享卡片能点 —— 所以用卡片,不要拼文本链接。
- ⚠️ 回读能证明「发出去了」,证明不了「对方已读」。
已知局限(不掩饰)
1. 判断准确率低于 B站 版 —— 没有字幕,只能读观众反应。
2. 「文案极简 + 评论少」的视频一定会漏 —— 这是刻意的:没依据就不硬猜。
3. 播放量不可用 —— 对非作者的视频经常返回 0,所以判据里完全不用它。
4. 关键词搜索不可用 —— 抖音 web 的 general/search 被 2483 挡住;只有推荐流。
5. 推荐流每轮不一样 —— 同一时间跑两次可能拿到不同候选;报告里会标注每个采集源新增了几条。
6. 评论可能拉不到 —— 签名失效 / 风控 / 接口变更都会让某几条没有评论区证据;报告会如实写。
排障
| 现象 | 看哪里 |
| 设置页不出现「刷抖音」 | 重启 dsh;看日志有没有 1 entry did not activate |
| 拿不到候选 | health.json / duty.log / forward-pick.json 的 meta(每个源的 new 和 status) |
| 报告是空的 | 宿主会补一份「占位报告」并贴日志尾部;完整日志在 duty.log |
| 判断太保守/太激进 | 调「文案太短阈值 / 评论少阈值」和「每次最多筛选几个」 |
| 一轮跑太久 | 降证据包条数(前 5 条才拉评论,拉评论是最慢的一步) |
| 昨天挑的今天还算配额 | 不会:配额按 out/<今天>.md 里的 ### ID 数,跨天自然归零 |
数据都在:<DSH_HOME>/douyin-duty/(config.json / collector.json / forward-pick.json / round-evidence.json / duty.md / out/ / inbox/ / duty.log / health.json)。
目录结构
dsh-douyin-scroll/
├── package.json / cordis.patch.yml # 插件声明(entry id 必须 = douyin-scroll) ├── index.js # 宿主半端:配置/工具/定时器/证据包/headless 跑一轮/HTTP ├── client.js # 设置页(非 ESM 自注册脚本) ├── AI-USAGE.md # ⭐ 给别的 AI 看的使用说明(MCP 接入 + 安全红线) ├── selfcheck.mjs # 本地预检(110 项) ├── tools/ │ ├── mcp-server.mjs # ⭐ 标准 MCP server(stdio,零依赖)—— 别的 agent 工具接进来 │ ├── panel-render-check.mjs # ⭐ 设置页真渲染一遍(抓白屏,语法检查查不出) │ ├── doctor.mjs # ⭐ 环境体检(纯 Node,不依赖 Python) │ ├── forward-pick.py # ⭐ 取候选 + 硬筛 + 打分 + dropped 计数 │ ├── forward-run.mjs # 命令行一键:拉候选 + 弹通知 │ ├── api-comments.py # 签名拉评论 │ ├── notice-read.py # 签名读通知(含 @ 我) │ ├── api-reply.py # 签名回评论(只读先通才写) │ ├── comment-api.mjs # 无头页面里发评论/回复 │ ├── comment-mention-ui.mjs # 真 @ 提及(只有 UI 点选才算数) │ ├── comment-delete.mjs # 删自己的评论(页面里 POST /comment/delete/) │ ├── comment-reply.mjs # 真实 UI 回某条评论(保守方案) │ ├── comment-read-dom.mjs # 从评论区 DOM 读(绕开"只翻前 80 条"的接口盲区) │ ├── card-share.mjs # 分享卡片(无头页面点「分享给朋友」) │ ├── im-send.mjs # 私信发送 + 回读(头像左右判发送方) │ ├── mk-msg.mjs # 把清单拼成私信正文 │ ├── qr-login.mjs # 扫码登录(唯一可见浏览器) │ ├── cdp.mjs # CDP 小工具 │ ├── dy-signer.mjs # 抖音 web 签名器(纯 Node + jsdom) │ ├── session-guard.py # 会话保险丝(坏签名别打写接口) │ └── smoke.py # 判断层自检(离线) ├── lib/ │ ├── hostdeps.mjs # 三级兜底取宿主依赖 │ ├── paths.mjs # ⭐ 家目录/插件目录的唯一来源(不写死盘符) │ ├── chrome.mjs # 无头浏览器(找不到 Chrome 就找 Edge/Chromium) │ ├── state.mjs # 家目录 / 谷时窗口 / 章程记账 / Cookie 脱敏 │ ├── credential.mjs # Cookie 存取 │ ├── notify.mjs # webhook + 报告摘要 + URL 脱敏 │ ├── toast.mjs # Windows 通知(默认) │ └── uvfetch.mjs # 机器上没有 uv/python 时自动下一份 ├── prompts/ │ ├── duty.md # 一轮的章程(判据 + 红线 + 报告格式) │ ├── persona.default.md # 中性默认人设(随包发布) │ └── sample-candidates.json # 内置示例(离线演示用) └── requirements.txt # 只有 curl_cffi + httpx
给别的 AI 用(MCP)
插件自带一个标准 MCP server(tools/mcp-server.mjs,stdio,零依赖)。
Claude Code / Cursor / Cline / 另一个 DSH 都能接:
{
"mcpServers": {
"douyin": {
"command": "node",
"args": ["<插件目录>/tools/mcp-server.mjs"],
"env": { "DOUYIN_HOME": "<插件家目录>" }
}
}
}
设置页「给别的 AI 用 → 看接入方式」里有一键复制(路径按你这台机器算好)。
完整说明见 AI-USAGE.md,或者直接让那个 AI 调 douyin_guide。
为什么不做成"任何 AI 都能直接装":MCP 是各家通用的接口,但它只解决"能调工具"。
「按点刷一轮 → 写日报」那条值班链路仍然在 DSH 里(要 headless 会话 + 定时器)。
两件事分开:能力走 MCP,值班走 DSH 插件。
许可证
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
NEWJO123/dsh-douyin-scroll
dsh-douyin-scroll | 刷抖音
相关插件 · 工具 / 效率
破甲管控台kingcharleslzy-ai/dsh-purge
dsh-purge Community · DeepSeek Harness 社区维护版:宿主兼容、状态诊断、提示词管理、更新与本机备份回滚。基于小杨 / YuJunZhiXue 的 dsh-purge。
★ 4,173已汉化DeepSeek 客户端
74.4
网文长卷lingfengQAQ/webnovel-writer
基于 Claude Code 的长篇网文辅助创作系统,解决 AI 写作中的「遗忘」和「幻觉」问题,支持 200…
★ 7,636已汉化DSH
72.2
多智能体协作HKUDS/AutoSwarm
AutoSwarm: Unify Your Agents. Unify Your Context (Codex · Claude Code · DeepSeek Harness · Muse · Grok · WorkBuddy· nanobot · and more)
★ 2,349已汉化DSH
71.6
Vox 拼贴视频生成Alisa0808/vox-director
🎬 Vox Director拼贴动效导演
★ 2,129已汉化DSH
71.4
Agent 演化引擎Q00/ouroboros
智能体 OS:智能体自己变聪明,我们只守住底线:面试准入、分阶段评估、带预算的进化循环。MCP 服务器,14 种运行时:Claude Code、Codex CLI、Gemini CLI、OpenCode、Copilot、Kiro 等。
★ 6,308已汉化DSH
71.3
智能体运行框架路由HarnessRouter/harnessrouter
智能体运行框架的统一接口。
★ 3,897已汉化DSH
71.2
这是你的仓库?认领条目