feat: QR login, AcFun settings panel, and 动态 (moment) tools

- scan.acfun.cn QR flow (start -> long-poll scanResult -> acceptResult) with
  rotated-signature handling, background poller, durable session in the Host
  credential service, and cookie handoff to the provider
- Client half: Settings -> AcFun page (status, QR code, poll, logout) over a
  same-origin /acfun-panel JSON API with cross-origin rejection
- acfun_moment / acfun_moment_comments / acfun_topic read tools, moment_like /
  moment_unlike / moment_banana actions, and comment posting on moments
- acfun_account status tool; docs for the QR contract, the 广场/动态 research
  verdict, and the client-composition restart requirement
This commit is contained in:
tamakyi
2026-10-27 11:50:44 +08:00
parent f9bedde19e
commit 97825f020d
19 changed files with 3723 additions and 189 deletions
+79 -47
View File
@@ -1,28 +1,29 @@
# dsh-plugin-acfun
给 **DeepSeek Harness** 用的 **AcFun(A站)** 插件:搜索视频、读取视频详情、
评论、弹幕、日/周排行榜和可播放的 m3u8 地址;只有在你主动开启后,才能用自己
的账号进行互动(点赞、投蕉、收藏、关注、发评论)。
这是社区 B 站工具插件在 A 站上的对应实现。读取类工具默认匿名可用;所有写操作
必须同时满足 `allowWriteActions: true` **和** AcFun 登录 Cookie,因此装好插件
并不会悄悄把账号的写权限交给模型。
评论、弹幕、日/周排行榜、播放地址和**动态(moment)**;可在
**“设置 → AcFun”里扫码登录**;开启写操作后可用自己的账号点赞、投蕉、收藏、
关注、评论(含动态)。
## 工具
| 工具 | 类型 | 作用 |
| --- | --- | --- |
| `acfun_search` | 读 | 关键词搜索视频;`sort: "views"` 按播放量排序(A 站只返回固定第一页,最多 30 条)。 |
| `acfun_search` | 读 | 关键词搜索视频;`sort: "views"` 按播放量(A 站只返回固定第一页 30 条)。 |
| `acfun_video` | 读 | 单个视频完整信息:UP 主、分区、标签、播放/点赞/收藏/投蕉/评论/弹幕数、发布时间、封面。 |
| `acfun_comments` | 读 | 视频评论(按楼层序)的一页。 |
| `acfun_danmaku` | 读 | 视频弹幕一批,按视频内时间排序,支持 `cursor` 翻页。 |
| `acfun_rank` | 读 | 日榜 / 周榜,`channelId: 0` 为全站。 |
| `acfun_playurl` | 读 | m3u8 清晰度列表(1080P+ / 1080P / 720P / 540P / 360P)。 |
| `acfun_interact` | 写 | `like`、`unlike`、`banana`(投蕉)、`favorite`、`unfavorite`、`follow`、`unfollow`。 |
| `acfun_post_comment` | 写 | 发表评论或回复某条评论。 |
| `acfun_moment` | 读 | 按 id 读取一条动态:正文、话题、图片、作者、计数、官方分享链接。 |
| `acfun_moment_comments` | 读 | 动态评论区一页(动态使用独立的评论枚举)。 |
| `acfun_topic` | 读 | 社区圈话题页(尽力解析,最多 15 条)——目前唯一可公开发现动态 id 的入口。 |
| `acfun_interact` | 写 | 视频:`like`、`unlike`、`banana`、`favorite`、`unfavorite`、`follow`、`unfollow`;动态:`moment_like`、`moment_unlike`、`moment_banana`。 |
| `acfun_post_comment` | 写 | 给视频(`acId`)或动态(`momentId`)发表评论或回复。 |
| `acfun_account` | 读 | 登录状态、凭据来源、写操作是否开启、下一步操作提示。 |
插件还附带一个面向模型的技能文件 `skills/plugin-tool-acfun/SKILL.md`,说明工具
调用顺序和写操作的安全规则。
插件还附带面向模型的技能文件 `skills/plugin-tool-acfun/SKILL.md`,说明调用顺序、
写操作安全规则和动态相关限制。
## 安装
@@ -32,11 +33,35 @@
plugin_manager action=install_bundle target=C:\path\to\dsh-acfun
```
插件会向 Loader 插入一行(`id: acfun`)。本地目录是链接安装,修改插件代码后
需要重启 Harness 才能加载新的模块实例。
插件会插入两行:`acfun`(工具、面板接口、扫码登录)与
`acfun-skill-filesystem`(把本包的 `skills/` 暴露给 Agent)。本地目录是链接
安装,修改插件代码后需要重启 Harness 才能加载新的模块实例。
卸载:`plugin_manager action=remove_bundle target=dsh-plugin-acfun`。
## 扫码登录与 Web UI 面板
插件自带 Client 半(`client.js`),在 **设置 → AcFun** 增加一页:显示登录状态、
凭据来源、写操作是否开启,渲染二维码并轮询直到手机确认。
流程全部在 Host 侧(`lib/qr-flow.js`):
1. `GET https://scan.acfun.cn/rest/pc-direct/qr/start?type=WEB_LOGIN` 返回 base64
PNG 以及 `qrLoginToken`、`qrLoginSignature`、`expireTime`(120 秒)。
2. `scanResult` 是 A 站保持的一次长轮询,直到手机扫码或二维码过期;扫码成功时
返回**轮换后的新签名**。
3. 用新签名调用 `acceptResult`,A 站通过 `Set-Cookie` 下发会话;插件捕获 Cookie,
经 Host 凭据服务持久化为 `acfun-session` 记录,并把 provider 切到该会话。
面板通过同源 `/acfun-panel/*` JSON 接口访问 Host,跨源请求会被拒绝。扫码确认这
一步插件无法自行验证,只有真实手机扫码才会走到确认分支。
**需要重启一次:** 如果某个包在 Host 已经扫描过之后才新增 Client 半,则需要重启
一次 Harness,再刷新页面,**设置 → AcFun** 才会出现(Boot 图是在启动时按当时的
Loader 条目组合的)。可用 `GET /acfun-panel/state` 的 `client.path`(必须非空)或
`GET /acfun-panel/diagnose` 判断,细节与证据见
[`docs/client-panel.md`](docs/client-panel.md)。
## 配置
配置位于 `cordis.patch.yml` 的 `acfun` 行,所有键都可省略。
@@ -49,66 +74,73 @@ plugin_manager action=install_bundle target=C:\path\to\dsh-acfun
| `requestTimeoutMs` | `30000` | 单次 HTTP 请求超时。 |
| `timeoutMs` | `45000` | 单次工具调用的协作式预算(写操作至少 60 秒)。 |
使用环境变量的例子:
面板扫码得到的 Cookie 以凭据记录保存,优先于 `cookie`/`ACFUN_COOKIE`;在面板退出
登录后回退到配置值。
```powershell
$env:ACFUN_COOKIE = 'auth_key=...; acPasstoken=...; _did=...'
```
### 获取 Cookie
### 不用面板时手动获取 Cookie
1. 在桌面浏览器登录 <https://www.acfun.cn/>。
2. 打开开发者工具 → Network,刷新任意 acfun.cn 页面,复制文档请求的完整
`Cookie` 请求头(包含 `auth_key`、`acPasstoken`、`_did` 等会话值)。
3. 把整行粘贴到 `cookie` 或 `ACFUN_COOKIE`。
`Cookie` 请求头(包含 `auth_key`、`acPasstoken`、`_did` 等)。
3. 粘贴到 `cookie` 或 `ACFUN_COOKIE`。
这串 Cookie 等同于账号密码,插件不会把它写进日志。
这串 Cookie 等同于账号密码,插件不会写进日志。
## 安全设计
- 读取类工具不会修改任何内容,也不需要 Cookie。
- 写操作必须同时具备 `allowWriteActions: true` 与 Cookie;只满足其一时工具会
返回 `WRITE_DISABLED` / `LOGIN_REQUIRED`,并且不会发出任何写请求。
- 写操作必须同时具备 `allowWriteActions: true` 与登录状态;只满足其一时返回
`WRITE_DISABLED` / `LOGIN_REQUIRED`,不会发出写请求。
- 写工具声明为非并发安全,Harness 不会并行执行两个账号操作。
- AcFun 返回人机验证/身份验证要求(`result` 400001–410999)时,插件以
`CAPTCHA_REQUIRED` 报告,绝不尝试绕过验证码。
- AcFun 返回人机验证要求(`result` 400001–410999)时报告 `CAPTCHA_REQUIRED`,
绝不尝试绕过。
## 验证情况
2026-10-27 对 acfun.cn 实测(匿名):
- 六个读取工具(`acfun_search` 含 `sort: "views"`、`acfun_video`、
`acfun_comments`、`acfun_danmaku`、`acfun_rank`、`acfun_playurl`)都返回真实
数据,并通过输出 schema 校验与渲染(`node scripts/live-check.mjs`)。
- 八个工具通过模拟 Cordis 上下文完成注册,写操作守卫正确失败关闭
- 九个读取工具(`acfun_search` 含 `sort: "views"`、`acfun_video`、
`acfun_comments`、`acfun_danmaku`、`acfun_rank`、`acfun_playurl`、
`acfun_moment`、`acfun_moment_comments`、`acfun_topic`)返回真实数据并通过
输出 schema 校验与渲染(`node scripts/live-check.mjs`)。
- 扫码登录的二维码下发、等待与中止路径已实测(真实 ticket + PNG;未扫码时保持长
轮询到 120 秒过期,然后报告 expired)。
- 十二个工具通过模拟 Cordis 上下文注册,面板接口在本地 HTTP 服务器上验证
(含跨源拒绝、登出),写操作守卫正确失败关闭
(`node scripts/self-check.mjs`)。
- 写接口路由存在,匿名调用返回 `{"result":-401}`;但本次没有 AcFun 账号
Cookie,**未**验证登录后的成功路径。首次使用时请以 A 站页面上的实际结果为准,
不要在结果不明时重复调用。
- 写接口路由存在,匿名调用返回 `{"result":-401}`;但**未**验证登录后的成功路径
(包括扫码最后一步和动态写操作),因为本次没有可用账号。首次使用时请以 A 站
页面上的实际结果为准,不要在结果不明时重复调用。
## 已知限制
- A 站的 pc-direct 搜索接口忽略 `page`/`pageSize`,永远只返回第一页 30 条,
因此 `acfun_search` 无法翻页;排序只有 `sort` 一个有效参数。
- 不支持番剧(bangumi)与文章页面——`acfun_video` 会返回 `UNSUPPORTED_RESOURCE`,
不会瞎猜。
- 弹幕翻页依赖 A 站不透明的 `pcursor`,插件通过 `cursor`/`nextCursor` 透传。
- 播放地址是带签名的临时 m3u8 链接,过期后重新调用 `acfun_playurl`;下载/合并
流媒体不在本插件范围内。
- 插件只调用 acfun.cn 的公开 Web 接口,属于非官方实现,A 站改动接口后可能失效。
- A 站的 pc-direct 搜索接口忽略 `page`/`pageSize`,永远只返回第一页 30 条;只有
`sort` 有效。
- 不支持番剧(bangumi)与文章页面——`acfun_video` 会返回
`UNSUPPORTED_RESOURCE`。
- **没有公开的“广场”列表接口。** 约 560 条路径探测后,各站点都没有公开的
广场/推荐动态列表;动态 id 只能通过 `acfun_topic`(SSR 页面,最多 15 条,无分页)
或分享链接获得,动态列表类接口都需要登录。
- 发布、删除、转发、投票动态只能在 App 内完成(需要签名 token),Web 插件做不了。
- 动态的点赞/投蕉/评论契约来自站点自身代码,只验证到 `401` 未登录,属于实验性
功能。
- 弹幕翻页依赖 A 站不透明的 `pcursor`,通过 `cursor`/`nextCursor` 透传。
- 播放地址是带签名的临时 m3u8,过期需重新调用;下载/合并流媒体不在范围内。
- 插件只调用 acfun.cn 的公开 Web 接口,属于非官方实现,A 站改版后可能失效。
## 开发
```
node scripts/self-check.mjs # 离线:导出、工具 schema、写操作守卫
node scripts/live-check.mjs # 在线:真实读取 + schema 校验 + 渲染
node scripts/self-check.mjs # 离线:导出、工具 schema、扫码状态机、面板 HTTP
node scripts/live-check.mjs # 在线:真实读取、二维码下发、schema 校验与渲染
```
无构建步骤、无运行时依赖。`lib/` 下全部是只依赖 Node 内置能力的纯 ESM。插件
故意不导出 `Config` schema:工作区链接安装的 bundle 无法解析 `@deepseek-ai/*`
包,所以由 `lib/config.js` 校验配置并带告警地回退到默认值。
无构建步骤、无运行时依赖。`lib/` 与 `client.js` 都是只依赖 Node/浏览器内置能力的
纯 JS。插件故意不导出 `Config` schema:工作区链接安装的 bundle 无法解析
`@deepseek-ai/*` 包,所以由 `lib/config.js` 校验配置并带告警地回退默认值。
接口逆向笔记见 [docs/acfun-api.md](docs/acfun-api.md)。
接口笔记:`docs/acfun-api.md`(视频/评论/弹幕/排行)、`docs/acfun-login.md`
(扫码登录)、`docs/acfun-moment.md`(广场/动态调研与结论)。
## 许可证