Files
live-hub-py/docs/HUYA_BROWSER_EXCHANGE.md
T
yml2213 4518b726ac feat(huya): 浏览器兑换三目标直达并收窄扩展注入范围
- 操作列改为「直播间 / 精英宝典 / 电竞宝典」三个直达按钮,点哪个就按哪个目标写入登录态并打开,去掉弹窗内选择
- 新增 services/huya_exchange_targets.py 统一三个目标地址,接口支持可选 body {"target": "..."}(不传=精英宝典)
- 扩展内容脚本从 <all_urls> 收窄到后台站点白名单,service worker 校验 sender.tab.url,拒绝其它来源的注入
- 注入改为 Cookie 写完后回执(ok/written/failed/error),前端按回执提示成功或失败,不再静默
- 扩展图标兜底改用 world:'MAIN' 读取;一个 Cookie 都没写成功时不打开目标页
- 兑换材料签发写审计 huya:exchange:kit(含账号与目标)
- 控制台脚本增加 huya.com 域校验与 Secure,与扩展路径保持一致
- 修复兑换记录轮询的跨账号串扰与卸载后 setState;兑换请求加 epoch 防止过期结果覆盖弹窗
- 新增 tests/test_huya_browser_exchange.py(13 项)并同步 docs/HUYA_BROWSER_EXCHANGE.md
2026-09-11 10:31:46 +08:00

