diff --git a/gateway/scripts/dashboard.py b/gateway/scripts/dashboard.py index 3b9384b..efab076 100644 --- a/gateway/scripts/dashboard.py +++ b/gateway/scripts/dashboard.py @@ -565,6 +565,21 @@ PLATFORM_SERVICES = [ ] +def _docker_session_failed(): + """Check docker logs for recent loginCheck errors (WeChat session expired silently).""" + try: + dr = subprocess.run( + ["docker", "logs", "wxBotWebhook", "--since", "15m"], + capture_output=True, text=True, timeout=5 + ) + logs = dr.stdout + dr.stderr + err_lines = [l for l in logs.split("\n") if "loginCheck" in l and "Error" in l] + return len(err_lines) >= 3 + except Exception: + return False + return False + + def _docker_login_check(): """Check docker logs for recent WeChat login. Returns datetime or None.""" try: @@ -648,6 +663,28 @@ def _augment_wechat_status(entry): if last_logout: logout_dt = datetime.strptime(last_logout, "%Y-%m-%d %H:%M:%S") + # ═══ 新增: 检测"正在等待扫码"状态 ═══ + # 如果 docker 日志最近显示二维码, 说明未登录等待扫码, 优先判定 + try: + _dr = subprocess.run( + ["docker", "logs", "wxBotWebhook", "--since", "5m"], + capture_output=True, text=True, timeout=5 + ) + _dlogs = _dr.stdout + _dr.stderr + _showing_qr = ("扫描以下二维码" in _dlogs) or ("Access the URL to login" in _dlogs) + _login_err = _dlogs.count("loginCheck") >= 2 + except Exception: + _showing_qr = False + _login_err = False + if _showing_qr or _login_err: + entry["login_ok"] = False + entry["status"] = "logged_out" + entry["message"] = "等待扫码登录" if _showing_qr else "会话失效(loginCheck错误)" + entry["qr_url"] = entry.get("qr_url") or "" + _done_flag = True + else: + _done_flag = False + # Check docker logs for recent login (catches fresh logins before webhook sees them) # Check webhook log for login event login_dt = None @@ -657,8 +694,15 @@ def _augment_wechat_status(entry): except ValueError: pass dlogin_dt = _docker_login_check() + # Session-failed: loginCheck errors = WeChat session expired silently + if _docker_session_failed(): + entry["login_ok"] = False + entry["message"] = "会话失效(loginCheck错误)" + entry["status"] = "logged_out" # Logout is the most authoritative signal: if logout happened after the last login, session is dead - if logout_dt and login_dt and logout_dt > login_dt: + if _done_flag: + pass + elif logout_dt and login_dt and logout_dt > login_dt: entry["login_ok"] = False h = round((now - logout_dt).total_seconds() / 3600, 1) entry["message"] = "已掉线" if h < 1 else f"已掉线 {h:.0f}h" @@ -1770,6 +1814,27 @@ def api_wechat_status(): except Exception as e: result["message"] = f"QR page fetch failed: {e}" + # 3a. 优先检测: docker 日志是否正在显示登录二维码 (未登录等待扫码) + try: + _dr = subprocess.run( + ["docker", "logs", "wxBotWebhook", "--since", "5m"], + capture_output=True, text=True, timeout=5 + ) + _dlogs = _dr.stdout + _dr.stderr + _showing_qr = ("扫描以下二维码" in _dlogs) or ("Access the URL to login" in _dlogs) + _login_err = _dlogs.count("loginCheck") >= 2 + except Exception: + _showing_qr = False + _login_err = False + if _showing_qr: + result["online"] = False + result["message"] = "等待扫码登录" + return jsonify(result) + if _login_err: + result["online"] = False + result["message"] = "会话失效(loginCheck错误)" + return jsonify(result) + # 3. Check login status from webhook log (last WeChat send) webhook_log = str(_GATEWAY_DIR / "linux" / "logs" / "webhook.log") try: @@ -1821,6 +1886,10 @@ def api_wechat_status(): # Check docker logs for recent login dlogin_dt = _docker_login_check() + # Session-failed: loginCheck errors = WeChat session expired silently + if _docker_session_failed(): + result["online"] = False + result["message"] = "会话失效(loginCheck错误)" # Check webhook login event login_dt = None if result.get("last_login_at"):