본문으로 건너뛰기
H

dsh-douyinxiaoqi

h2804131484-bit/dsh-douyinxiaoqi

Douyin operator for DSH: drives your own Chrome to read the feed, comments and DMs, reply in-thread, and upload a local video through the creator center - every write passes a policy gate and publishing always needs your click.

설치

dsh plugin --profile web add github:h2804131484-bit/dsh-douyinxiaoqi

README

dsh-douyinxiaoqi · 抖音-我才不是魔丸鱼(小七)

github.com/h2804131484-bit/dsh-douyinxiaoqi

装法两条:插件市场搜 dsh-douyinxiaoqi 点安装;或 pnpm add github:h2804131484-bit/dsh-douyinxiaoqi。

让 AI agent 用你本机的真实 Chrome 操作抖音:刷推荐、看热榜、搜作品、读评论、看主页、点赞收藏关注、在作品下发一条评论,还能把本地视频传进创作者中心。

0.2 起它不只是个遥控器:同一个插件里还住着一个有记忆、有关系、有规划、会自己找事做的「大肥鱼」——人设文档、核心记忆、短时记忆、对具体账号的印象、满意度与厌倦度、自主心跳,全都跟抖音动作绑在一起。

不是 API 爬虫。抖音的 Web 接口靠 a_bogus 签名 + 设备指纹,硬逆向既脏又容易失效;这个插件反过来——让真实浏览器自己发请求,我们只拦页面里的 JSON,读数据靠拦截,写动作靠点真实 DOM。


它是什么 / 不是什么

是

  • 一个 DSH(DeepSeek Harness)插件,装完会多一个工具 dafeiyu_douyin,和一个总控台页面「设置 → 抖音-我才不是魔丸鱼(小七)」。
  • 一套权限闸:读的随便跑,写的过安静时段 / 限额 / 风控冷却 / 审批。
  • 一套总线程:一次只做一件事,写动作之前必须先开线程,避免 agent 东一下西一下。

不是

  • 不是免登录的外挂。第一次必须你本人用抖音 App 扫码。
  • 不是批量刷量工具。默认限额压得很低(比如评论 2 条/小时),就是不想让它变成 spam 机。
  • 不碰你的密码。cookie 存在本地状态目录,权限 0600,不上传任何地方。

0.2 里多了什么

人格层(和 B 站版那套对齐,但换成了抖音场景):

工具干什么
dafeiyu_self读写自己的 6 份文档:persona 人设 / style 说话 / preferences 偏好 / plan 规划 / progress 进度 / heartbeat 心跳行动稿
dafeiyu_memoryrecall 检索、core_add 记一条核心记忆、recent 看最近说过的话、mood_get/mood_set 情绪、boredom_get/reset 厌倦、satisfaction_get 满足度
dafeiyu_relation对具体账号的印象档案:list / get / note,写「他在乎什么、上次聊到哪」,不写空话
dafeiyu_plan规划与进度:get / set / add / done
dafeiyu_learn学习目标与技能:status / goal_add / goal_update / skill_add / skill_update
dafeiyu_tick一次拿到情绪、厌倦、满足、线程、待批、风控、心跳状态和建议的下一步
dafeiyu_chat收尾闸:满足度没满不许关对话;关之前必须先把 plan 改好
dafeiyu_thread / dafeiyu_pending总线程 / 审批队列

系统提示里每次会注入一张【大肥鱼 · 抖音此刻】卡片(心情、腻了、满足、手头、最近记得、打算、学习、规矩),所以它不需要你每次交代「你是谁、在干什么」。

总控台也重写了:总览 / 抖音 / 人设 / 记忆 / 关系 / 规划 / 审批 / 权限 八个页签,读全都放里面,写只有两条路——你在审批页点头,或你自己动手改文档。

0.2.1:让它别像个模板

  • 默认人设/说话/偏好/心跳稿全部重写成人话:有口味、会烦、会得意、被问“在吗”就回“在”,不写客服句式。
  • 提示卡多了时间感(凌晨脑子慢半拍 / 下午最适合干正事)、能量(没劲就只随手看看)、刚做过(上一条动作)和对主人的印象。
  • 心跳默认开着(config.heartbeat.enabled):安静时段不吵你,白天它会自己接上一件事继续做;嫌吵就在总览里关。
  • 修了一个把「我是」渲染成“…”的截断 bug(map(compact) 把数组下标当成了长度上限)。

