Files
autoPlus/core/sentinel.py
2026-01-26 15:04:02 +08:00

226 lines
7.2 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.
"""
Sentinel 反爬机制处理器
Sentinel 是 OpenAI 用于防止自动化注册的安全机制,包括:
- Proof of Work (PoW) 挑战
- 设备指纹验证
- 行为分析
✅ 已集成完整的 Sentinel 解决方案(使用 reference/ 下的代码)
"""
from typing import Optional, Dict, Any
from utils.logger import logger
from utils.fingerprint import BrowserFingerprint
class SentinelHandler:
"""
Sentinel 反爬机制处理器
✅ 已集成用户的 Sentinel 解决方案
使用 reference/ 目录下的完整实现
"""
# Sentinel API 端点(从开发文档提取)
SENTINEL_API = "https://chatgpt.com/_next/static/chunks/sentinel.js"
SENTINEL_TOKEN_ENDPOINT = "https://api.openai.com/sentinel/token"
def __init__(self, session):
"""
初始化 Sentinel 处理器
参数:
session: OAISession 实例(需要使用其 Cookie 和代理)
"""
from core.session import OAISession
self.session: OAISession = session
self.oai_did = session.oai_did
# 初始化浏览器指纹
self.fingerprint = BrowserFingerprint(session_id=self.oai_did)
# 延迟导入 SentinelSolver避免循环导入
self._solver = None
logger.info("SentinelHandler initialized")
def _get_solver(self):
"""延迟初始化 Sentinel 求解器"""
if self._solver is None:
try:
from reference.sentinel_solver import SentinelSolver
self._solver = SentinelSolver(self.fingerprint)
logger.debug("SentinelSolver initialized successfully")
except ImportError as e:
logger.error(f"Failed to import SentinelSolver: {e}")
raise ImportError(
"Sentinel solver not found. Please check reference/ directory."
) from e
return self._solver
async def get_token(
self,
flow: str = "username_password_create",
**kwargs
) -> Dict[str, Any]:
"""
获取 Sentinel Token
✅ 已实现 - 使用 reference/ 下的完整解决方案
参数:
flow: 注册流程类型,常见值:
- "username_password_create" (注册流程)
- "username_password_create__auto" (自动注册)
**kwargs: 其他可能需要的参数
返回:
Sentinel Token 字典 {"p": "...", "t": "...", "c": "...", "id": "...", "flow": "..."}
实现说明:
使用 reference/sentinel_solver.py 生成 requirements token
返回完整的 JSON 对象(用于 Header
"""
logger.info(f"Generating Sentinel token for flow='{flow}'")
try:
# 获取求解器
solver = self._get_solver()
# 生成 requirements token
token_dict = solver.generate_requirements_token()
# 构建完整 token
# 格式: {"p": "gAAAAAC...", "t": "...", "c": "...", "id": "uuid", "flow": "..."}
token_dict["flow"] = flow
token_dict["id"] = self.oai_did
# 验证必需字段
if "p" not in token_dict or not token_dict["p"]:
raise ValueError("Generated token missing 'p' field")
logger.success(f"Sentinel token generated: {str(token_dict)[:50]}...")
return token_dict
except ImportError as e:
logger.error(f"Sentinel solver not available: {e}")
raise NotImplementedError(
"❌ Sentinel solver import failed.\n\n"
"Please ensure:\n"
"1. reference/ directory exists with sentinel_solver.py\n"
"2. sdk/sdk.js file exists\n"
"3. Node.js is installed and available in PATH\n\n"
f"Error: {e}"
) from e
except Exception as e:
logger.exception(f"Failed to generate Sentinel token: {e}")
raise RuntimeError(f"Sentinel token generation failed: {e}") from e
async def solve_enforcement(
self,
enforcement_config: Dict[str, Any]
) -> str:
"""
解决完整的 enforcement 挑战PoW + Turnstile
✅ 已实现 - 使用 reference/sentinel_solver.py
参数:
enforcement_config: 服务器返回的挑战配置
{
'proofofwork': {
'seed': '...',
'difficulty': '0003a',
'token': '...', # cached token
'turnstile': {
'dx': '...' # VM bytecode
}
}
}
返回:
完整的 Sentinel token (JSON string)
"""
logger.info("Solving enforcement challenge...")
try:
solver = self._get_solver()
token_json = solver.solve_enforcement(enforcement_config)
logger.success(f"Enforcement solved: {token_json[:50]}...")
return token_json
except Exception as e:
logger.exception(f"Failed to solve enforcement: {e}")
raise RuntimeError(f"Enforcement solving failed: {e}") from e
def _build_payload(self, flow: str) -> Dict[str, Any]:
"""
构建 Sentinel 请求 Payload
参考开发文档中的请求格式:
{
"p": "gAAAAAB...", # Proof of Work 答案
"id": "a1b2c3d4-...", # oai-did
"flow": "username_password_create__auto"
}
参数:
flow: 注册流程类型
返回:
Payload 字典
"""
return {
"p": "gAAAAAB_PLACEHOLDER_POW_ANSWER",
"id": self.oai_did,
"flow": flow
}
async def verify_token(self, token: str) -> bool:
"""
验证 Sentinel Token 是否有效(可选功能)
参数:
token: 待验证的 Sentinel Token
返回:
True 如果有效,否则 False
"""
if not token or token == "placeholder_token":
logger.warning("Received placeholder token, validation skipped")
return False
# Token 基本格式检查
if not token.startswith("gAAAAA"):
logger.warning(f"Invalid token format: {token[:20]}...")
return False
logger.info(f"Token validation: {token[:20]}... (length={len(token)})")
return True
def get_sentinel_script(self) -> Optional[str]:
"""
获取 Sentinel JavaScript 代码(可选,用于分析)
返回:
Sentinel JS 代码内容,失败则返回 None
"""
try:
response = self.session.get(self.SENTINEL_API)
if response.status_code == 200:
logger.info(f"Sentinel script fetched: {len(response.text)} bytes")
return response.text
else:
logger.error(f"Failed to fetch Sentinel script: {response.status_code}")
return None
except Exception as e:
logger.error(f"Error fetching Sentinel script: {e}")
return None
# 导出主要接口
__all__ = ["SentinelHandler"]