第三方弹幕接入

第三方自己的直播间/聊天系统里的弹幕,怎么让 AI 主播看见、回应,再把回应拿回去显示。

0. 先对号入座

你的情况用哪条路你会得到什么
A. 只有聊天/IM(App 内聊天、客服群)POST /v1/anchors/{id}/ask纯文本回答,你自己渲染(不发声)
B. 有直播间和弹幕(B 站/抖音/自有直播)弹幕推进来 + 听我们房间的声音主播回应你们的观众;回复文本用 danmaku.replied 取回
C. 观众就在我们页面不用接,现有弹幕通道照常
用哪把钥匙:弹幕注入需要 danmaku:ingest。推荐用运营在后台「API 接入」里给这个主播生成的 专用 Key(只能操作这一个主播,泄露也只影响这一个);平台直推则用 ingest 源自己的 secret, 连主 Key 都不用给。另外该主播的 API 开关必须开着,否则会返回 403 api_disabled。

1. 三条上行通道

① 单条注入(事件驱动,最简单)

curl -X POST https://szr.dzhan.cc/v1/anchors/a_1f2c3d4e/danmaku \
  -H "Authorization: Bearer vt_live_…" -H 'Content-Type: application/json' \
  -H "Idempotency-Key: bili-887766" \
  -d '{
    "source": "bilibili",
    "source_msg_id": "887766",
    "user": {"id": "u_1", "name": "观众A", "level": 12},
    "text": "主播这个功能怎么用?",
    "kind": "chat",
    "ts": 1730000000
  }'

→ {"ok":true,"accepted":1,"results":[{"source_msg_id":"887766","status":"accepted"}],"note":"是否回应由门控判定"}

kind:chat|gift|follow|enter。礼物/关注优先级最高(会绕过判定直接答谢)。

② 批量注入(自己攒批)

curl -X POST https://szr.dzhan.cc/v1/anchors/a_1f2c3d4e/danmaku/batch \
  -H "Authorization: Bearer …" -H 'Content-Type: application/json' \
  -d '{"items":[
    {"source":"bilibili","id":"1","user":"观众A","text":"这个多少钱"},
    {"source":"bilibili","id":"2","user":"观众B","text":"哈哈哈哈"},
    {"source":"bilibili","id":"3","user":"观众C","text":"送了个小心心","kind":"gift"}
  ]}'

限制:一次 ≤ 50 条、≤ 8KB;返回逐条 accepted / duplicate / filtered / dropped 便于对账。

③ 平台直推(推荐给"有回调能力"的直播平台)

不要把主 API Key 填进第三方平台的后台。先建一个 ingest 源,它有自己的 secret:

curl -X POST https://szr.dzhan.cc/v1/anchors/a_1f2c3d4e/ingest-sources \
  -H "Authorization: Bearer …" -H 'Content-Type: application/json' \
  -d '{"platform":"generic",
       "fieldmap":{"items_path":"$.data.list",
                   "text":"$.content",
                   "user.name":"$.nick",
                   "id":"$.msg_id",
                   "kind":"const:chat"}}'
{"ok":true,"id":"ig_9f2c…","ingest_url":"https://szr.dzhan.cc/v1/ingest/ig_9f2c…",
 "secret":"…(只显示一次)","platform":"generic","fieldmap":{…}}

然后把平台回调地址填成 ingest_url,请求体用平台原始 JSON(不用改造)。签名:

X-VTB-Timestamp: 1730000000
X-VTB-Signature: sha256=HMAC_SHA256(secret, "1730000000." + raw_body)

调试时也可以直接带 X-VTB-Secret: <secret>(不建议生产用)。

fieldmap 是"字段路径映射",支持 $.a.b、a.b、const:xxx,不用改代码:

键含义
items_path弹幕数组在 JSON 里的位置(为空则整个 body 当一条)
text弹幕文本
user.name用户名
id平台消息 id(用于去重,强烈建议给)
kind类型;const:chat 表示固定为聊天
ts时间戳(可选)

内置平台预设:generic / bilibili / douyin / vtb,选平台名即可,再用 fieldmap 覆盖个别字段。

2. 内部怎么处理(你会感受到的行为)

去重 → 过滤 → 排优先级 → 门控判定 → 插播/继续播 → 回传事件

「现在弹幕在刷:观众A:这个多少钱、观众B:有优惠吗(共 47 条)」 主播回应的是全场气氛,而不是逐条。送礼和提问会被单独拎出来不参与聚合。

3. 回复怎么拿回来

AI 只有声音和文本,不能直接往你的平台发弹幕。回传靠事件:

事件关键字段用途
danmaku.repliedsource source_msg_id reply_text duration_ms主用:把 AI 这句贴回你的聊天区
speech.finishedkind text duration_ms记录全部播报(含主动讲解、笑话)
speech.startedkind想"一开口就显示气泡"用这个
anchor.started/stoppedroom生命周期
{"event":"danmaku.replied","anchor_id":"a_1f2c3d4e","ts":1793616197.2,
 "data":{"source":"bilibili","source_msg_id":"887766",
         "reply_text":"这位朋友问怎么用,点右上角新建就行,第一次会送你一个体验额度。",
         "duration_ms":7043}}

两种取法:

  1. Webhook(推荐,服务端收):POST /v1/webhooks {"url":"https://your.app/hook","events":["danmaku.replied"]}
  2. SSE(前端收):GET /v1/events?anchor=a_1f2c3d4e&access_token=<key>

也可以给 ingest 源配 reply_webhook:我们会直接把 {source_msg_id, reply_text} POST 到你给的地址。

时序说明:danmaku.replied 在这句话播完时发(文本才稳定)。 想更早显示,用 speech.started 先占位、再用 speech.finished 补全文。

4. 联调清单

检查期望
同一条 source_msg_id 连发 3 次只有 1 次 accepted,其余 duplicate
发一条 "哈哈哈哈"大概率不回应(门控不拦但判定为低价值)
发一条 "送了个小心心" + kind:gift一定被答谢
1 秒灌 200 条不崩、不丢礼物、每 30 秒出现一条聚合摘要
停掉主播再注入409 anchor_not_live(先把主播开起来)
用别人的 anchor_id404(按租户隔离,不暴露存在性)

本地想看点效果可以跑 python scripts/demo_danmaku.py,或用 scripts/webhook_sink.py 起一个接收器看事件。