Files
live-hub-py/web/backend/services/huya_exchange_targets.py
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

77 lines
2.7 KiB
Python
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.
"""虎牙浏览器兑换的可选目标页:直播间 / 精英宝典 / 电竞宝典。
人工兑换时把账号登录态写入操作者自己的浏览器,再打开其中一个目标页:
- 直播间:直接进房间,适合在房间内完成的活动操作;
- 精英宝典:沿用绑定/充值活动的 zt 活动页(历史行为);
- 电竞宝典:同一活动的另一个 zt 活动页,仅 uuid 不同。
三个地址都由 pid(`HuyaConfig.room_pid`)与固定活动常量拼出,前端只传目标 key。
"""
from __future__ import annotations
from .huya_runner_bind import HUYA_BIND_ROOM_ID, HUYA_BIND_ZT_UUID
from .huya_runner_core import HUYA_RECHARGE_SOURCE_ID
EXCHANGE_TARGET_LIVE_ROOM = "live_room"
EXCHANGE_TARGET_ELITE = "elite"
EXCHANGE_TARGET_ESPORTS = "esports"
#: 未指定目标时使用的默认页(保持历史行为:精英宝典)
DEFAULT_EXCHANGE_TARGET = EXCHANGE_TARGET_ELITE
#: 精英宝典复用绑定/充值活动 uuid;电竞宝典为独立的 zt 活动 uuid
HUYA_ELITE_ZT_UUID = HUYA_BIND_ZT_UUID
HUYA_ESPORTS_ZT_UUID = "50ea007f"
#: 选择器展示顺序
EXCHANGE_TARGET_KEYS: tuple[str, ...] = (
EXCHANGE_TARGET_LIVE_ROOM,
EXCHANGE_TARGET_ELITE,
EXCHANGE_TARGET_ESPORTS,
)
EXCHANGE_TARGET_LABELS: dict[str, str] = {
EXCHANGE_TARGET_LIVE_ROOM: "直播间",
EXCHANGE_TARGET_ELITE: "精英宝典",
EXCHANGE_TARGET_ESPORTS: "电竞宝典",
}
#: 供 Pydantic 校验使用的正则
EXCHANGE_TARGET_PATTERN = "^(live_room|elite|esports)$"
def _zt_exchange_url(zt_uuid: str, room_pid: str) -> str:
"""拼 zt 活动页地址,参数与绑定/充值跳转保持一致。"""
return (
f"https://zt.huya.com/{zt_uuid}/pc/index.html"
f"?sourceId={HUYA_RECHARGE_SOURCE_ID}"
f"&pid={room_pid}"
f"&anchorUid={room_pid}"
f"&roomid={HUYA_BIND_ROOM_ID}"
)
def exchange_target_url(key: str, room_pid: str) -> str:
"""返回指定目标页地址;pid 由调用方(路由)归一化,空值在这里会拼出无 pid 的地址。"""
pid = str(room_pid or "").strip()
if key == EXCHANGE_TARGET_LIVE_ROOM:
return f"https://www.huya.com/{HUYA_BIND_ROOM_ID}"
if key == EXCHANGE_TARGET_ELITE:
return _zt_exchange_url(HUYA_ELITE_ZT_UUID, pid)
if key == EXCHANGE_TARGET_ESPORTS:
return _zt_exchange_url(HUYA_ESPORTS_ZT_UUID, pid)
raise ValueError(f"不支持的兑换目标:{key}")
def exchange_target_options(room_pid: str) -> list[dict[str, str]]:
"""返回三个可选目标页,顺序固定,供前端选择器渲染。"""
return [
{
"key": key,
"label": EXCHANGE_TARGET_LABELS[key],
"url": exchange_target_url(key, room_pid),
}
for key in EXCHANGE_TARGET_KEYS
]