AutoRpa/rpa_rebate/apa_browser_adapter.py

167 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 返利班浏览器适配。共享协议在 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(),
)