修复斗鱼绑定主链路:双 actAlias 分工与任务驱动扫码确认

扫码/确认走活动 alias,角色与换绑最新态优先查 cjm;生成二维码前强制校验换绑冷却,前端以任务结果为单一数据源展示二维码、待确认角色与换绑时间。
This commit is contained in:
yml2213
2026-07-24 22:36:25 +08:00
parent 781ad74762
commit 9471a5b4ff
4 changed files with 1770 additions and 331 deletions
+53 -9
View File
@@ -32,7 +32,7 @@ class DouyuActivityClient:
ACF_CCN_API = "https://www.douyu.com/curl/csrfApi/getCsrfCookie?"
ACF_CCN_FALLBACK_API = "https://www.douyu.com/curl/csrfNlApi/getCsrfCookie"
SUBSCRIBE_API = "https://www.douyu.com/wgapi/livenc/asubscribe/userSubStatus"
ROLE_PARAM_API = "https://www.douyu.com/japi/carnivalApi/tencent/roleParam"
ROLE_PARAM_API = "https://www.douyu.com/japi/carnivalApi/v2/tencent/roleParam"
BIND_INFO_API = "https://www.douyu.com/japi/carnivalApi/tencent/bindInfo"
BIND_INFO_V2_API = "https://www.douyu.com/japi/carnivalApi/v2/tencent/bindInfo"
BIND_API = "https://www.douyu.com/japi/carnivalApi/tencent/bind"
@@ -194,15 +194,14 @@ class DouyuActivityClient:
return ctn
def get_bind_qr(self, act_alias: str) -> dict[str, Any]:
"""生成腾讯游戏绑定二维码链接。"""
"""生成腾讯游戏绑定二维码链接v2 接口)"""
ctn = self.acf_ccn(refresh_subscribe=True)
payload = self._request_json(
"post",
self.ROLE_PARAM_API,
"获取绑定二维码",
data={"actAlias": act_alias, "ctn": ctn},
params={"actAlias": act_alias},
headers={
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Referer": "https://www.douyu.com/",
},
)
@@ -211,6 +210,14 @@ class DouyuActivityClient:
raise DouyuActivityError(payload.get("msg") or "获取绑定二维码失败")
return {"url": url, "ctn": ctn, "raw": payload}
@staticmethod
def _first_present(data: dict[str, Any], *keys: str):
"""按候选 key 取第一个非 None 值。"""
for key in keys:
if key in data and data.get(key) is not None:
return data.get(key)
return None
def bind_info(self, act_alias: str, *, v2: bool = False) -> dict[str, Any]:
"""查询绑定游戏账号信息。"""
url = self.BIND_INFO_V2_API if v2 else self.BIND_INFO_API
@@ -221,19 +228,56 @@ class DouyuActivityClient:
params={"actAlias": act_alias},
headers={"Origin": "", "Referer": "https://www.douyu.com/"},
)
if payload.get("error") not in (0, "0", None):
raise DouyuActivityError(payload.get("msg") or "查询绑定信息失败")
data = payload.get("data") or {}
game_account = data.get("gameAccount") or {}
game_role = data.get("gameRole") or {}
tx_act = data.get("txAct") or {}
bind_account = data.get("bindAccount")
bind_role = data.get("bindRole")
bind_act = data.get("bindAct")
# 换绑倒计时在不同 alias/版本字段可能不一致,尽量兼容。
change_role_wait_time = self._first_present(
data,
"changeRoleWaitTime",
"change_role_wait_time",
"changeBindWaitTime",
"changBindTime",
)
if change_role_wait_time is None and isinstance(tx_act, dict):
change_role_wait_time = self._first_present(
tx_act,
"changeRoleWaitTime",
"changeBindWaitTime",
"changBindTime",
)
can_change_role = self._first_present(
data,
"canChangeRole",
"can_change_role",
"canChangeBind",
)
if can_change_role is None and isinstance(tx_act, dict):
can_change_role = self._first_present(tx_act, "canChangeRole", "canChangeBind")
return {
"bind_account": data.get("bindAccount"),
"bind_role": data.get("bindRole"),
"bind_act": data.get("bindAct"),
"act_alias": act_alias,
"api_version": "v2" if v2 else "v1",
"bind_account": bind_account,
"bind_role": bind_role,
"bind_act": bind_act,
"need_bind_role": data.get("needBindRole"),
"need_bind_act": data.get("needBindAct"),
"is_bound_account": str(bind_account) == "1",
"is_bound_role": str(bind_role) == "1",
"is_bound_act": str(bind_act) == "1",
"nickname": game_account.get("nick") or "",
"role_name": game_role.get("roleName") or "",
"area_name": game_role.get("areaName") or "",
"plat_name": game_role.get("platName") or "",
"change_role_wait_time": data.get("changeRoleWaitTime"),
"can_change_role": data.get("canChangeRole"),
"tx_act": tx_act if isinstance(tx_act, dict) else {},
"change_role_wait_time": change_role_wait_time,
"can_change_role": can_change_role,
"raw": payload,
}