优化斗鱼绑定状态同步

This commit is contained in:
yml2213
2026-08-02 17:54:36 +08:00
parent c62f52a067
commit a466e11c9c
4 changed files with 200 additions and 108 deletions
+89 -83
View File
@@ -150,6 +150,16 @@ class DouyuBatchRunner:
def _current_bind_act_alias(cls, config: dict) -> str:
return cls._confirm_act_alias(config) or cls._bind_qr_act_alias(config)
@classmethod
def _action_act_aliases(cls, config: dict) -> list[str]:
"""当前活动动作 alias;不包含只用于查询最新扫码态的 legacy/cjm。"""
aliases: list[str] = []
for key in ("confirm_act_alias", "bind_act_alias"):
alias = cls._action_act_alias(config, key)
if alias and alias not in aliases:
aliases.append(alias)
return aliases
@staticmethod
def _role_channel(bind_info: dict) -> str:
return " / ".join(
@@ -194,8 +204,7 @@ class DouyuBatchRunner:
if not bind_info:
return False
role_name = str(bind_info.get("role_name") or "").strip()
is_bound = cls._is_bound_act(bind_info) or bool(role_name)
if not is_bound:
if not role_name or not cls._is_bound_act(bind_info):
return False
return not cls._can_change_role(bind_info)
@@ -768,6 +777,32 @@ class DouyuBatchRunner:
return info
return ordered[0]
def _pick_current_bound_info(
self,
candidates: list[dict],
config: dict,
*,
extra_prefer_aliases: list[str] | None = None,
) -> dict | None:
"""选当前活动已生效绑定,避免把 legacy/cjm 的待确认态当成当前角色。"""
if not candidates:
return None
prefer = []
for alias in [*(extra_prefer_aliases or []), *self._action_act_aliases(config)]:
alias = str(alias or "").strip()
if alias and alias not in prefer:
prefer.append(alias)
bound = [
info
for info in candidates
if self._is_bound_act(info) and str(info.get("role_name") or "").strip()
and str(info.get("act_alias") or "") in prefer
]
if not bound:
return None
return sorted(bound, key=lambda info: prefer.index(str(info.get("act_alias") or "")))[0]
def _pick_baseline_bind_info(
self,
candidates: list[dict],
@@ -781,26 +816,7 @@ class DouyuBatchRunner:
"""
if not candidates:
return None
prefer = []
for key in ("confirm_act_alias", "bind_act_alias", "legacy_act_alias"):
alias = str(config.get(key) or "").strip()
if alias and alias not in prefer:
prefer.append(alias)
# 先找“已绑定且有角色”的活动结果
for alias in prefer:
for info in candidates:
if str(info.get("act_alias") or "") != alias:
continue
if self._is_bound_act(info) and str(info.get("role_name") or "").strip():
return info
# 再退回任意有角色的结果(仍按活动 alias 优先)
return self._pick_bind_info(
candidates,
prefer_pending=False,
prefer_aliases=prefer,
)
return self._pick_current_bound_info(candidates, config)
def _wait_bind_role_result(
self,
@@ -992,19 +1008,21 @@ class DouyuBatchRunner:
self._mark_task(db, task, "failed", "查询绑定信息失败,请检查 Cookie 或 actAlias")
return
# baseline 用活动 alias 的“当前已绑定”;pending 检测优先 cjm 的换绑最新态
before = self._pick_baseline_bind_info(before_candidates, config) or before_candidates[0]
before = self._pick_baseline_bind_info(before_candidates, config) or {}
# 换绑冷却必须看活动当前绑定(QYOOB),不要用 cjm
cooldown_info = self._pick_change_wait_bind_info(before_candidates, config) or before
cooldown_info = self._pick_change_wait_bind_info(before_candidates, config)
pending_before = self._pick_bind_info(
before_candidates,
baseline_role_name=str(before.get("role_name") or ""),
baseline_is_bound_act=self._is_bound_act(before),
prefer_pending=True,
prefer_aliases=query_aliases,
) or before
) or before or before_candidates[0]
before_snapshot = self._bind_snapshot(before)
cooldown_snapshot = self._bind_snapshot(cooldown_info)
current_role_name = before_snapshot["role_name"] or cooldown_snapshot["role_name"]
current_role_name = before_snapshot["role_name"] or (
cooldown_snapshot["role_name"] if cooldown_snapshot["is_bound_act"] else ""
)
wait_time = cooldown_snapshot["change_role_wait_time"]
self._push_log(
"info",
@@ -1118,6 +1136,9 @@ class DouyuBatchRunner:
query_aliases = self._query_bind_act_aliases(config)
if confirm_alias and confirm_alias not in query_aliases:
query_aliases = [confirm_alias, *query_aliases]
if not confirm_alias:
self._mark_task(db, task, "failed", "请先配置确认绑定活动 actAlias")
return
if not query_aliases:
self._mark_task(db, task, "failed", "请先配置绑定活动 actAlias")
return
@@ -1137,13 +1158,10 @@ class DouyuBatchRunner:
role_name = before_snapshot["role_name"]
query_alias = str(before.get("act_alias") or "")
# 确认前“已生效绑定”角色(bound_act=1),确认失败/回查失败时写库用,避免待确认角色污染 game_name
before_bound = next(
(
info
for info in before_candidates
if self._is_bound_act(info) and str(info.get("role_name") or "").strip()
),
None,
before_bound = self._pick_current_bound_info(
before_candidates,
config,
extra_prefer_aliases=[confirm_alias],
)
self._push_log(
"info",
@@ -1169,8 +1187,13 @@ class DouyuBatchRunner:
)
return
if before_snapshot["is_bound_act"]:
self._apply_bind_info_to_account(account, before, "bind_confirmed")
before_is_current_bound = (
before_bound is not None
and str(before.get("act_alias") or "") == str(before_bound.get("act_alias") or "")
and role_name == str(before_bound.get("role_name") or "")
)
if before_is_current_bound:
self._apply_bind_info_to_account(account, before_bound, "bind_confirmed")
self._mark_task(
db,
task,
@@ -1180,8 +1203,8 @@ class DouyuBatchRunner:
"act_alias": confirm_alias or query_alias,
"query_act_alias": query_alias,
"query_act_aliases": query_aliases,
"before_bind_info": before,
**before_snapshot,
"before_bind_info": before_bound,
**self._bind_snapshot(before_bound),
"bind_ready_for_confirm": True,
"bind_confirmed": True,
"bind_phase": "confirmed",
@@ -1197,7 +1220,11 @@ class DouyuBatchRunner:
confirm_msg = str(exc)
self._push_log("warning", f"确认绑定接口失败: {confirm_msg}")
# 待绑定游戏账号侧换绑限制(未到换绑时间等)导致确认失败:保留原绑定并给出明确提示
self._apply_bind_info_to_account(account, before_bound or before, "game_queried")
if before_bound is not None:
self._apply_bind_info_to_account(account, before_bound, "game_queried")
else:
account.bind_status = "game_queried"
account.updated_at = datetime.now(timezone.utc)
self._mark_task(
db,
task,
@@ -1223,18 +1250,11 @@ class DouyuBatchRunner:
f"error={confirm_raw.get('error')} msg={confirm_raw.get('msg') or '-'}",
)
def _pick_bound_after(candidates: list[dict]) -> dict | None:
"""确认后回查:只认已生效绑定(bound_act=1)。
cjm 的角色名恒有且 bound_act 恒为 0,若按 alias 顺序挑选会永远
判定“确认未生效”,必须按 bound_act=1 判定确认结果。
"""
return next(
(
info
for info in candidates
if self._is_bound_act(info) and str(info.get("role_name") or "").strip()
),
None,
"""确认后回查:只认活动 alias 上的已生效绑定(bound_act=1)。"""
return self._pick_current_bound_info(
candidates,
config,
extra_prefer_aliases=[use_confirm_alias],
)
try:
@@ -1255,7 +1275,11 @@ class DouyuBatchRunner:
break
if after is None:
# 已生效绑定始终未出现:确认未生效(或同步延迟超时),保留原绑定
self._apply_bind_info_to_account(account, before_bound or before, "game_queried")
if before_bound is not None:
self._apply_bind_info_to_account(account, before_bound, "game_queried")
else:
account.bind_status = "game_queried"
account.updated_at = datetime.now(timezone.utc)
self._mark_task(
db,
task,
@@ -1284,7 +1308,11 @@ class DouyuBatchRunner:
except DouyuActivityError as exc:
# 查询接口异常:确认接口已成功时按成功处理,但保留错误信息。
# 写库优先确认前已生效绑定,避免待确认角色被误写入。
self._apply_bind_info_to_account(account, before_bound or before, "bind_confirmed")
if before_bound is not None:
self._apply_bind_info_to_account(account, before_bound, "bind_confirmed")
else:
account.bind_status = "bind_confirmed"
account.updated_at = datetime.now(timezone.utc)
self._mark_task(
db,
task,
@@ -2006,14 +2034,7 @@ class DouyuBatchRunner:
# 1) 优先“已生效绑定”角色(bound_act=1 且有角色名,通常是活动 alias)。
# 避免把扫码后未确认的新角色当成当前绑定结果。
bound_info = next(
(
info
for info in candidates
if self._is_bound_act(info) and str(info.get("role_name") or "").strip()
),
None,
)
bound_info = self._pick_current_bound_info(candidates, config)
# 2) 待确认角色(cjm 扫码后未确认;无已绑定时也用于首次绑定展示)
pending_info = self._pick_bind_info(
candidates,
@@ -2089,30 +2110,15 @@ class DouyuBatchRunner:
"""换绑倒计时优先看活动当前绑定(QYOOB),不是 cjm 换绑最新态。"""
if not candidates:
return None
# 1) 优先活动 alias 上有角色的结果
baseline = self._pick_baseline_bind_info(candidates, config)
if baseline is not None:
wait = self._to_int(baseline.get("change_role_wait_time"))
if wait is not None:
return baseline
# 2) 任意带 wait_time 的结果里取 wait 最大的
with_wait = []
for info in candidates:
wait = self._to_int(info.get("change_role_wait_time"))
if wait is not None:
with_wait.append((wait, info))
if with_wait:
with_wait.sort(key=lambda item: item[0], reverse=True)
return with_wait[0][1]
# 3) 回退 baseline / 首个有角色
return baseline or self._pick_bind_info(
candidates,
# 冷却是“当前已生效绑定”的属性,不能用 legacy/cjm 的待确认角色判断。
current_bound = self._pick_current_bound_info(candidates, config)
if current_bound is not None:
return current_bound
action_aliases = self._action_act_aliases(config)
return self._pick_bind_info(
[info for info in candidates if str(info.get("act_alias") or "") in action_aliases],
prefer_pending=False,
prefer_aliases=[
str(config.get("confirm_act_alias") or "").strip(),
str(config.get("bind_act_alias") or "").strip(),
str(config.get("legacy_act_alias") or "").strip(),
],
prefer_aliases=action_aliases,
)
def _execute_query_change_bind_time(self, db: Session, task: DouyuTask, account: Account, cookie: str, config: dict):