- 操作列改为「直播间 / 精英宝典 / 电竞宝典」三个直达按钮,点哪个就按哪个目标写入登录态并打开,去掉弹窗内选择
- 新增 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
98 lines
8.2 KiB
Markdown
98 lines
8.2 KiB
Markdown
# 虎牙浏览器兑换(本机浏览器)
|
||
|
||
人工兑换在操作者自己的 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 或扩展转给无关人员;如需共享操作,建议使用系统的账号授权机制而非转发凭证。
|