98 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 虎牙浏览器兑换(本机浏览器)
人工兑换在操作者自己的 Chrome/Edge 中完成:后端下发目标页地址和登录材料,登录态自动(扩展)或手动(控制台脚本)写入本机浏览器,兑换本身始终由人工点击,服务端不托管任何浏览器会话。
## 入口与账号范围
独立页面:虎牙 → 浏览器兑换。导航按角色区分:客服只看到浏览器兑换一个入口;管理员/运营仍显示完整的虎牙菜单(账号管理、精英宝典等)。
- 页面维护一份「兑换账号列表」,通过「导入账号」从自己可见的账号池勾选加入;客服只能看到分配给自己的账号。
- 列表按用户隔离保存(工作台 scope `browser_exchange`),与精英宝典的账号集合互不影响。
- 账号需已有 Cookie 才能执行兑换;账号的 App 协议登录由管理员在账号管理页(直链 `/huya/accounts`)完成,客服遇到无 Cookie 账号时联系管理员处理。
## 目标页三个直达按钮
操作列直接提供三个目标按钮(直播间 / 精英宝典 / 电竞宝典),点哪个就按哪个目标写入登录态并打开对应页面;接口不传 `target` 时用「精英宝典」(与历史行为一致)。
| key | 名称 | 地址 |
| --- | --- | --- |
| `live_room` | 直播间 | `https://www.huya.com/30596253` |
| `elite` | 精英宝典(默认) | `https://zt.huya.com/b02faae1/pc/index.html?sourceId=yellowcarlist&pid={room_pid}&anchorUid={room_pid}&roomid=30596253` |
| `esports` | 电竞宝典 | `https://zt.huya.com/50ea007f/pc/index.html?sourceId=yellowcarlist&pid={room_pid}&anchorUid={room_pid}&roomid=30596253` |
- `{room_pid}` 取虎牙配置的 `room_pid`;配置为空时用默认 `1199650619883`(`web/backend/huya_defaults.py`)。直播间地址与 `room_pid` 无关。
- 每次点击都会按该目标重新取一次兑换材料(登录脚本里的跳转地址跟着目标变)。
- 新增目标:在 `web/backend/services/huya_exchange_targets.py` 加常量与地址,并在 `web/backend/schemas.py` 的 `HuyaBrowserExchangeRequest.target` 正则里补上 key(`tests/test_huya_browser_exchange.py` 会校验两处不漂移)。
## 一键自动注入(推荐):安装内置扩展
项目自带 Chrome/Edge 扩展 `web/extension/huya-exchange-helper/`,安装一次后即可自动注入:
1. 打开扩展管理页:Chrome 为 `chrome://extensions`,Edge 为 `edge://extensions`。
2. 开启「开发者模式」,点击「加载已解压的扩展程序」,选择仓库目录 `web/extension/huya-exchange-helper`。
3. 刷新后台页面(扩展的内容脚本需要对当前页生效)。
- 已装过旧版扩展时,升级后必须回到 `chrome://extensions` 点一次「刷新」;否则浏览器里跑的还是旧版(旧版会注入所有网站,本次安全收窄不生效)。
之后的流程:
1. 在兑换列表操作列点击目标按钮(直播间 / 精英宝典 / 电竞宝典)→ 后端返回兑换材料 → 扩展自动把该账号 Cookie 写入 `.huya.com` 域并在新标签页打开该页面;若一个 Cookie 都没写成功,扩展**不会**打开目标页(避免打开未登录或上一个账号的页面),弹窗显示失败原因。
2. 弹窗会显示本次注入结果(写入 / 失败条数);失败时按提示处理后点「注入登录态并打开」重试。
3. 确认页面右上角账号无误后,手动完成兑换,回到后台点击「完成」。
扩展只在注入时覆盖与登录态同名的旧 Cookie,其余虎牙 Cookie 不动;扩展图标点击可作为兜底(读取当前**后台页签**留存的材料),未检测到材料时图标显示「无任务」角标。
内容脚本只注入后台站点白名单(`manifest.json` 的 `content_scripts.matches`):生产 `https://live.khhao.com`、开发隧道 `https://www.u499731.nyat.app`、本地 `http://localhost:5174`、`http://127.0.0.1:5174`、`http://localhost:8800`、`http://127.0.0.1:8800`。若后台使用其它域名,需要把该域名加进名单再重新加载扩展——service worker 从这份名单推导允许触发注入的来源,其它站点派发的注入请求会被拒绝。
## 查询兑换记录
兑换列表每行的「兑换记录」按钮会为该账号创建 `query_exchange_records` 精英宝典任务(活动 SID 取精英宝典配置),前端轮询任务完成后:
- 表格新增的「兑换记录」列直接展示奖品名(多条时显示"xxx 等 N 条",悬停可看全部名称),未查询显示"未查询",查询中显示加载图标,失败显示"查询失败"及原因。
- 点击单元格中的奖品名可打开详情弹窗,查看每条记录的奖品、消耗积分、发放状态、兑换时间与订单号。
任务失败(如 SID 未配置、Cookie 失效)时弹窗内直接显示原因,可点击「重新查询」。
## 手动方式(未装扩展时的兜底)
1. 在兑换列表操作列点击目标按钮(直播间 / 精英宝典 / 电竞宝典)→ 未装扩展时目标页会直接在新标签页打开,并弹出「浏览器兑换」窗口给出手动兜底。
2. 在**虎牙域下的页面**按 `F12` 打开控制台,粘贴脚本并回车执行。Chrome/Edge 首次粘贴需先手动输入 `allow pasting` 再回车。脚本会先校验当前页面在 `huya.com` 域下(不满足会弹窗提示并中止),写入带 `Secure`(与扩展路径一致)的 Cookie 后跳转目标页。
3. 确认登录态正确后完成兑换,回到系统点击「完成」。
没有控制台操作习惯时,可安装 Cookie-editor 类扩展,使用「复制 Cookie 字符串」在 `.huya.com` 域下导入。
## 接口
`POST /api/huya/accounts/{account_id}/browser-exchange`(权限 `huya:task`),可选 body `{"target": "live_room" | "elite" | "esports"}`:
```json
{
"account_id": 1,
"username": "xxx",
"target": "elite",
"targets": [
{ "key": "live_room", "label": "直播间", "url": "https://www.huya.com/30596253" },
{ "key": "elite", "label": "精英宝典", "url": "https://zt.huya.com/b02faae1/pc/index.html?..." },
{ "key": "esports", "label": "电竞宝典", "url": "https://zt.huya.com/50ea007f/pc/index.html?..." }
],
"target_url": "https://...",
"cookie_string": "yyuid=...; ...",
"cookie_script": "(function(){...})();"
}
```
- `targets` 固定返回三个可选目标(顺序:直播间、精英宝典、电竞宝典),前端据此渲染选择器;非法 `target` 由 Pydantic 正则拦下(422)。
- 脚本由 `core.huya.cookie_utils.browser_login_script` 生成:先校验 `location.hostname` 属于 `huya.com`,Cookie 以 JSON 数组内联(自动转义),逐条 `document.cookie` 写入 `domain=.huya.com; path=/; secure`,有效期 180 天,最后跳转 `target_url`。
- 扩展注入走同一条接口:前端解析 `cookie_string` 后通过 DOM 事件交给扩展,由 `chrome.cookies` API 写入,并把写入结果回执给页面(前端据此提示成功/失败,不再静默)。
- 兑换账号列表复用 `/api/huya/workbench-accounts`(`handbook_scope=browser_exchange`)。
- 每次签发都会留审计日志:`audit_logs.action = huya:exchange:kit`,`target = huya_account:{id}`,detail 含 `account_id` 与 `target`。
- 服务端不保存任何会话状态。
## 安全说明
- 接口受 `huya:task` 权限与账号可见范围约束;脚本、Cookie 字符串与扩展注入等同账号凭证,仅在授权用户点击时下发,且逐次留痕。
- 登录态只写入操作者自己的浏览器,不会在服务器留存浏览器进程或 Profile。
- 扩展为内部工具,通过开发者模式加载;内容脚本只注入后台站点白名单,service worker 二次校验消息来源站点,且只写 `huya.com` 域的 Cookie。
- 仍存在的边界:注入请求由后台页面用 `CustomEvent` 派发,页面内其它脚本理论上可以监听或伪造同名事件(页面派发的事件 `isTrusted` 恒为 false,无法据此区分真伪);因此不要把不受信任的脚本引入后台页面。
- 扩展写入的 Cookie 非 `HttpOnly`(`chrome.cookies` 限制),虎牙域内脚本可读,这是本方案的固有取舍。
- 请勿把脚本、Cookie 或扩展转给无关人员;如需共享操作,建议使用系统的账号授权机制而非转发凭证。