"""登录处理器:处理网站登录流程。""" from __future__ import annotations import logging import time from typing import Any, Optional from jiangchang_skill_core.activity import interruptible_sleep logger = logging.getLogger(__name__) # 登录相关选择器(需要根据实际页面调整) LOGIN_SELECTORS = { "username_input": [ "input[name='username']", "input[placeholder*='用户名']", "input[placeholder*='账号']", "#username", "[data-testid='username']", ], "password_input": [ "input[name='password']", "input[type='password']", "#password", "[data-testid='password']", ], "login_button": [ "button[type='submit']", "button:has-text('登录')", "input[type='submit']", ".login-btn", "[data-testid='login-button']", ], "captcha_input": [ "input[name='captcha']", "input[placeholder*='验证码']", "#captcha", ], "captcha_image": [ ".captcha-image", "img[alt*='验证码']", "#captcha-image", ], } # 登录成功检测 LOGIN_SUCCESS_SELECTORS = [ ".user-info", ".welcome", "[data-testid='user-avatar']", "a[href*='logout']", ".dashboard", ] async def login_if_needed( page: Any, username: str, password: str, wait_sec: int = 120, ) -> bool: """检查登录状态,如需要则执行登录。 Args: page: Playwright页面对象 username: 用户名 password: 密码 wait_sec: 等待人工验证的超时时间 Returns: bool: 登录是否成功 """ # 检查是否已登录 if await _is_logged_in(page): logger.info("already_logged_in url=%s", page.url) return True # 导航到登录页面 if "/login" not in page.url: await page.goto("https://sandbox.jc2009.com/login", wait_until="domcontentloaded") await interruptible_sleep(2.0) # 尝试自动登录 login_success = await _try_auto_login(page, username, password) if login_success: return True # 等待人工登录 return await _wait_for_human_login(page, wait_sec) async def _try_auto_login(page: Any, username: str, password: str) -> bool: """尝试自动登录。""" try: # 查找用户名输入框 username_input = await _find_element(page, LOGIN_SELECTORS["username_input"]) if not username_input: logger.warning("username_input_not_found") return False # 查找密码输入框 password_input = await _find_element(page, LOGIN_SELECTORS["password_input"]) if not password_input: logger.warning("password_input_not_found") return False # 输入用户名(模拟人类输入) await username_input.click() await interruptible_sleep(0.5) await username_input.fill("") await page.keyboard.type(username, delay=100) await interruptible_sleep(0.5) # 输入密码 await password_input.click() await interruptible_sleep(0.5) await password_input.fill("") await page.keyboard.type(password, delay=100) await interruptible_sleep(0.5) # 查找并点击登录按钮 login_button = await _find_element(page, LOGIN_SELECTORS["login_button"]) if not login_button: logger.warning("login_button_not_found") return False await login_button.click() await interruptible_sleep(3.0) # 检查是否登录成功 return await _is_logged_in(page) except Exception as e: logger.error("auto_login_failed error=%s", str(e)) return False async def _wait_for_human_login(page: Any, wait_sec: int) -> bool: """等待人工完成登录。""" print(f"请在浏览器中完成登录,最多等待 {wait_sec} 秒...") deadline = time.monotonic() + wait_sec while time.monotonic() < deadline: if await _is_logged_in(page): print("检测到登录成功") return True # 检查是否有验证码需要处理 captcha_present = await _check_captcha(page) if captcha_present: print("检测到验证码,请人工完成验证...") await interruptible_sleep(2.0) print("登录超时") return False async def _is_logged_in(page: Any) -> bool: """检查是否已登录。""" try: # 检查URL是否包含登录成功标识 if "/login" not in page.url and "/index" in page.url: return True # 检查页面元素 for selector in LOGIN_SUCCESS_SELECTORS: try: element = page.locator(selector) if await element.count() > 0 and await element.is_visible(): return True except Exception: continue return False except Exception: return False async def _check_captcha(page: Any) -> bool: """检查是否有验证码。""" try: for selector in LOGIN_SELECTORS["captcha_input"]: element = page.locator(selector) if await element.count() > 0 and await element.is_visible(): return True for selector in LOGIN_SELECTORS["captcha_image"]: element = page.locator(selector) if await element.count() > 0 and await element.is_visible(): return True return False except Exception: return False async def _find_element(page: Any, selectors: list) -> Optional[Any]: """根据选择器列表查找元素。""" for selector in selectors: try: element = page.locator(selector) if await element.count() > 0 and await element.is_visible(): return element except Exception: continue return None