# dsh-plugin-acfun DeepSeek Harness plugin for **AcFun (A站, acfun.cn)**: search videos, read video metadata, comment threads, danmaku (弹幕), the day/week ranking, playable stream URLs and 动态 (moments); interact with your own account (like, tip bananas, favorite, follow, comment — including moments); and sign in by **scanning a QR code in the Harness Web UI** (Settings → AcFun). This is the AcFun counterpart of the community Bilibili tool plugins. Reads are anonymous and work out of the box; every mutating call is gated behind `allowWriteActions: true` **and** an AcFun login cookie, so installing the bundle never silently gives the model write access to an account. ## Tools | Tool | Kind | What it does | | --- | --- | --- | | `acfun_search` | read | Keyword video search; `sort: "views"` orders by play count (AcFun returns one fixed first page of up to 30). | | `acfun_video` | read | Full metadata for one video: author, channel, tags, stats, publish time, cover. | | `acfun_comments` | read | One page of a video's comment thread, in floor order. | | `acfun_danmaku` | read | One batch of a video's 弹幕, ordered by in-video position, with cursor paging. | | `acfun_rank` | read | Day or week ranking, all channels or one `channelId`. | | `acfun_playurl` | read | m3u8 quality ladder (1080P+ / 1080P / 720P / 540P / 360P). | | `acfun_moment` | read | One 动态 by id: text, topics, images, author, counts, canonical share URL. | | `acfun_moment_comments` | read | One page of a 动态's comment thread (moments have their own comment enum). | | `acfun_topic` | read | 社区圈 topic page (best effort, 15 newest items) — the only public way to discover current moment ids. | | `acfun_interact` | write | Video: `like`, `unlike`, `banana`, `favorite`, `unfavorite`, `follow`, `unfollow`. Moment: `moment_like`, `moment_unlike`, `moment_banana`. | | `acfun_post_comment` | write | Post a top-level comment or reply on a video (`acId`) or a 动态 (`momentId`). | | `acfun_account` | read | Login state, credential source, whether interactions are enabled, and the next sign-in step. | A model-facing skill (`skills/plugin-tool-acfun/SKILL.md`) teaches the tool sequence, the write-safety rules, and the 动态 limitations. ## Install Install the workspace directory as a profile bundle: ``` plugin_manager action=install_bundle target=C:\path\to\dsh-acfun ``` The bundle inserts two Loader rows: `acfun` (the tools, panel API and QR login) and `acfun-skill-filesystem` (which exposes this package's `skills/` directory to the agent). A local directory is linked, so edits to the plugin need a Harness restart to load a fresh module generation. To remove it, use `plugin_manager action=remove_bundle target=dsh-plugin-acfun`. ## QR login and the Web UI panel The bundle ships a Client half (`client.js`) that adds a **Settings → AcFun** page. It shows the current login state, the credential source and whether interaction tools are enabled, renders the QR code, and polls until the phone confirms. Flow (all Host-side, `lib/qr-flow.js`): 1. `GET https://scan.acfun.cn/rest/pc-direct/qr/start?type=WEB_LOGIN` returns a base64 PNG plus `qrLoginToken`, `qrLoginSignature` and `expireTime` (120 s). 2. `scanResult` is a single long-poll held by AcFun until the phone scans or the code expires. On scan it returns a **rotated signature**. 3. `acceptResult` with the rotated signature issues the session cookies; the plugin captures `Set-Cookie`, persists the cookie through the Host credential service (`acfun-session` grant record), and points the provider at it. The panel talks to the Host over `/acfun-panel/*` (same-origin JSON, cross-origin requests rejected). Scanning a QR code is the only step a plugin cannot verify by itself, so the confirm branch is exercised only by a real phone scan. **Restart requirement:** a Client half added to a package that the Host already scanned needs one Harness restart, then a page reload, before **Settings → AcFun** appears. The graph is composed from the live Loader entry set at boot, so a bundle installed into a running page is not in it yet. Detect it with `GET /acfun-panel/state` (`client.path` must be non-null) or `GET /acfun-panel/diagnose`; details and evidence are in [`docs/client-panel.md`](docs/client-panel.md). ## Configuration The row config lives in `cordis.patch.yml`; every key is optional. | Key | Default | Meaning | | --- | --- | --- | | `cookie` | `''` | Full AcFun `Cookie` header value. Prefer the environment variable so the secret does not enter the patch. | | `cookieEnv` | `ACFUN_COOKIE` | Environment variable read when `cookie` is empty. | | `allowWriteActions` | `false` | Master switch for `acfun_interact` and `acfun_post_comment`. | | `requestTimeoutMs` | `30000` | Per-HTTP-request timeout. | | `timeoutMs` | `45000` | Cooperative per-tool-call budget (write calls use at least 60 s). | A cookie from the panel's QR login is stored as a credential record and wins over `cookie`/`ACFUN_COOKIE`; signing out in the panel falls back to the config. Example with the environment variable: ```powershell $env:ACFUN_COOKIE = 'auth_key=...; acPasstoken=...; _did=...' ``` ### Getting the cookie without the panel 1. Sign in to in a desktop browser. 2. Open DevTools → Network, reload any acfun.cn page, and copy the complete `Cookie` request header of the document request (it contains `auth_key`, `acPasstoken`, `_did`, and other session values). 3. Paste it as one line into `cookie` or `ACFUN_COOKIE`. Treat that string like a password: it grants full access to the account. The plugin never logs it. ## Safety model - Reads cannot mutate anything and need no cookie. - Writes require both `allowWriteActions: true` and a cookie. With only one of them the tools return `WRITE_DISABLED` / `LOGIN_REQUIRED` and perform no HTTP write request. - Write tools are declared non-concurrency-safe so the harness will not run two account mutations in parallel. - A CAPTCHA/identity-verification challenge from AcFun (`result` 400001–410999) is surfaced as `CAPTCHA_REQUIRED`; the plugin never tries to solve it. ## Verification status Verified live on 2026-10-27 against acfun.cn (anonymous): - All six read tools — `acfun_search` (including `sort: "views"`), `acfun_video`, `acfun_comments`, `acfun_danmaku`, `acfun_rank`, `acfun_playurl`, `acfun_moment`, `acfun_moment_comments`, `acfun_topic` — return real API data and pass output-schema validation plus rendering (`node scripts/live-check.mjs`). - QR login issuance and the waiting/abort path were verified live against `scan.acfun.cn` (a real ticket and PNG are returned; an unscanned ticket holds the long-poll until the 120 s expiry, then reports "expired"). - All twelve tools register through a mock Cordis context, the panel routes are served on a local HTTP server, and the write guard fails closed (`node scripts/self-check.mjs`). - Write routes exist and reject anonymous requests with `{"result":-401}`. The authenticated happy path — including the final QR accept step and moment writes — was **not** exercised here because no AcFun account was available. Treat first use as a live test: check the result in the AcFun UI instead of retrying blindly. ## Known limitations - AcFun's pc-direct search endpoint ignores `page`/`pageSize` and always returns its first page (30 entries), so `acfun_search` cannot paginate. `sort` is the only ordering control AcFun honours. - Bangumi (番剧) and article pages are not supported — `acfun_video` reports `UNSUPPORTED_RESOURCE` instead of guessing. - **No 广场 feed API exists.** After ~560 path probes no public square/recommend moment list was found on any host; moment ids can only be discovered through `acfun_topic` (server-rendered, 15 newest items, no pagination) or a share link. The only moment *lists* are login-scoped following feeds. - Publishing, deleting, reposting and voting on a 动态 are app-only behind the signed midground token; the web plugin cannot do them. - Moment like/banana/comment contracts are derived from the site's own store and verified only up to their auth error (`401`), so treat moment writes as experimental. - Danmaku paging beyond the first batch depends on AcFun's opaque `pcursor`; `acfun_danmaku` passes it through as `cursor`/`nextCursor`. - Play URLs are time-limited signed m3u8 links; re-run `acfun_playurl` when one expires. Downloading/merging streams is out of scope. - This plugin only talks to acfun.cn's public web endpoints; it is unofficial and can break when AcFun changes them. ## Development ``` node scripts/self-check.mjs # offline: exports, tool schemas, QR state machine, panel HTTP node scripts/live-check.mjs # online: real reads, QR issuance, schema validation + render ``` No build step, no runtime dependencies. Everything under `lib/` and `client.js` is plain ESM/JavaScript that imports only Node built-ins/globals. The bundle intentionally exports no `Config` schema: a workspace-linked bundle cannot resolve `@deepseek-ai/*` packages, so `lib/config.js` validates the row config and falls back to defaults with warnings instead of using schemastery. Endpoint notes: `docs/acfun-api.md` (videos/comments/danmaku/rank), `docs/acfun-login.md` (QR login), `docs/acfun-moment.md` (广场/动态 research and verdict). ## License MIT — see `LICENSE`.