Prefer Chromium for turnstilePatch on headless servers

Google Chrome blocks --load-extension, so CF tokens stay empty under
Xvfb. Auto-select Chromium when the extension is present, install/
detect chromium in start.sh, and document browser_prefer overrides.
This commit is contained in:
chaos committed 2026-07-12 00:37:27 +08:00
1 parent cd4a2351ae
commit cf6c0d2b4d
7 files changed
+141 -45

No files matched your search

+3 -2
View File
@@ -543,8 +543,9 @@ curl -sS http://127.0.0.1:8317/v1/chat/completions \
| 协议 `sso invalid` | SSO 过期/无效;会回退浏览器;检查账本第三段 | | 协议 `sso invalid` | SSO 过期/无效;会回退浏览器;检查账本第三段 |
| 协议 verify/approve 失败 | 会话态变化 / 风控;看日志后自动回退浏览器 | | 协议 verify/approve 失败 | 会话态变化 / 风控;看日志后自动回退浏览器 |
| 一直 `authorization_pending` | 浏览器路径未完成 consent;需到「设备已授权」且 token 200 | | 一直 `authorization_pending` | 浏览器路径未完成 consent;需到「设备已授权」且 token 200 |
| Cloudflare / Turnstile | 优先 `xvfb-run` 有头;纯 headless 易拦;Chromium+turnstilePatch、检查代理 | | Cloudflare / Turnstile | 必须用 **Chromium + turnstilePatch**(Chrome 无法 `--load-extension`,token 常为 0);配合 `./start.sh` 自动 Xvfb |
| 浏览器 connection fails / no interface | 无 DISPLAY:已自动 headless;仍失败则装 Chrome 并用 `xvfb-run -a ...` | | Turnstile token 长度=0 | 日志若 `skip turnstilePatch on Google Chrome`:`sudo apt install -y chromium` 后重跑;或 `browser_path` 指向 chromium |
| 浏览器 connection fails / no interface | 无 DISPLAY:`./start.sh` 自动 Xvfb / Python headless |
| Hotmail 收不到码 | 检查四段凭证、ClientID/Token、IMAP 主机与 alias 计数 | | Hotmail 收不到码 | 检查四段凭证、ClientID/Token、IMAP 主机与 alias 计数 |
| 有 token 但无 grok-4.5 | `cpa_base_url` 是否为 `cli-chat-proxy` | | 有 token 但无 grok-4.5 | `cpa_base_url` 是否为 `cli-chat-proxy` |
| 注册成功但无 `cpa_auths` | `cpa_export_enabled`?看 `cpa_auths/cpa_auth_failed.txt` | | 注册成功但无 `cpa_auths` | `cpa_export_enabled`?看 `cpa_auths/cpa_auth_failed.txt` |
+3 -2
View File
@@ -184,12 +184,13 @@ def save_account(email: str, password: str, sso: str = ""):
def resolve_browser_path() -> str | None: def resolve_browser_path() -> str | None:
# Prefer Chromium: Google Chrome blocks --load-extension (turnstilePatch). # Prefer Chromium: Google Chrome blocks --load-extension (turnstilePatch).
# Chrome-first order often leaves Turnstile token=0 on Xvfb servers.
for cand in ( for cand in (
"/snap/bin/chromium",
"/usr/bin/chromium", "/usr/bin/chromium",
"/usr/bin/chromium-browser", "/usr/bin/chromium-browser",
"/usr/bin/google-chrome", "/snap/bin/chromium",
"/usr/bin/google-chrome-stable", "/usr/bin/google-chrome-stable",
"/usr/bin/google-chrome",
): ):
if os.path.isfile(cand) or os.path.islink(cand): if os.path.isfile(cand) or os.path.islink(cand):
return cand return cand
+4 -2
View File
@@ -56,9 +56,11 @@
"// ===== 网络 / 浏览器 =====": "", "// ===== 网络 / 浏览器 =====": "",
"// proxy": "注册机主代理:Chromium + 邮箱 HTTP。可带账号 http://user:pass@host:port。优先于系统 https_proxy。", "// proxy": "注册机主代理:Chromium + 邮箱 HTTP。可带账号 http://user:pass@host:port。优先于系统 https_proxy。",
"proxy": "http://127.0.0.1:7890", "proxy": "http://127.0.0.1:7890",
"// browser_headless": "注册 Chromium:true=强制无头,false=强制有头,auto/省略=无 DISPLAY 时自动 headless。也可用 env BROWSER_HEADLESS / CLI --headless|--headed。无头服务器建议 xvfb-run。", "// browser_headless": "注册浏览器:true=强制无头,false=强制有头,auto/省略=无 DISPLAY 时自动 headless。也可用 env BROWSER_HEADLESS / CLI --headless|--headed。无头服务器建议 start.sh 自动 Xvfb。",
"browser_headless": "auto", "browser_headless": "auto",
"// browser_path": "可选,强制 Chromium/Chrome 可执行文件路径;默认自动探测 google-chrome-stable / chromium。", "// browser_prefer": "auto=有 turnstilePatch 时优先 Chromium(可 --load-extension);chromium|chrome 强制偏好。Google Chrome 无法加载扩展,服务器上 Turnstile 常 token=0。",
"browser_prefer": "auto",
"// browser_path": "可选,强制浏览器可执行文件路径;默认按 browser_prefer 自动探测。",
"browser_path": "", "browser_path": "",
"// user_agent": "Chromium UA;一般保持默认 Chrome 桌面串即可。", "// user_agent": "Chromium UA;一般保持默认 Chrome 桌面串即可。",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
+2 -2
View File
@@ -233,11 +233,11 @@ def create_standalone_page(
# Prefer Chromium: Google Chrome blocks --load-extension (turnstilePatch). # Prefer Chromium: Google Chrome blocks --load-extension (turnstilePatch).
for cand in ( for cand in (
"/snap/bin/chromium",
"/usr/bin/chromium", "/usr/bin/chromium",
"/usr/bin/chromium-browser", "/usr/bin/chromium-browser",
"/usr/bin/google-chrome", "/snap/bin/chromium",
"/usr/bin/google-chrome-stable", "/usr/bin/google-chrome-stable",
"/usr/bin/google-chrome",
): ):
if os.path.isfile(cand) or os.path.islink(cand): if os.path.isfile(cand) or os.path.islink(cand):
try: try:
+61 -12
View File
@@ -58,6 +58,7 @@ DEFAULT_CONFIG = {
"cloudmail_password": "", "cloudmail_password": "",
"cpa_gui_close_mint_browser": True, "cpa_gui_close_mint_browser": True,
"browser_headless": "auto", "browser_headless": "auto",
"browser_prefer": "auto",
"browser_path": "", "browser_path": "",
"hotmail_accounts_file": "mail_credentials.txt", "hotmail_accounts_file": "mail_credentials.txt",
"hotmail_alias_mode": "random", "hotmail_alias_mode": "random",
@@ -772,16 +773,22 @@ def apply_headless_to_options(options, headless: bool = True) -> None:
except Exception: except Exception:
pass pass
# Prefer Google Chrome for registration reliability (email delivery path). # Browser discovery order is decided by resolve_browser_path():
# Chromium is fallback; turnstilePatch extension only loads on Chromium. # - default (auto): prefer Chromium so turnstilePatch can load (--load-extension)
# On Chrome we inject the same patch via CDP add_init_js (see apply_stealth_patches). # - Google Chrome blocks unpacked extensions; CDP stealth alone often fails CF on servers
BROWSER_CANDIDATES = ( # - config browser_prefer: auto | chromium | chrome
"/usr/bin/google-chrome-stable", # - config browser_path: absolute override
"/usr/bin/google-chrome", CHROMIUM_CANDIDATES = (
"/snap/bin/chromium",
"/usr/bin/chromium", "/usr/bin/chromium",
"/usr/bin/chromium-browser", "/usr/bin/chromium-browser",
"/snap/bin/chromium",
) )
CHROME_CANDIDATES = (
"/usr/bin/google-chrome-stable",
"/usr/bin/google-chrome",
)
# Legacy flat list (tests / external callers)
BROWSER_CANDIDATES = CHROMIUM_CANDIDATES + CHROME_CANDIDATES
# Minimal CDP stealth only. Aggressive patches (chrome.runtime delete, permissions # Minimal CDP stealth only. Aggressive patches (chrome.runtime delete, permissions
# monkeypatch, fake plugins/languages, postMessage spam) caused xAI UI to show # monkeypatch, fake plugins/languages, postMessage spam) caused xAI UI to show
@@ -816,15 +823,54 @@ def _is_google_chrome_path(path: str | None) -> bool:
return "chrome" in base and "chromium" not in path return "chrome" in base and "chromium" not in path
def _first_existing_browser(candidates) -> str | None:
for cand in candidates:
if cand and (os.path.isfile(cand) or os.path.islink(cand)):
return cand
return None
def resolve_browser_path(): def resolve_browser_path():
"""Pick Chromium/Chrome for registration.
Priority:
1. config browser_path (absolute)
2. env BROWSER_PATH / CHROME_PATH
3. browser_prefer=chromium|chrome|auto
auto → Chromium first when turnstilePatch exists (needed for CF)
4. any remaining candidate
"""
# Optional override from config.json "browser_path" # Optional override from config.json "browser_path"
override = str((config.get("browser_path") if isinstance(config, dict) else "") or "").strip() override = str((config.get("browser_path") if isinstance(config, dict) else "") or "").strip()
if override and (os.path.isfile(override) or os.path.islink(override)): if override and (os.path.isfile(override) or os.path.islink(override)):
return override return override
for cand in BROWSER_CANDIDATES: for env_key in ("BROWSER_PATH", "CHROME_PATH", "CHROMIUM_PATH"):
if os.path.isfile(cand) or os.path.islink(cand): env_path = (os.environ.get(env_key) or "").strip()
return cand if env_path and (os.path.isfile(env_path) or os.path.islink(env_path)):
return None return env_path
prefer = str((config.get("browser_prefer") if isinstance(config, dict) else "") or "auto").strip().lower()
if prefer in ("", "default", "detect"):
prefer = "auto"
# env override
env_pref = (os.environ.get("BROWSER_PREFER") or "").strip().lower()
if env_pref:
prefer = env_pref
has_ext = os.path.isdir(EXTENSION_PATH)
if prefer in ("chrome", "google-chrome", "google"):
order = list(CHROME_CANDIDATES) + list(CHROMIUM_CANDIDATES)
elif prefer in ("chromium", "chromeium"): # typo tolerant
order = list(CHROMIUM_CANDIDATES) + list(CHROME_CANDIDATES)
else:
# auto: extension needs Chromium; without extension Chrome is fine
if has_ext:
order = list(CHROMIUM_CANDIDATES) + list(CHROME_CANDIDATES)
else:
order = list(CHROME_CANDIDATES) + list(CHROMIUM_CANDIDATES)
path = _first_existing_browser(order)
return path
def apply_stealth_patches(page=None, log_callback=None) -> bool: def apply_stealth_patches(page=None, log_callback=None) -> bool:
@@ -902,11 +948,14 @@ def create_browser_options(headless=None):
# Google Chrome blocks CLI unpacked extension loading; Chromium still allows it. # Google Chrome blocks CLI unpacked extension loading; Chromium still allows it.
if _is_google_chrome_path(browser_path): if _is_google_chrome_path(browser_path):
print( print(
" [ext] Chrome: minimal CDP stealth only (extension blocked)", " [ext] Chrome: minimal CDP stealth only (extension blocked)\n"
" 建议安装 Chromium 或 config browser_prefer=chromium / browser_path=/usr/bin/chromium\n"
" 否则 Turnstile token 常为 0(无头/Xvfb 服务器上尤其明显)",
flush=True, flush=True,
) )
else: else:
options.add_extension(EXTENSION_PATH) options.add_extension(EXTENSION_PATH)
print(f" [ext] turnstilePatch loaded: {EXTENSION_PATH}", flush=True)
# Apply config.json "proxy" to Chromium. Without this, only HTTP helpers # Apply config.json "proxy" to Chromium. Without this, only HTTP helpers
# used get_proxies(); the browser itself fell through to system/env proxy. # used get_proxies(); the browser itself fell through to system/env proxy.
proxy = (config.get("proxy") or "").strip() proxy = (config.get("proxy") or "").strip()
+11 -5
View File
@@ -59,7 +59,8 @@ def _patched_create_browser_options():
except Exception: except Exception:
pass pass
# Prefer Google Chrome for registration reliability; Chromium fallback for extension. # Prefer register resolve_browser_path (Chromium first when turnstilePatch exists).
# Do NOT force Google Chrome here — Chrome blocks --load-extension and CF often fails.
browser_path = None browser_path = None
resolve = getattr(reg, "resolve_browser_path", None) resolve = getattr(reg, "resolve_browser_path", None)
if callable(resolve): if callable(resolve):
@@ -69,11 +70,11 @@ def _patched_create_browser_options():
browser_path = None browser_path = None
if not browser_path: if not browser_path:
for cand in ( for cand in (
"/usr/bin/google-chrome-stable",
"/usr/bin/google-chrome",
"/snap/bin/chromium",
"/usr/bin/chromium", "/usr/bin/chromium",
"/usr/bin/chromium-browser", "/usr/bin/chromium-browser",
"/snap/bin/chromium",
"/usr/bin/google-chrome-stable",
"/usr/bin/google-chrome",
): ):
if os.path.isfile(cand) or os.path.islink(cand): if os.path.isfile(cand) or os.path.islink(cand):
browser_path = cand browser_path = cand
@@ -103,10 +104,15 @@ def _patched_create_browser_options():
and "chromium" not in browser_path and "chromium" not in browser_path
) )
if is_chrome: if is_chrome:
print("[ext] skip turnstilePatch on Google Chrome (--load-extension blocked)", flush=True) print(
"[ext] skip turnstilePatch on Google Chrome (--load-extension blocked); "
"install chromium or set browser_prefer=chromium",
flush=True,
)
else: else:
try: try:
opts.add_extension(ext_path) opts.add_extension(ext_path)
print(f"[ext] turnstilePatch loaded: {ext_path}", flush=True)
except Exception: except Exception:
pass pass
+57 -20
View File
@@ -339,33 +339,58 @@ step3_sync_deps() {
step4_check_browser() { step4_check_browser() {
echo -e "${CYAN}[4/6] 检测/安装浏览器...${NC}" echo -e "${CYAN}[4/6] 检测/安装浏览器...${NC}"
# Prefer Chromium for turnstilePatch (--load-extension works).
# Google Chrome alone often leaves CF token=0 on Xvfb servers.
local candidate local candidate
local found_chromium=""
local found_chrome=""
for candidate in \ for candidate in \
google-chrome-stable google-chrome chromium chromium-browser \ chromium chromium-browser \
/usr/bin/chromium /usr/bin/chromium-browser /snap/bin/chromium; do
if have_cmd "$candidate" || [ -x "$candidate" ] 2>/dev/null; then
found_chromium="$(command -v "$candidate" 2>/dev/null || echo "$candidate")"
break
fi
done
for candidate in \
google-chrome-stable google-chrome \
/usr/bin/google-chrome-stable /usr/bin/google-chrome \ /usr/bin/google-chrome-stable /usr/bin/google-chrome \
/snap/bin/chromium /usr/bin/chromium /usr/bin/chromium-browser \
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; do "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; do
if have_cmd "$candidate" || [ -x "$candidate" ] 2>/dev/null; then if have_cmd "$candidate" || [ -x "$candidate" ] 2>/dev/null; then
echo -e " ${GREEN}✓${NC} 浏览器: $(command -v "$candidate" 2>/dev/null || echo "$candidate")" found_chrome="$(command -v "$candidate" 2>/dev/null || echo "$candidate")"
return 0 break
fi fi
done done
echo -e " ${YELLOW}⚠${NC} 未检测到 Chrome/Chromium,尝试自动安装..." if [ -n "$found_chromium" ]; then
echo -e " ${GREEN}✓${NC} Chromium: $found_chromium (可加载 turnstilePatch)"
if [ -n "$found_chrome" ]; then
echo -e " ${CYAN}·${NC} 另检测到 Chrome: $found_chrome(注册默认优先 Chromium)"
fi
return 0
fi
if [ -n "$found_chrome" ]; then
echo -e " ${YELLOW}⚠${NC} 仅有 Google Chrome: $found_chrome"
echo " Chrome 无法加载 turnstilePatch,Turnstile 容易 token=0。"
echo " 将尝试安装 Chromium..."
else
echo -e " ${YELLOW}⚠${NC} 未检测到 Chrome/Chromium,尝试自动安装 Chromium..."
fi
local installed=false local installed=false
# Debian/Ubuntu # Debian/Ubuntu — prefer chromium (extension support)
if have_cmd apt-get; then if have_cmd apt-get; then
if have_cmd sudo; then if have_cmd sudo; then
if sudo -n true 2>/dev/null; then if sudo -n true 2>/dev/null; then
echo " → 使用 apt 安装 chromium(免密 sudo)..." echo " → 使用 apt 安装 chromium(免密 sudo)..."
if sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq chromium-browser chromium 2>/dev/null; then if sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq chromium chromium-browser 2>/dev/null; then
installed=true installed=true
fi fi
else else
echo -e " ${YELLOW}⚠${NC} 检测到 apt,但需要交互 sudo,跳过自动安装浏览器" echo -e " ${YELLOW}⚠${NC} 检测到 apt,但需要交互 sudo,跳过自动安装浏览器"
echo " 可手动: sudo apt install -y chromium-browser" echo " 可手动: sudo apt install -y chromium"
fi fi
fi fi
# Arch # Arch
@@ -388,23 +413,35 @@ step4_check_browser() {
else else
echo " 可手动: sudo dnf install -y chromium" echo " 可手动: sudo dnf install -y chromium"
fi fi
# macOS Homebrew # macOS Homebrew — chromium cask if available, else chrome
elif have_cmd brew; then elif have_cmd brew; then
echo " → 使用 Homebrew 安装 Google Chrome..." echo " → 使用 Homebrew 安装 chromium / chrome..."
if brew install --cask google-chrome 2>/dev/null; then if brew install --cask chromium 2>/dev/null || brew install --cask google-chrome 2>/dev/null; then
installed=true installed=true
fi fi
fi fi
# 再检测一次 # 再检测一次(Chromium 优先)
for candidate in \ for candidate in \
google-chrome-stable google-chrome chromium chromium-browser \ chromium chromium-browser \
/usr/bin/chromium /usr/bin/chromium-browser /snap/bin/chromium \
google-chrome-stable google-chrome \
/usr/bin/google-chrome-stable /usr/bin/google-chrome \ /usr/bin/google-chrome-stable /usr/bin/google-chrome \
/snap/bin/chromium /usr/bin/chromium /usr/bin/chromium-browser \
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; do "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; do
if have_cmd "$candidate" || [ -x "$candidate" ] 2>/dev/null; then if have_cmd "$candidate" || [ -x "$candidate" ] 2>/dev/null; then
echo -e " ${GREEN}✓${NC} 浏览器已就绪: $(command -v "$candidate" 2>/dev/null || echo "$candidate")" local resolved
return 0 resolved="$(command -v "$candidate" 2>/dev/null || echo "$candidate")"
case "$resolved" in
*chromium*)
echo -e " ${GREEN}✓${NC} 浏览器已就绪 (Chromium): $resolved"
return 0
;;
*)
echo -e " ${YELLOW}⚠${NC} 仅有 Chrome: $resolved"
echo " 注册机将用 CDP stealth;过 CF 仍建议: sudo apt install -y chromium"
return 0
;;
esac
fi fi
done done
@@ -412,12 +449,12 @@ step4_check_browser() {
echo -e " ${YELLOW}⚠${NC} 安装命令已执行,但仍未在 PATH 中找到浏览器,请重启终端后重试" echo -e " ${YELLOW}⚠${NC} 安装命令已执行,但仍未在 PATH 中找到浏览器,请重启终端后重试"
else else
echo -e " ${YELLOW}⚠${NC} 未能自动安装浏览器" echo -e " ${YELLOW}⚠${NC} 未能自动安装浏览器"
echo " DrissionPage 需要 Chrome/Chromium;协议 mint 不强制浏览器,回退流程需要。" echo " 注册需要 Chromium(推荐,可加载 turnstilePatch)或 Chrome。"
echo " 手动安装示例:" echo " 手动安装示例:"
echo " Ubuntu/Debian: sudo apt install -y chromium-browser" echo " Ubuntu/Debian: sudo apt install -y chromium"
echo " Arch: sudo pacman -S chromium" echo " Arch: sudo pacman -S chromium"
echo " macOS: brew install --cask google-chrome" echo " macOS: brew install --cask chromium"
echo " ${YELLOW}脚本将继续,注册可能因缺少浏览器而失败。${NC}" echo " ${YELLOW}脚本将继续,注册可能因缺少浏览器 / Turnstile 失败。${NC}"
fi fi
} }