# 语音弹幕桥(Chrome 扩展) 这是 [`dsh-voice-danmaku`](../README.md) 的**页面发送通道**。它做的事只有一件: > 把插件识别出来的文字,填进你已经打开的那个 B 站直播间页面,然后点一下「发送」。 弹幕请求因此是**页面自己**发出去的 —— wbi 签名、csrftoken 全由 B 站前端计算, 插件里不需要保存任何凭证。 --- ## 装(一次性,约一分钟) 1. Chrome 地址栏输入 `chrome://extensions` 回车; 2. 打开右上角的 **开发者模式**; 3. 点 **加载已解压的扩展程序**,选中这个 `extension` 目录(就是本文件所在的目录); 4. 扩展列表里出现「语音弹幕桥」就成了。 > 为什么不用应用商店:这个扩展只对你自己有意义(它要连你本机的插件), > 上架要走审核、要维护发布流程,收益为零。代价是每次 Chrome 重启后如果 > 提示"要停用开发者模式扩展",选**保留**即可(那个提示对所有未上架扩展都会出现)。 ## 配(两边填一样的) 1. 插件设置页:**设置 → 语音弹幕 → 发送通道**(现在只有这一条通道,所以没有 「发送方式」下拉框了); 2. 页面上会出现「本地桥端口」和「本地桥口令」两个值; 3. 点浏览器工具栏上的扩展图标,把这两个值**原样复制**进去,点「保存并测试连接」; 4. 打开你的 B 站直播间页面,等一两秒,扩展弹窗里应该显示: ``` ✓ 本地桥可达(端口 39217) ✓ 这个浏览器已经在轮询(0 秒前) ✓ 页面就绪:<你的直播间标题> ``` 插件设置页里也会出现一行 **「✓ 页面已就绪」**。到这一步就能用了。 ## 它做了什么、没做什么 装一个能操作你浏览器页面的扩展,你有权知道边界。全部代码就在这个目录里, 一共三个文件,可以自己读: | 文件 | 干什么 | |---|---| | `page-hook.js` | 在页面里包一层 `fetch` / `XMLHttpRequest`,**只读** `/msg/send` 这一个接口的响应,把 `code` 和 `message` 转发出去 | | `content.js` | 找弹幕输入框和「发送」按钮、填字、点击、把结果报回后台 | | `background.js` | 唯一发网络请求的地方,只连 `127.0.0.1` 上的插件桥 | 它**不会**:读取或上传页面内容、cookie、聊天记录;不碰 `/msg/send` 以外的任何接口; 不访问任何外部服务器;不在你不知情的时候发送任何东西 —— 每次发送都必须由你在 游戏里按一下键(或插件里显式触发)才会发生。 它申请的权限只有两项,都是最小集: - `storage` —— 存端口和口令; - `http://127.0.0.1/*` —— 连本机插件,**没有任何外部域名的权限**; - 内容脚本只注入 `https://live.bilibili.com/*`。 `npm run test:extension` 会检查这些约束没有被后来的改动破坏。 ## 出问题时 | 现象 | 原因 | |---|---| | 插件设置页显示「扩展还没连上来」 | 扩展没启用、没有打开直播间页面、或端口/口令两边不一致 | | 显示「那个页面里没找到输入框或发送按钮」 | 打开的不是直播间页面(或没登录),也可能是 B 站改版了 —— 先在页面上手动发一条试试 | | 显示「点了发送但页面没有反应」 | 三种触发方式(click / 完整鼠标序列 / 回车)都没让页面接受。看下面的控制台日志 | | 显示「页面已接受……但没抓到服务端回执」 | 点击生效了,但读回执的钩子没工作(Chrome 低于 111,或页面结构变了) | | 弹幕发出去了但插件说失败 | 扩展没抓到服务端的回执。**先看一眼直播间**再决定要不要重发,别直接重按 | ### 怎么自己看发生了什么 扩展把每一步都打进了**页面控制台**(不是扩展管理页的那个控制台): 1. 在直播间页面按 `F12`,切到 **Console**; 2. 找 `[语音弹幕]` 开头的行,例如: ``` [语音弹幕] 准备发送「这是一条测试」 [语音弹幕] 尝试触发方式:click [语音弹幕] click 没有反应(输入框里的文字还在) [语音弹幕] 尝试触发方式:mouse-sequence [语音弹幕] mouse-sequence 生效(输入框已清空) [语音弹幕] 收到服务端回执: {code: 0, message: '', at: 1759…} ``` 这一段直接说明了**哪种触发方式有效**、**服务端回了什么**。反馈问题时把它贴出来就够了。 `code: 0` 才是成功;其它值会被插件翻译成中文解释(拒绝码表在 `src/channels/bilibili-codes.ts`)。 ### 改版了怎么办 B 站前端改版会让"找元素"失效。这个扩展刻意**不写死 class 名**,而是按语义找: - 输入框:可见、且 `placeholder` 里提到「弹幕」/「说点」/「发言」; - 发送按钮:可见、且文字正好是「发送」。 所以一般的样式改版不影响它。如果连语义都变了(比如按钮文字改了), 改 `content.js` 里的 `looksLikeDanmakuInput()` 和 `findSendButton()` 两个函数即可, 各只有几行。 ## 它是唯一的发送方式 插件里曾经还有一条通道:自己拼 HTTP 请求直发(需要用户粘贴整行 Cookie)。 **那条已经删掉了**,所以现在弹幕只有这一条路: | | 说明 | |---|---| | 凭证 | 插件里**什么都不存** —— 请求由页面发出,cookie 是浏览器自己的 | | 签名 | 页面自己算(`w_rid`/`wts`),B 站改算法也不用跟 | | 出问题看得见 | 直播间页面上直接显示 | | 代价 | 直播间标签页得一直开着;依赖 B 站**前端结构** | 删除的理由有两条,第一条是决定性的:那条实现**没有 `w_rid` wbi 签名**, 而真实浏览器请求带了它 —— 服务端大概率会直接拒绝。第二条:它是唯一需要把 登录凭证存进插件的地方。 频率限制(最小间隔 + 每小时上限)仍然在插件侧执行,跟发送方式无关 —— 风控看的是账号的行为节奏。