193 lines
6.0 KiB
Python
193 lines
6.0 KiB
Python
"""调用 jiangchang_desktop_wake(只调不复制注入逻辑)。
|
||
|
||
- 主窗 WeWorkWindow → ``ensure_ui_ready("wecom")``
|
||
- 搜索浮层 SearchResultWindow2 → ``ensure_ui_ready("wecom_search")``
|
||
未安装 kit 时降级为跳过,不阻断采集。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import time
|
||
from typing import Any, Optional
|
||
|
||
_CACHE_TTL_SEC = 45.0
|
||
|
||
# profile -> cache
|
||
_caches: dict[str, dict[str, Any]] = {}
|
||
|
||
_PROFILE_WECOM = "wecom"
|
||
_PROFILE_WECOM_SEARCH = "wecom_search"
|
||
|
||
_THRESHOLDS = {
|
||
_PROFILE_WECOM: 80,
|
||
_PROFILE_WECOM_SEARCH: 20,
|
||
}
|
||
|
||
|
||
def resolve_wake_profile(*, class_name: str = "", title: str = "") -> Optional[str]:
|
||
"""返回应调用的 kit profile id;非企微相关则 None。"""
|
||
cls = (class_name or "").strip()
|
||
title = title or ""
|
||
if cls == "SearchResultWindow2":
|
||
return _PROFILE_WECOM_SEARCH
|
||
if cls == "WeWorkWindow":
|
||
return _PROFILE_WECOM
|
||
if "WeWork" in cls and ("企业微信" in title or "WeCom" in title or "wecom" in title.lower()):
|
||
return _PROFILE_WECOM
|
||
return None
|
||
|
||
|
||
def looks_like_wecom(*, class_name: str = "", title: str = "") -> bool:
|
||
return resolve_wake_profile(class_name=class_name, title=title) is not None
|
||
|
||
|
||
def kit_available() -> bool:
|
||
try:
|
||
from jiangchang_desktop_wake import ensure_ui_ready # noqa: F401
|
||
|
||
return True
|
||
except ImportError:
|
||
return False
|
||
|
||
|
||
def auto_ensure_if_wecom(
|
||
*,
|
||
class_name: str = "",
|
||
title: str = "",
|
||
hwnd: Optional[int] = None,
|
||
force: bool = False,
|
||
findall_hint: Optional[int] = None,
|
||
) -> dict[str, Any]:
|
||
"""若目标像企微主窗/搜索浮层则静默 ensure;否则跳过。"""
|
||
profile = resolve_wake_profile(class_name=class_name, title=title)
|
||
if profile is None:
|
||
return {
|
||
"applicable": False,
|
||
"skipped": True,
|
||
"reason": "not_wecom",
|
||
"available": kit_available(),
|
||
}
|
||
|
||
warm_min = int(_THRESHOLDS.get(profile, 80))
|
||
cache = _caches.setdefault(
|
||
profile, {"t": 0.0, "warm": False, "hwnd": 0, "findall": 0}
|
||
)
|
||
now = time.monotonic()
|
||
hint = int(findall_hint) if findall_hint is not None else -1
|
||
if (
|
||
not force
|
||
and cache.get("warm")
|
||
and (now - float(cache.get("t") or 0.0)) < _CACHE_TTL_SEC
|
||
and (hint < 0 or hint >= warm_min)
|
||
):
|
||
return {
|
||
"applicable": True,
|
||
"available": True,
|
||
"skipped": True,
|
||
"reason": "cache_warm",
|
||
"ok": True,
|
||
"warm": True,
|
||
"wake_applied": False,
|
||
"findall": int(cache.get("findall") or 0),
|
||
"hwnd": int(cache.get("hwnd") or 0),
|
||
"profile": profile,
|
||
}
|
||
|
||
try:
|
||
from jiangchang_desktop_wake import ensure_ui_ready
|
||
from jiangchang_desktop_wake.errors import DesktopWakeError
|
||
except ImportError:
|
||
return {
|
||
"applicable": True,
|
||
"available": False,
|
||
"skipped": True,
|
||
"reason": "kit_not_installed",
|
||
"ok": False,
|
||
"profile": profile,
|
||
"hint": "请安装 jiangchang-platform-kit>=1.2.6(含 wecom_search)后再采企微",
|
||
}
|
||
|
||
t0 = time.perf_counter()
|
||
try:
|
||
result = ensure_ui_ready(
|
||
profile,
|
||
hwnd=int(hwnd) if hwnd else None,
|
||
raise_on_cold=True,
|
||
)
|
||
except DesktopWakeError as exc:
|
||
raw = (getattr(exc, "code", None) or "").strip()
|
||
code = raw[6:] if raw.startswith("ERROR:") else (raw or "WAKE_FAILED")
|
||
return {
|
||
"applicable": True,
|
||
"available": True,
|
||
"skipped": False,
|
||
"ok": False,
|
||
"warm": False,
|
||
"wake_applied": False,
|
||
"error_code": code,
|
||
"message": str(getattr(exc, "message", None) or exc),
|
||
"elapsed_ms": int(round((time.perf_counter() - t0) * 1000)),
|
||
"profile": profile,
|
||
}
|
||
except Exception as exc:
|
||
return {
|
||
"applicable": True,
|
||
"available": True,
|
||
"skipped": False,
|
||
"ok": False,
|
||
"warm": False,
|
||
"error_code": "ENSURE_EXCEPTION",
|
||
"message": f"{type(exc).__name__}: {exc}",
|
||
"elapsed_ms": int(round((time.perf_counter() - t0) * 1000)),
|
||
"profile": profile,
|
||
}
|
||
|
||
after = result.probe_after
|
||
findall = int(after.findall_count) if after is not None else 0
|
||
report = {
|
||
"applicable": True,
|
||
"available": True,
|
||
"skipped": False,
|
||
"ok": bool(result.warm),
|
||
"warm": bool(result.warm),
|
||
"wake_applied": bool(result.wake_applied),
|
||
"findall": findall,
|
||
"hwnd": int(result.hwnd) if result.hwnd else 0,
|
||
"elapsed_ms": int(round((time.perf_counter() - t0) * 1000)),
|
||
"profile": profile,
|
||
"markers_found": list(after.markers_found) if after is not None else [],
|
||
}
|
||
if result.warm:
|
||
cache["t"] = now
|
||
cache["warm"] = True
|
||
cache["hwnd"] = report["hwnd"]
|
||
cache["findall"] = findall
|
||
else:
|
||
cache["warm"] = False
|
||
return report
|
||
|
||
|
||
def format_kit_wake_line(report: Optional[dict[str, Any]]) -> str:
|
||
if not report:
|
||
return "kit_wake: (none)"
|
||
if not report.get("applicable"):
|
||
return "kit_wake: n/a (非企微)"
|
||
if report.get("reason") == "kit_not_installed":
|
||
return "kit_wake: 未安装 platform-kit(企微可能冷树)"
|
||
prof = report.get("profile") or "?"
|
||
if report.get("skipped") and report.get("reason") == "cache_warm":
|
||
return (
|
||
f"kit_wake[{prof}]: cache warm=True findall={report.get('findall')} "
|
||
f"hwnd={report.get('hwnd')}"
|
||
)
|
||
if report.get("ok"):
|
||
return (
|
||
f"kit_wake[{prof}]: warm={report.get('warm')} "
|
||
f"wake_applied={report.get('wake_applied')} "
|
||
f"findall={report.get('findall')} elapsed_ms={report.get('elapsed_ms')}"
|
||
)
|
||
return (
|
||
f"kit_wake[{prof}]: FAILED code={report.get('error_code')} "
|
||
f"msg={report.get('message')}"
|
||
)
|