167 lines
5.3 KiB
Python
167 lines
5.3 KiB
Python
# 返利班浏览器适配。共享协议在 rpa_browser,这里只留 Chrome junction / 端口差异。
|
||
#
|
||
# RPA_USE_USER_CHROME 打开时 profile 走仓库外 daily-chrome junction(不要 resolve),调试口 9222。
|
||
# 未开时 REBATE_* 节点共用查号会话目录和 9335 口。
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
_REPO_ROOT = Path(__file__).resolve().parents[1]
|
||
if str(_REPO_ROOT) not in sys.path:
|
||
sys.path.insert(0, str(_REPO_ROOT))
|
||
|
||
from rpa_browser.adapter import ( # noqa: E402
|
||
ApaBrowserAdapter as _SharedAdapter,
|
||
REPO_ROOT,
|
||
launch_independent_chrome,
|
||
resolve_chrome_profile_base,
|
||
wait_debug_port,
|
||
wait_devtools_port,
|
||
)
|
||
|
||
_USER_CHROME_PORT = 9222
|
||
_FLOW_DEBUG_PORTS = {
|
||
"REBATE_FIND": 9335,
|
||
"default": 9335,
|
||
}
|
||
|
||
__all__ = [
|
||
"ApaBrowserAdapter",
|
||
"REPO_ROOT",
|
||
"chrome_debug_port",
|
||
"chrome_session_flow",
|
||
"resolve_chrome_profile_base",
|
||
"resolve_chrome_profile_dir",
|
||
"use_user_chrome",
|
||
"user_chrome_profile_dir",
|
||
"user_chrome_real_profile_dir",
|
||
"wait_debug_port",
|
||
"wait_devtools_port",
|
||
]
|
||
|
||
|
||
# 是否改用本机日常 Chrome 用户目录(RPA_USE_USER_CHROME)。
|
||
def use_user_chrome() -> bool:
|
||
raw = (os.getenv("RPA_USE_USER_CHROME") or "").strip().lower()
|
||
return raw in {"1", "true", "yes", "on"}
|
||
|
||
|
||
# 本机 Chrome 默认 User Data 目录(Windows LOCALAPPDATA)。
|
||
def user_chrome_real_profile_dir() -> Path:
|
||
return (Path(os.environ.get("LOCALAPPDATA", "")) / "Google" / "Chrome" / "User Data").resolve()
|
||
|
||
|
||
# junction 不存在时用 mklink /J 指向真实 User Data。已存在则不动。非 Windows 只保证父目录。
|
||
def _ensure_user_chrome_junction(link: Path, real: Path) -> None:
|
||
if link.exists():
|
||
return
|
||
link.parent.mkdir(parents=True, exist_ok=True)
|
||
if os.name != "nt":
|
||
return
|
||
subprocess.run(
|
||
["cmd.exe", "/c", "mklink", "/J", str(link), str(real)],
|
||
check=False,
|
||
stdin=subprocess.DEVNULL,
|
||
stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.DEVNULL,
|
||
)
|
||
|
||
|
||
# 日常 Chrome 的调试入口目录。Chrome 禁止在默认 User Data 上开调试口,用仓库外 junction。
|
||
#
|
||
# 返回值不要 resolve,否则又变回默认路径。
|
||
def user_chrome_profile_dir() -> Path:
|
||
link = REPO_ROOT.parent / "rpa-chrome-profile" / "daily-chrome"
|
||
real = user_chrome_real_profile_dir()
|
||
_ensure_user_chrome_junction(link, real)
|
||
return link
|
||
|
||
|
||
# REBATE_* 节点共用查号已登录的 Chrome 会话名。
|
||
def chrome_session_flow(flow: str) -> str:
|
||
name = (flow or "default").strip() or "default"
|
||
if name.startswith("REBATE"):
|
||
return "REBATE_FIND"
|
||
return name
|
||
|
||
|
||
# 本班次 Chrome 用户目录。用户 Chrome 走 junction;否则按查号会话分子目录。
|
||
def resolve_chrome_profile_dir(
|
||
flow: str = "default",
|
||
requested: str | None = None,
|
||
*,
|
||
repo_root: Path | None = None,
|
||
) -> Path:
|
||
if use_user_chrome():
|
||
return user_chrome_profile_dir()
|
||
base = resolve_chrome_profile_base(requested, repo_root=repo_root)
|
||
session = chrome_session_flow(flow)
|
||
safe = "".join(ch if ch.isalnum() or ch in "-_" else "_" for ch in session)
|
||
return (base / (safe or "default")).resolve()
|
||
|
||
|
||
# 本班次调试口。环境变量优先;用户 Chrome 9222;否则查号会话 9335。
|
||
def chrome_debug_port(flow: str = "default") -> int:
|
||
raw = (os.getenv("RPA_CHROME_DEBUG_PORT") or "").strip()
|
||
if raw:
|
||
try:
|
||
return int(raw)
|
||
except ValueError:
|
||
pass
|
||
if use_user_chrome():
|
||
return _USER_CHROME_PORT
|
||
session = chrome_session_flow(flow)
|
||
return _FLOW_DEBUG_PORTS.get(session, 9335)
|
||
|
||
|
||
# 在已打开的窗口里切到采购协议页签。找不到返回 False。
|
||
def _focus_agreement_tab(driver) -> bool:
|
||
try:
|
||
handles = list(driver.window_handles)
|
||
except Exception:
|
||
return False
|
||
for handle in handles:
|
||
try:
|
||
driver.switch_to.window(handle)
|
||
href = str(driver.current_url or "")
|
||
title = str(driver.title or "")
|
||
except Exception:
|
||
continue
|
||
if "procurementAgree" in href or "采购协议" in title:
|
||
return True
|
||
return False
|
||
|
||
|
||
# 返利班浏览器适配器。用户 Chrome junction / 端口按本模块解析。
|
||
class ApaBrowserAdapter(_SharedAdapter):
|
||
# 本班次 Chrome 用户目录:用户 Chrome 走 junction,否则按查号会话。
|
||
def _profile_dir(self) -> Path:
|
||
return resolve_chrome_profile_dir(self._flow)
|
||
|
||
# 本班次调试口:用户 Chrome 9222,否则查号会话 9335。
|
||
def _debug_port(self) -> int:
|
||
return chrome_debug_port(self._flow)
|
||
|
||
# 是否按日常 Chrome 用户目录重开并恢复标签。
|
||
def _restore_session(self) -> bool:
|
||
return use_user_chrome()
|
||
|
||
# 用户 Chrome 时切到已打开的采购协议页。
|
||
def _focus_existing_tab(self, driver) -> bool:
|
||
if not use_user_chrome():
|
||
return False
|
||
return _focus_agreement_tab(driver)
|
||
|
||
# 独立进程拉起 Chrome 再附着,Python 退出后窗口还在。
|
||
def _launch_driver(self, service, profile_dir: Path, port: int):
|
||
return launch_independent_chrome(
|
||
service,
|
||
profile_dir,
|
||
port,
|
||
restore_session=use_user_chrome(),
|
||
)
|