Skip to content

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 liveDanmaku capability. 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 least ai.agent.inboundQueue.live.minIntervalMs apart (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 in zhin.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: sendMode defaults to direct, so replies go out as danmaku immediately (danmaku is real-time; review-then-send makes no sense). Set sendMode: off to let the Bot listen without speaking.
  • Your own room only: the endpoint connects only to the roomId you 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 ​

  1. The id of your own Bilibili live room (roomId; short or full id)
  2. To let the Bot send danmaku: your SESSDATA and bili_jct cookies in .env (gitignored) as BILIBILI_SESSDATA and BILIBILI_BILI_JCT. Leave them out to listen only
  3. The Agent is enabled (ai.agent): its inboundQueue.live drives the danmaku window

Minimal Configuration ​

yaml
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:

yaml
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 replies

Danmaku 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 ​

FieldValue
conversation{ kind: 'channel', id: <full room id> }
message.idPlatform danmaku id (timestamp + uid + sequence when missing)
sender.idViewer 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 ​

SymptomCause and fix
Log bilibili_live_anonymousNo cookies or expired cookies: listening anonymously, and sends fail with not_logged_in. Refresh the cookies and restart
Connection keeps failing, getDanmuInfo returns -352Risk control blocked the token request. Check the cookies, add buvid3 and restart; if it still fails the API has changed
bilibili live auth rejectedThe auth packet was rejected (expired token or wrong room). Check roomId; reconnects fetch a fresh token
Log bilibili_live_circuit_openSending 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_limitedLess than minSendIntervalSec since the last send; this reply is dropped, not queued