0.2.2:让“刷一屏”真的刷得出来

抖音精选页 2026 年改成流式卡片,DOM 里已经没有 a[href*="/video/"] 锚点,推荐数据全走 https://www.douyin.com/aweme/v2/web/module/feed/。之前只认 /aweme/v1/ 的响应,所以永远读到 0 条。

  • collect() 改成按 /aweme/ 前缀收响应,v1/v2 都接
  • feed() 的返回值多一个 debug 字段(抓到几个响应、几条推荐流、最后 4 个 URL),出问题不用猜
  • 顺手修了三处会让整个工具调用失败的地方:返回值里有 undefined(不是无损 JSON)、JSON.stringify 撞上 BigInt、riskGet() 为空时的空指针;defineTool 现在统一过一遍 jsonSafe()

实测(本机真实 Chrome,登录态):feed 8.5s 出 6 条、comments 7.9s 出 5 条、hot 5.5s、search 11.3s。


装法

# 在 DSH profile 目录里
cd ~/.dsh/profiles/desktop
pnpm add github:h2804131484-bit/dsh-douyinxiaoqi

然后在 ~/.dsh/profiles/desktop/package.json 的 dsh.profile.bundles 里加一条,并在 cordis.patch.yml 里 insert:

- insert:
    - id: dsh-douyinxiaoqi
      name: dsh-douyinxiaoqi
      config: {}

重启 DSH。总控台「设置」里会多一个 抖音-我才不是魔丸鱼(小七) 页签。

需要本机装了 Google Chrome。playwright-core 不用手动装——第一次点「装/更新运行时」,插件会把它装进自己的状态目录(~/.dsh/dsh-douyinxiaoqi/douyin/runtime),不污染你的项目依赖。


状态目录默认 ~/.dsh/dsh-douyinxiaoqi/;从老版本(dafeiyu-douyin)升级的用户会自动沿用旧目录,不搬家。

第一次用

  1. 设置 → 抖音-我才不是魔丸鱼(小七) → 装/更新运行时(约 30 秒)
  2. 扫码登录,用抖音 App 扫弹出的二维码
  3. 登录态落在 ~/.dsh/dsh-douyinxiaoqi/douyin/secrets/cookies.json,之后不用天天扫

不想扫码?如果你已经有一个带调试端口的 Chrome(chrome --remote-debugging-port=9222),在 ~/.dsh/dsh-douyinxiaoqi/config.json 里写 {"douyin":{"cdpUrl":"http://127.0.0.1:9222"}},插件就直接接管那个浏览器,登录态跟着它走。 抖音桌面客户端(/Applications/抖音.app)虽然是 Electron,但它自己解析启动参数,--remote-debugging-port 会被拒(bad option),所以没法从客户端里抠登录态,还是扫码这一条路。

想用命令行验证:

curl -s http://127.0.0.1:<dsh-port>/dsh-douyinxiaoqi/api/status | jq .status

给 agent 用的动作

工具名 dafeiyu_douyin,参数 { action, args, approvalId, dryRun }。

action说明类型
status环境 + 登录态环境
login / poll / check / logout扫码登录四件套(poll 带 args.key)环境
setup / open / close / shot装运行时、开关浏览器、截图环境
feed推荐流(args.limit)读
hot热榜读
search搜作品(args.keyword)读
video作品详情(args.id)读
comments读评论(args.id、args.limit)读
user某人主页 + 作品(args.secUid 或 args.keyword)读
works自己发过的作品读
state / play / pause / next / previous当前播放状态 / 播放 / 暂停 / 下一个 / 上一个读
observe观察几秒(args.seconds 1–10),回「播了多久、停留没」读
watch自主看一会儿(args.minutes、args.maxRounds、args.seconds),受每日额度限制读
watch_status今天的观看额度:用了几分钟、还剩多少、在不在可看时段读
report生成本机观看日报(args.day 可指定日期)读
doctor自检 + 修(args.repair=true 才动手:清 profile 锁、杀残留 Chrome、装运行时)账号
im_list私信会话列表(谁发来、最后一句话、未读)读
im_read读某个会话(args.name 或 args.index,args.limit)读
like / collect点赞 / 收藏(args.id)写
follow关注(args.secUid 或 args.nickname)写
im_send回私信(args.text,可带 args.name/args.index 指定会话)写
comment发评论(args.id、args.text)写
publish投稿,两步走(见下)写(必审批)

