161 lines
6.1 KiB
Python
161 lines
6.1 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
检测 BOSS 直聘账号登录状态任务。
|
||
流程:在比特浏览器分组中按名称查找环境 → 打开该浏览器 → 访问 BOSS 聊天页 → 获取 .user-name 文本。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import asyncio
|
||
from typing import Any, Callable, Coroutine, Dict
|
||
|
||
from common.protocol import TaskType
|
||
from worker.tasks.base import BaseTaskHandler
|
||
from worker.bit_browser import BitBrowserAPI
|
||
from worker.browser_control import connect_browser, human_delay
|
||
|
||
|
||
CHAT_INDEX_URL = "https://www.zhipin.com/web/chat/index"
|
||
|
||
|
||
class CheckLoginHandler(BaseTaskHandler):
|
||
"""检测 BOSS 直聘账号是否已登录。"""
|
||
|
||
task_type = TaskType.CHECK_LOGIN.value
|
||
|
||
async def execute(
|
||
self,
|
||
task_id: str,
|
||
params: Dict[str, Any],
|
||
progress_cb: Callable[[str, str], Coroutine],
|
||
) -> Any:
|
||
"""
|
||
params:
|
||
- account_name: str 比特浏览器环境名称(前端传入的 browser_name)
|
||
- account_id: str 比特浏览器窗口 ID(可选,优先级高于 name)
|
||
- bit_api_base: str 比特浏览器 API 地址(可选)
|
||
"""
|
||
account_name = params.get("account_name", "")
|
||
account_id = params.get("account_id", "")
|
||
bit_api_base = params.get("bit_api_base", "http://127.0.0.1:54345")
|
||
cancel_event = params.get("_cancel_event")
|
||
|
||
self.ensure_not_cancelled(cancel_event)
|
||
await progress_cb(task_id, f"正在比特浏览器中查找环境: {account_name}...")
|
||
|
||
result = await asyncio.get_event_loop().run_in_executor(
|
||
None,
|
||
self._run_sync,
|
||
account_name,
|
||
account_id,
|
||
bit_api_base,
|
||
cancel_event,
|
||
)
|
||
|
||
self.ensure_not_cancelled(cancel_event)
|
||
status_text = "已登录" if result["is_logged_in"] else "未登录"
|
||
await progress_cb(task_id, f"检测完成: {result['browser_name']} → {status_text} ({result['boss_username']})")
|
||
return result
|
||
|
||
def _run_sync(
|
||
self,
|
||
account_name: str,
|
||
account_id: str,
|
||
bit_api_base: str,
|
||
cancel_event,
|
||
) -> dict:
|
||
"""同步执行(线程池中运行)。"""
|
||
self.ensure_not_cancelled(cancel_event)
|
||
bit_api = BitBrowserAPI(bit_api_base)
|
||
|
||
# ── 1. 在比特浏览器分组中查找匹配的环境 ──
|
||
browser_id = account_id
|
||
browser_name = account_name
|
||
|
||
if not browser_id and account_name:
|
||
self.logger.info("正在比特浏览器中查找环境: %s", account_name)
|
||
# 获取所有浏览器窗口,按名称匹配
|
||
all_browsers = bit_api.list_browsers(page_size=200)
|
||
matched = None
|
||
|
||
# 精确匹配
|
||
for item in all_browsers:
|
||
if item.get("name", "").strip() == account_name.strip():
|
||
matched = item
|
||
break
|
||
|
||
# 未精确匹配到则模糊匹配
|
||
if not matched:
|
||
for item in all_browsers:
|
||
name = item.get("name", "").strip().lower()
|
||
if account_name.strip().lower() in name:
|
||
matched = item
|
||
break
|
||
|
||
if not matched:
|
||
raise RuntimeError(
|
||
f"在比特浏览器中未找到名为 '{account_name}' 的环境。"
|
||
f"当前共 {len(all_browsers)} 个环境,"
|
||
f"名称列表: {[b.get('name', '') for b in all_browsers[:10]]}..."
|
||
)
|
||
|
||
browser_id = matched.get("id", "")
|
||
browser_name = matched.get("name", account_name)
|
||
self.logger.info(
|
||
"找到匹配环境: name=%s, id=%s, remark=%s",
|
||
browser_name, browser_id, matched.get("remark", ""),
|
||
)
|
||
|
||
if not browser_id:
|
||
raise RuntimeError("未指定 account_name 或 account_id,无法确定浏览器环境")
|
||
|
||
self.ensure_not_cancelled(cancel_event)
|
||
# ── 2. 打开该浏览器 ──
|
||
cdp_addr, port, browser_id = bit_api.open_browser(browser_id=browser_id)
|
||
self.logger.info("已打开浏览器 %s (%s), CDP: %s", browser_name, browser_id, cdp_addr)
|
||
|
||
# ── 3. 连接浏览器 ──
|
||
browser = connect_browser(port=port)
|
||
tab = browser.latest_tab
|
||
|
||
# ── 4. 访问 BOSS 直聘聊天页 ──
|
||
tab.get(CHAT_INDEX_URL)
|
||
tab.wait.load_start()
|
||
human_delay(3.0, 5.0)
|
||
self.ensure_not_cancelled(cancel_event)
|
||
|
||
# ── 5. 查找 .user-name 元素,判断是否已登录 ──
|
||
boss_username = ""
|
||
boss_id = ""
|
||
is_logged_in = False
|
||
|
||
try:
|
||
user_name_ele = tab.ele("css:.user-name", timeout=10)
|
||
if user_name_ele and user_name_ele.text:
|
||
boss_username = user_name_ele.text.strip()
|
||
is_logged_in = bool(boss_username)
|
||
self.logger.info("环境 %s 已登录: %s", browser_name, boss_username)
|
||
|
||
# 提取 BOSS 直聘用户 ID(window._PAGE?.uid)
|
||
try:
|
||
uid_result = tab.run_js(
|
||
"(function(){try{var p=window._PAGE;if(p&&p.uid)return String(p.uid);}catch(e){}return '';})()"
|
||
)
|
||
if uid_result is not None and str(uid_result).strip():
|
||
boss_id = str(uid_result).strip()
|
||
self.logger.info("环境 %s boss_id=%s", browser_name, boss_id)
|
||
except Exception as js_err:
|
||
self.logger.debug("提取 boss_id 失败: %s", js_err)
|
||
else:
|
||
self.logger.info("环境 %s 未检测到 .user-name,账号未登录", browser_name)
|
||
except Exception as e:
|
||
self.logger.warning("环境 %s 查找 .user-name 失败: %s", browser_name, e)
|
||
|
||
self.ensure_not_cancelled(cancel_event)
|
||
return {
|
||
"browser_id": browser_id,
|
||
"browser_name": browser_name,
|
||
"boss_username": boss_username,
|
||
"boss_id": boss_id,
|
||
"is_logged_in": is_logged_in,
|
||
}
|