- 操作列改为「直播间 / 精英宝典 / 电竞宝典」三个直达按钮,点哪个就按哪个目标写入登录态并打开,去掉弹窗内选择
- 新增 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
77 lines
2.7 KiB
Python
77 lines
2.7 KiB
Python
"""虎牙浏览器兑换的可选目标页:直播间 / 精英宝典 / 电竞宝典。
|
||
|
||
人工兑换时把账号登录态写入操作者自己的浏览器,再打开其中一个目标页:
|
||
- 直播间:直接进房间,适合在房间内完成的活动操作;
|
||
- 精英宝典:沿用绑定/充值活动的 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
|
||
]
|