Documentation Sync
This page mirrors plugins/adapters/bilibili-live/README.md. Please edit the in-package README and then run pnpm sync:adapter-docs.
@zhin.js/adapter-bilibili-live
Experimental — built on Bilibili Live's unofficial web APIs (danmaku WebSocket,
msg/send). It may break whenever the platform changes. It is not part of the stable smoke tests, and its interface and config may change incompatibly.
A Bilibili live danmaku adapter for Zhin.js (Plugin Runtime). The Bot watches danmaku in your own live room and picks one to answer, like a streamer would. Design: ADR 0038: Continuous input merge §5. Discussion: #1049.
Read this first
- One reply per window: the adapter declares the
liveDanmakucapability. The Agent does not answer each danmaku. It collects danmaku in a window, and the model picks one (or summarizes them) and replies once. Two replies are at leastai.agent.inboundQueue.live.minIntervalMsapart (15 seconds by default). - Danmaku turns are read-only: they run with anonymous-viewer access and only see tools that declare
readOnly: true. They never show an approval card and never run shell, MCP or sub-agents. Muting and blocking do not go through danmaku. - Danmaku is posted as your account: sending needs your login cookies (
SESSDATA+bili_jct). Treat them like your password: inject them with environment variables, never write them in plain text inzhin.config.yml, never commit them, and never paste them into logs or issues. Without cookies the Bot can only listen anonymously (the platform masks nicknames and reports uid 0) and cannot send. - Direct send by default:
sendModedefaults todirect, so replies go out as danmaku immediately (danmaku is real-time; review-then-send makes no sense). SetsendMode: offto let the Bot listen without speaking. - Your own room only: the endpoint connects only to the
roomIdyou configure and only posts there. Follow Bilibili's user agreement; marketing, traffic farming and spam are forbidden. - Risk control trips a breaker: on rate-limit (
10031) or risk-control error codes, the endpoint stops sending and logs the error. It does not retry automatically; check the account and restart.
Prerequisites
- The id of your own Bilibili live room (
roomId; short or full id) - To let the Bot send danmaku: your
SESSDATAandbili_jctcookies in.env(gitignored) asBILIBILI_SESSDATAandBILIBILI_BILI_JCT. Leave them out to listen only - The Agent is enabled (
ai.agent): itsinboundQueue.livedrives the danmaku window
Minimal Configuration
configVersion: 2
plugins:
bilibili-live: "@zhin.js/adapter-bilibili-live"
bilibili-live:
sendMode: direct # default; off = listen only
maxLength: 20 # characters per danmaku; higher live levels allow more
maxChunks: 2 # danmaku per reply at most
minSendIntervalSec: 5 # minimum gap between two sends (the Agent adds its own 15 s window gap)
endpoints:
- id: my-live
roomId: 123456 # your own live room only
sessdata: ${BILIBILI_SESSDATA}
biliJct: ${BILIBILI_BILI_JCT}Required fields (endpoints[i]): id, roomId.
Danmaku window
The window is configured on the Agent, not on this adapter:
ai:
agent:
inboundQueue:
live:
mode: pick-one # pick-one: answer one; summary: summarize the window and answer once
windowMs: 10000 # how long after the first danmaku the window closes
minIntervalMs: 15000 # minimum gap between two danmaku repliesDanmaku from a master skips the window and gets its own turn. The audit code danmaku_window_picked records the candidate count and the picked item.
Conversations and messages
| Field | Value |
|---|---|
conversation | { kind: 'channel', id: <full room id> } |
message.id | Platform danmaku id (timestamp + uid + sequence when missing) |
sender.id | Viewer uid; anon:<masked nickname> when listening anonymously |
metadata | { roomId, liveDanmaku: true } |
Outbound is plain text only: Markdown is stripped and images and other segments are unsupported. The first mention in a reply is not written into the text; it becomes the platform's "reply to" field, so the picked viewer sees it.
Disk usage
The adapter writes nothing to disk. The dedupe table (the latest 500 danmaku ids) and connection state live in memory and are cleared on restart.
Known limitations
- Without login the platform reports uid 0 and masked nicknames, so anonymous viewers with the same masked nickname count as one person.
- The platform may change its APIs at any time (for example WBI signing or risk control on
getDanmuInfo). The connection then keeps failing and backing off; watch the logs.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
Log bilibili_live_anonymous | No cookies or expired cookies: listening anonymously, and sends fail with not_logged_in. Refresh the cookies and restart |
Connection keeps failing, getDanmuInfo returns -352 | Risk control blocked the token request. Check the cookies, add buvid3 and restart; if it still fails the API has changed |
bilibili live auth rejected | The auth packet was rejected (expired token or wrong room). Check roomId; reconnects fetch a fresh token |
Log bilibili_live_circuit_open | Sending hit rate limiting or risk control and has stopped. Raise minSendIntervalSec or inboundQueue.live.minIntervalMs, check the account and restart |
| Reply cut off with "…" | It exceeded maxLength × maxChunks. Accounts with a higher live level can raise maxLength |
Send returns rate_limited | Less than minSendIntervalSec since the last send; this reply is dropped, not queued |