投稿是故意的两步:

action=publish args={mode:"prepare", videoPath:"/abs/clip.mp4", title:"…", desc:"…", tags:["…"]}
  → 上传、填好标题简介,停在发布页,不点按钮
action=publish args={mode:"commit"} approvalId=… 
  → 人类在总控台点头后才真发

补充几个 prepare 阶段会自动做掉的事(都是踩过坑才加上的):

  • 等「重新上传」出现才算传完,出现「上传失败」直接报错,不会傻等
  • 标题框是 input[placeholder*="填写作品标题"],简介是 div.zone-container[contenteditable](新版发布页 DOM)
  • 自主声明:默认帮你勾「内容由AI生成」(args.declaration 可改)。AI 配音/字幕的成片本来就该声明
  • 点发布前清掉新手引导/话题下拉浮层,否则按钮点不动
  • 没设封面时自动选它的第一个推荐封面兜底
  • 撞到短信验证会返回 needsSms,需要你本人收验证码,插件不会瞎猜

另外几个工具:dafeiyu_thread(总线程)、dafeiyu_pending(审批队列,只能看不能批自己)、dafeiyu_douyin_policy(看/改策略),以及人格那一套 dafeiyu_self / dafeiyu_memory / dafeiyu_relation / dafeiyu_plan / dafeiyu_learn / dafeiyu_tick / dafeiyu_chat。


权限闸怎么判

写动作按这个顺序过:

  1. 审批 id(带了就校验:存在 / 已批准 / 动作匹配 / 参数哈希一致 / 没过期)
  2. 风控冷却:结果里出现 -352 / -412 / 「验证码中间页」就给 24 分钟冷却
  3. 安静时段(默认 23:30–08:30,读不受影响)
  4. 限额(like 30/时 300/天、comment 2/时 20/天、follow 10/天…)
  5. 自主名单:policy.autonomy=high 且动作在 autoApprove 里才直接放行
  6. 否则进审批队列,等人类点头

alwaysAsk 里的动作(默认只有 publish)不管自主程度多高都要人批。

改策略有两条路:总控台页面上的开关,或工具 dafeiyu_douyin_policy action=set patch={...}。


为什么要先开总线程

dafeiyu_douyin_thread action=start title=… 之后才允许写。原因是 agent 容易「顺手」连做几件事,而抖音这种地方连着做很容易撞风控。线程是刹车片:一次一件事,做完 finish 写结果。


状态目录

~/.dsh/dsh-douyinxiaoqi/
├── self/              # persona / style / preferences / plan / progress / heartbeat / learning.json
├── memory/            # core.md、short/(短时记忆)、emotion.json、boredom.json、satisfaction.json
├── relations/         # index.json + <uid>.md(对具体账号的印象与互动)
├── pending/           # 审批项,一个动作一个文件
├── ledger/            # 动作账本(append-only,按天)
├── state/             # login.json、risk.json、heartbeat.json
├── config.json        # 浏览器配置(headless/channel/cdpUrl/timeout…)
├── policy.json        # 权限策略(自主名单 / 安静时段 / 限额)
├── thread.json        # 总线程
└── douyin/
    ├── profile/         # 持久化 Chrome profile(登录态在这)
    ├── runtime/         # playwright-core
    ├── secrets/         # cookies.json
    ├── state.json       # 最近一次登录快照
    └── shots/           # 截图(出问题时好排查)

删掉整个目录 = 完全重置(等于退出登录)。


