mirror of
https://github.com/zc-zhangchen/any-auto-register.git
synced 2026-05-16 11:06:45 +08:00
fix(chatgpt): strengthen sentinel token path and 400 retry handling
This commit is contained in:
@@ -833,9 +833,15 @@ class RefreshTokenRegistrationEngine:
|
||||
body_preview = response.text[:200]
|
||||
self._log(f"账户创建失败: {body_preview}", "warning")
|
||||
|
||||
should_retry = response.status_code in (400, 403) and (
|
||||
"sentinel" in body_preview.lower()
|
||||
or "registration_disallowed" in body_preview.lower()
|
||||
body_lower = body_preview.lower()
|
||||
should_retry = response.status_code in (400, 403) and any(
|
||||
marker in body_lower
|
||||
for marker in (
|
||||
"sentinel",
|
||||
"registration_disallowed",
|
||||
"failed to create account",
|
||||
"please try again",
|
||||
)
|
||||
)
|
||||
if not should_retry:
|
||||
return False
|
||||
@@ -985,11 +991,52 @@ class RefreshTokenRegistrationEngine:
|
||||
auth_base="https://auth.openai.com",
|
||||
)
|
||||
|
||||
final_status = response.status_code
|
||||
body_text = response.text[:200]
|
||||
if response.status_code == 400 and "already_exists" in body_text.lower():
|
||||
if final_status == 400 and "already_exists" in body_text.lower():
|
||||
return "https://auth.openai.com/sign-in-with-chatgpt/codex/consent"
|
||||
|
||||
self._log(f"OAuth about-you create_account 失败: {response.status_code} {body_text}", "warning")
|
||||
body_lower = body_text.lower()
|
||||
should_retry = final_status in (400, 403) and any(
|
||||
marker in body_lower
|
||||
for marker in (
|
||||
"sentinel",
|
||||
"registration_disallowed",
|
||||
"failed to create account",
|
||||
"please try again",
|
||||
)
|
||||
)
|
||||
if should_retry:
|
||||
retry_token = self._check_sentinel(
|
||||
self._device_id or "",
|
||||
flow="oauth_create_account",
|
||||
)
|
||||
if retry_token:
|
||||
headers["openai-sentinel-token"] = retry_token
|
||||
try:
|
||||
retry_resp = self.session.post(
|
||||
OPENAI_API_ENDPOINTS["create_account"],
|
||||
headers=headers,
|
||||
data=json.dumps(user_info),
|
||||
)
|
||||
if retry_resp.status_code == 200:
|
||||
retry_data = retry_resp.json() or {}
|
||||
return normalize_flow_url(
|
||||
str(retry_data.get("continue_url") or ""),
|
||||
auth_base="https://auth.openai.com",
|
||||
)
|
||||
final_status = retry_resp.status_code
|
||||
body_text = retry_resp.text[:200]
|
||||
except Exception as e:
|
||||
self._log(
|
||||
f"OAuth about-you create_account 重试失败: {e}",
|
||||
"warning",
|
||||
)
|
||||
|
||||
self._log(
|
||||
f"OAuth about-you create_account 失败: {final_status} {body_text}",
|
||||
"warning",
|
||||
)
|
||||
return ""
|
||||
|
||||
def _resolve_post_otp_continue_url(self) -> str:
|
||||
|
||||
@@ -3,12 +3,197 @@ Sentinel Token 生成器模块
|
||||
基于对 sentinel.openai.com SDK 的逆向分析
|
||||
"""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import uuid
|
||||
import random
|
||||
import base64
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from shutil import which
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
|
||||
SENTINEL_REQ_URL = "https://sentinel.openai.com/backend-api/sentinel/req"
|
||||
SENTINEL_SDK_VERSION = os.getenv("OPENAI_SENTINEL_SDK_VERSION", "20260219f9f6")
|
||||
SENTINEL_SDK_URL = f"https://sentinel.openai.com/sentinel/{SENTINEL_SDK_VERSION}/sdk.js"
|
||||
SENTINEL_REFERER = (
|
||||
f"https://sentinel.openai.com/backend-api/sentinel/frame.html?sv={SENTINEL_SDK_VERSION}"
|
||||
)
|
||||
|
||||
|
||||
def _resolve_vm_script() -> Path | None:
|
||||
direct_script = os.getenv("OPENAI_SENTINEL_VM_SCRIPT", "").strip()
|
||||
if direct_script:
|
||||
path = Path(direct_script).expanduser().resolve()
|
||||
if path.exists() and path.is_file():
|
||||
return path
|
||||
|
||||
vm_dir = os.getenv("OPENAI_SENTINEL_VM_DIR", "").strip()
|
||||
if vm_dir:
|
||||
candidate = Path(vm_dir).expanduser().resolve() / "openai_sentinel_vm.js"
|
||||
if candidate.exists() and candidate.is_file():
|
||||
return candidate
|
||||
|
||||
# 默认尝试工作区同级目录:D:/Develop/AI/sentinel/openai_sentinel_vm.js
|
||||
candidate = (
|
||||
Path(__file__).resolve().parents[3] / "sentinel" / "openai_sentinel_vm.js"
|
||||
)
|
||||
if candidate.exists() and candidate.is_file():
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_node_binary() -> str | None:
|
||||
node_env = os.getenv("OPENAI_SENTINEL_NODE_PATH", "").strip()
|
||||
if node_env:
|
||||
path = Path(node_env).expanduser().resolve()
|
||||
if path.exists() and path.is_file():
|
||||
return str(path)
|
||||
return which("node")
|
||||
|
||||
|
||||
def _ensure_sdk_file() -> Path | None:
|
||||
direct = os.getenv("OPENAI_SENTINEL_SDK_FILE", "").strip()
|
||||
if direct:
|
||||
path = Path(direct).expanduser().resolve()
|
||||
if path.exists() and path.is_file():
|
||||
return path
|
||||
|
||||
cache_dir = Path(tempfile.gettempdir()) / "openai-sentinel-cache" / SENTINEL_SDK_VERSION
|
||||
cache_file = cache_dir / "sdk.js"
|
||||
try:
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
except Exception:
|
||||
return None
|
||||
if cache_file.exists() and cache_file.stat().st_size > 0:
|
||||
return cache_file
|
||||
|
||||
request = Request(
|
||||
SENTINEL_SDK_URL,
|
||||
headers={
|
||||
"User-Agent": "Mozilla/5.0",
|
||||
"Referer": "https://auth.openai.com/",
|
||||
"Accept": "*/*",
|
||||
},
|
||||
)
|
||||
try:
|
||||
with urlopen(request, timeout=20) as response:
|
||||
cache_file.write_bytes(response.read())
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if cache_file.exists() and cache_file.stat().st_size > 0:
|
||||
return cache_file
|
||||
return None
|
||||
|
||||
|
||||
def _vm_browser_payload(device_id: str, user_agent: str | None) -> dict:
|
||||
return {
|
||||
"device_id": str(device_id or "").strip(),
|
||||
"user_agent": user_agent
|
||||
or (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
||||
"Chrome/146.0.0.0 Safari/537.36"
|
||||
),
|
||||
"language": "zh-CN",
|
||||
"languages": ["zh-CN", "zh"],
|
||||
"hardware_concurrency": 12,
|
||||
"screen_width": 1366,
|
||||
"screen_height": 768,
|
||||
"performance_now": 12345.67,
|
||||
"time_origin": 1710000000000.0,
|
||||
"js_heap_size_limit": 4294967296,
|
||||
}
|
||||
|
||||
|
||||
def _run_vm(action: str, payload: dict) -> dict | None:
|
||||
node_binary = _resolve_node_binary()
|
||||
vm_script = _resolve_vm_script()
|
||||
sdk_file = _ensure_sdk_file()
|
||||
if not node_binary or not vm_script or not sdk_file:
|
||||
return None
|
||||
|
||||
full_payload = {"action": action, "sdk_path": str(sdk_file), **(payload or {})}
|
||||
timeout_sec = int(os.getenv("OPENAI_SENTINEL_VM_TIMEOUT_SEC", "40") or "40")
|
||||
|
||||
try:
|
||||
process = subprocess.run(
|
||||
[node_binary, str(vm_script)],
|
||||
input=json.dumps(full_payload, separators=(",", ":")),
|
||||
text=True,
|
||||
capture_output=True,
|
||||
cwd=str(vm_script.parent),
|
||||
timeout=max(5, timeout_sec),
|
||||
check=False,
|
||||
)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if process.returncode != 0:
|
||||
return None
|
||||
output = str(process.stdout or "").strip()
|
||||
if not output:
|
||||
return None
|
||||
try:
|
||||
data = json.loads(output)
|
||||
except Exception:
|
||||
return None
|
||||
return data if isinstance(data, dict) else None
|
||||
|
||||
|
||||
def _build_sentinel_token_via_vm(
|
||||
session,
|
||||
device_id,
|
||||
*,
|
||||
flow="authorize_continue",
|
||||
user_agent=None,
|
||||
sec_ch_ua=None,
|
||||
impersonate=None,
|
||||
):
|
||||
payload = _vm_browser_payload(str(device_id or ""), user_agent)
|
||||
req_data = _run_vm("requirements", payload)
|
||||
request_p = str((req_data or {}).get("request_p") or "").strip()
|
||||
if not request_p:
|
||||
return None
|
||||
|
||||
challenge = fetch_sentinel_challenge(
|
||||
session,
|
||||
device_id,
|
||||
flow=flow,
|
||||
user_agent=user_agent,
|
||||
sec_ch_ua=sec_ch_ua,
|
||||
impersonate=impersonate,
|
||||
request_p=request_p,
|
||||
)
|
||||
if not challenge:
|
||||
return None
|
||||
c_value = str(challenge.get("token") or "").strip()
|
||||
if not c_value:
|
||||
return None
|
||||
|
||||
solved = _run_vm(
|
||||
"solve",
|
||||
{**payload, "request_p": request_p, "challenge": challenge},
|
||||
)
|
||||
final_p = str((solved or {}).get("final_p") or (solved or {}).get("p") or "").strip()
|
||||
if not final_p:
|
||||
return None
|
||||
t_value = (solved or {}).get("t")
|
||||
|
||||
return json.dumps(
|
||||
{
|
||||
"p": final_p,
|
||||
"t": "" if t_value is None else str(t_value),
|
||||
"c": c_value,
|
||||
"id": device_id,
|
||||
"flow": flow,
|
||||
},
|
||||
separators=(",", ":"),
|
||||
)
|
||||
|
||||
|
||||
class SentinelTokenGenerator:
|
||||
@@ -138,23 +323,37 @@ class SentinelTokenGenerator:
|
||||
return "gAAAAAC" + data
|
||||
|
||||
|
||||
def fetch_sentinel_challenge(session, device_id, flow="authorize_continue", user_agent=None, sec_ch_ua=None, impersonate=None):
|
||||
def fetch_sentinel_challenge(
|
||||
session,
|
||||
device_id,
|
||||
flow="authorize_continue",
|
||||
user_agent=None,
|
||||
sec_ch_ua=None,
|
||||
impersonate=None,
|
||||
request_p=None,
|
||||
):
|
||||
"""调用 sentinel 后端 API 获取 challenge 数据"""
|
||||
generator = SentinelTokenGenerator(device_id=device_id, user_agent=user_agent)
|
||||
request_p = str(request_p or "").strip() or generator.generate_requirements_token()
|
||||
req_body = {
|
||||
"p": generator.generate_requirements_token(),
|
||||
"p": request_p,
|
||||
"id": device_id,
|
||||
"flow": flow,
|
||||
}
|
||||
|
||||
headers = {
|
||||
"Content-Type": "text/plain;charset=UTF-8",
|
||||
"Referer": "https://sentinel.openai.com/backend-api/sentinel/frame.html",
|
||||
"Accept": "*/*",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
"Referer": SENTINEL_REFERER,
|
||||
"Origin": "https://sentinel.openai.com",
|
||||
"User-Agent": user_agent or "Mozilla/5.0",
|
||||
"sec-ch-ua": sec_ch_ua or '"Not:A-Brand";v="99", "Google Chrome";v="145", "Chromium";v="145"',
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": '"Windows"',
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
}
|
||||
|
||||
kwargs = {
|
||||
@@ -166,7 +365,7 @@ def fetch_sentinel_challenge(session, device_id, flow="authorize_continue", user
|
||||
kwargs["impersonate"] = impersonate
|
||||
|
||||
try:
|
||||
resp = session.post("https://sentinel.openai.com/backend-api/sentinel/req", **kwargs)
|
||||
resp = session.post(SENTINEL_REQ_URL, **kwargs)
|
||||
if resp.status_code == 200:
|
||||
return resp.json()
|
||||
except Exception:
|
||||
@@ -177,7 +376,25 @@ def fetch_sentinel_challenge(session, device_id, flow="authorize_continue", user
|
||||
|
||||
def build_sentinel_token(session, device_id, flow="authorize_continue", user_agent=None, sec_ch_ua=None, impersonate=None):
|
||||
"""构建完整的 openai-sentinel-token JSON 字符串"""
|
||||
challenge = fetch_sentinel_challenge(session, device_id, flow=flow, user_agent=user_agent, sec_ch_ua=sec_ch_ua, impersonate=impersonate)
|
||||
vm_token = _build_sentinel_token_via_vm(
|
||||
session,
|
||||
device_id,
|
||||
flow=flow,
|
||||
user_agent=user_agent,
|
||||
sec_ch_ua=sec_ch_ua,
|
||||
impersonate=impersonate,
|
||||
)
|
||||
if vm_token:
|
||||
return vm_token
|
||||
|
||||
challenge = fetch_sentinel_challenge(
|
||||
session,
|
||||
device_id,
|
||||
flow=flow,
|
||||
user_agent=user_agent,
|
||||
sec_ch_ua=sec_ch_ua,
|
||||
impersonate=impersonate,
|
||||
)
|
||||
|
||||
if not challenge:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user