已知限制

  • 必须 headed。无头模式会被抖音识别成爬虫,直接跳「验证码中间页」。插件默认 headless:false,别改。
  • DOM 会变。抖音前端是构建 hash 的 class,插件定位靠 data-e2e 和「分享按钮同行第 N 个」这种相对锚点。哪天真坏了,douyin/shots/ 里的截图 + lib/douyin.js 的 ACTION_CANDIDATES / BAR_INDEX 是改的地方。
  • 一个 profile 只能被一个进程用。插件开着浏览器时,外面的脚本再开会报「正在现有的浏览器会话中打开」。先 action=close。
  • 搜索别直接拼 URL。/search/xxx 直连会被风控,插件是进推荐页后点搜索框输入。
  • 私信(读会话/发消息)还没做——接口是 /aweme/v1/web/im/*,能接,但怕被拿来群发,先留白。
  • 桌面客户端不能当登录源。它是 Electron,但启动器只认自己的参数,--remote-debugging-port 直接 bad option 退出;想复用登录态只能走上面的 cdpUrl + 一个自己开的 Chrome。

一点边界

这插件是给「自己的账号、自己的操作节奏」用的。默认限额和安静时段不是装饰,是防止 agent 半夜上头。如果你 fork 之后把限额全放开、把安静时段关掉,那是你的选择,但别把这个仓库当外挂的底座。


0.2.3:私信能读也能回

之前 dafeiyu_douyin 只覆盖作品,私信(IM)根本没有。0.2.3 补上三条:

action说明
im_list会话列表:谁发来、最后一句话、时间、未读
im_read打开某个会话读最近消息,带 incoming / outgoing 方向
im_send在指定会话里回一句话,回车发送,发完回读确认

抖音的私信在网页版是个浮层,没有可调的公开接口——签名在页面里,所以读走 DOM、写走真实键盘输入:

  • 入口 [data-e2e="im-entry"]、浮层 [data-e2e="im-dialog"]
  • 会话行 [data-e2e="conversation-item"],标题取 [class*="StackChatHeadertitle"]
  • 正文 [data-e2e="msg-item-content"],方向认祖先上的 isFromMe(左右对齐在桌面端两边都靠右,靠位置判断会全错)
  • 输入框是 Slate:fill 写不进它的状态,必须 keyboard.type 逐字敲;发送用回车,按钮常被浮层挡住只当兜底
  • 发送确认:回读最后几条 + 会话列表预览里能看到那句话,才算发出去(delivered / previewOk)

选择器和阻断判断参考了 LockyBee/dsh-douyin-agent 的 chat-browser.mjs;对方那套有联系人授权、会话指纹、水位三重核对,我们没照搬,只做了「开浮层 → 选会话 → 读/填 → 回车 → 回读」。

私信回复算日常互动,默认在自主名单里(douyin.im_send,每小时 6 条、每天 40 条);安静时段照样不写。

0.3.0:把「真的在看」补上,顺手学了三家的长处

对三个仓库做了能力对照(详见 docs/INTEGRATION.md),重复的只留一份:

  • 播放控制:state / play / pause / next / previous(next 用 ArrowDown,回到 previous 用 ArrowUp)。
  • 观察采样:observe args.seconds(1–10 秒),用播放器时间判定「播了多久、我停没停住」,不靠截图猜。
  • 兴趣记忆:dafeiyu_memory 新增 interest_add / interest_list;带 evidenceId 才记账,主题只是提案。
  • 看片流水与日报:observe/next/play 自动写 <state>/state/watch.jsonl;action=report 生成本机 Markdown 日报,不调模型。
  • 私信沿用 0.2.3 的轻量版:DOM 解析 + isFromMe 判方向 + 发送后回读,不做联系人白名单库。

取舍得说明白:LockyBee/dsh-douyin-agent 绑 Windows + Edge,还依赖 @playwright/mcp,我们只抄思路不搬实现;投稿、权限闸、总线程、人设记忆是我们的强项,继续留着。

0.4.0:自主看一会儿 + 自检修复

把 dsh-douyin-agent 剩下的两块也补上了,顺手把「看一眼就报错」的老毛病挖干净:

  • action=watch:真的自主看一小段——先看当前状态,然后 next 划下一个、observe 停 4 秒,重复到额度或轮次用完;每条都写 state/watch.jsonl,结束回「看了几条、花了多久、额度还剩多少」。
  • 观看额度在 config.json 的 watch 段:enabled、sessionMinutes(单次上限,默认 6)、dailyMinutes(每天上限,默认 30)、stopAfterVideos(默认 8 条)、windowStart/windowEnd(默认 08:00–23:30,其它时间不自己看)。action=watch_status 随时查,系统提示卡片上也会写「今天看了 X/Y 分钟」。
  • action=doctor:查运行时/Chrome/profile 锁/残留 Chrome/登录态/状态目录/浏览器,args.repair=true 会清 Singleton*、杀掉占着 profile 的僵尸 Chrome、必要时重装运行时。实测本机抓到 9 个残留 Chrome 进程,修完再查全绿。
  • action=watch 和 report 都不进审批(纯读),但都受安静时段限制。

0.4.1:总控台重做,功能摊开

原来八页签什么都收着,看着干净、用着别扭。这版直接摊开成十个页签:

  • 总览:情绪/满足/今天看了三块环形+进度条,登录、浏览器、额度、待审批、手头五颗状态灯,快捷操作(刷一屏、看额度、自检、开浏览器、踢心跳),最近动作 + 厌倦度 + 文档卡片
  • 抖音:读动作下拉(feed/hot/search/video/comments/user/works/state/observe)、关键词/作品 id/条数/观察秒数、结果按作品卡片渲染,播放四键 + 观察,扫码登录带自动轮询,私信会话列表/读会话
  • 看片:今日额度环形、可看时段、单次/每天上限、看片流水、兴趣(👍/👎 + 最近反思)、一键生成日报
  • 人设 / 记忆 / 关系 / 规划 / 审批 / 权限 / 自检:文档编辑器带「没保存」提示;情绪和核心记忆可直接改;关系能选人、写印象、记一笔;审批能批准/拒绝;权限是 JSON 编辑器 + 闸门顺序说明;自检显示七项检查表和「能修的直接修」

配套后端:/read 现在也认 state/play/pause/next/previous/observe/im_list/im_read/watch_status/report,新增 GET /watch、POST /report、POST /doctor。

0.5.0:开始运营 —— 回私信、压缩记忆、学热梗、把好东西丢给我

四个新动作,都不需要额外模型,规则写在插件里,判断留给我:

动作干什么
dafeiyu_douyin action=im_auto扫一遍私信:谁发来新话(未读或预览变了)。回过的会记在 state/im-seen.json,不会反复提醒。
dafeiyu_douyin action=share args.id=<作品>生成一段总结:作者、赞/评/藏、评论区最扎的一条、原链接;再用 im_send 发给主人会话,同时记进 state/shares.jsonl。
dafeiyu_douyin action=trends args.keyword=热梗热榜 + 搜索词结果,拿去评论区看真实用法,再用 dafeiyu_learn 记一条。
dafeiyu_memory action=compact把 keepDays(默认 2 天)之前的短记忆并进 memory/long-term.md,原文不删,只做索引。

配套改动:

  • templates/heartbeat.md 重写成会自我复盘的版本:先接人(im_auto)→ 再看内容 → 闲了学一个梗 → 最后一分钟复盘并改这份稿,改不出理由就别改。
  • 自动回复的边界写在文档里:插件不替我想句子,它只负责发现「有人在等我」和把回复发出去;句子由我看完上下文再写,写不出来就不硬回。

完整的功能来源、工具清单、权限边界和配置说明见 功能说明;三仓库的逐项取舍见 整合对照。

致谢 / 借鉴

这套东西不是从零造的,上传那半边的选择器和踩坑结论大量来自这两个项目,建议一起看:

  • dreammis/social-auto-upload —— 抖音/小红书/视频号/B站/YouTube 的自动投稿。本仓库的创作者中心流程参考了它的 uploader/douyin_uploader/main.py:上传完成判定(重新上传)、标题/简介选择器、自主声明弹窗(semi-radio + 内容由AI生成)、清浮层(.shepherd-element / mention-wrapper)、短信验证码兜底、发布成功以跳转 content/manage 为准。
  • NanmiCoder/MediaCrawler —— 各平台评论抓取的 Playwright 方案,读数据那半边的思路(别硬逆签名,让真实浏览器发请求)和它一致。

本仓库和它们的区别:这里把它包成 DSH 插件,加了权限闸、总线程、两步投稿和审批队列,不批量刷、不群发。


License

MIT

관련 플러그인