Files
any-auto-register/platforms/chatgpt/sentinel_token.py

264 lines
7.5 KiB
Python
Raw Permalink 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 Token 生成器模块(纯 Python 方案)。
"""
import base64
import json
import random
import time
import uuid
SENTINEL_REQ_URL = "https://sentinel.openai.com/backend-api/sentinel/req"
SENTINEL_REFERER = "https://sentinel.openai.com/backend-api/sentinel/frame.html"
class SentinelTokenGenerator:
"""
Sentinel Token 纯 Python 生成器。
说明:
- 该实现不依赖 Node / JS。
- t 字段按当前纯 Python 方案固定空串,由上游接口判定可用性。
"""
MAX_ATTEMPTS = 500000
ERROR_PREFIX = "wQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D"
def __init__(self, device_id=None, user_agent=None):
self.device_id = device_id or str(uuid.uuid4())
self.user_agent = user_agent or (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/145.0.0.0 Safari/537.36"
)
self.requirements_seed = str(random.random())
self.sid = str(uuid.uuid4())
@staticmethod
def _fnv1a_32(text):
h = 2166136261
for ch in text:
h ^= ord(ch)
h = (h * 16777619) & 0xFFFFFFFF
h ^= h >> 16
h = (h * 2246822507) & 0xFFFFFFFF
h ^= h >> 13
h = (h * 3266489909) & 0xFFFFFFFF
h ^= h >> 16
return format(h & 0xFFFFFFFF, "08x")
def _get_config(self):
from datetime import datetime, timezone
now = datetime.now(timezone.utc)
date_str = now.strftime("%a %b %d %Y %H:%M:%S GMT+0000 (Coordinated Universal Time)")
perf_now = random.uniform(1000, 50000)
time_origin = time.time() * 1000 - perf_now
nav_prop = random.choice(
[
"vendorSub",
"productSub",
"vendor",
"maxTouchPoints",
"scheduling",
"userActivation",
"doNotTrack",
"geolocation",
"connection",
"plugins",
"mimeTypes",
"pdfViewerEnabled",
"webkitTemporaryStorage",
"webkitPersistentStorage",
"hardwareConcurrency",
"cookieEnabled",
"credentials",
"mediaDevices",
"permissions",
"locks",
"ink",
]
)
return [
"1920x1080",
date_str,
4294705152,
random.random(),
self.user_agent,
"https://sentinel.openai.com/sentinel/20260124ceb8/sdk.js",
None,
None,
"en-US",
"en-US,en",
random.random(),
f"{nav_prop}undefined",
random.choice(["location", "implementation", "URL", "documentURI", "compatMode"]),
random.choice(["Object", "Function", "Array", "Number", "parseFloat", "undefined"]),
perf_now,
self.sid,
"",
random.choice([4, 8, 12, 16]),
time_origin,
]
@staticmethod
def _base64_encode(data):
raw = json.dumps(data, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
return base64.b64encode(raw).decode("ascii")
def _run_check(self, start_time, seed, difficulty, config, nonce):
config[3] = nonce
config[9] = round((time.time() - start_time) * 1000)
encoded = self._base64_encode(config)
digest = self._fnv1a_32(seed + encoded)
if digest[: len(difficulty)] <= difficulty:
return encoded + "~S"
return None
def generate_token(self, seed=None, difficulty=None):
seed = seed or self.requirements_seed
difficulty = difficulty or "0"
start_time = time.time()
config = self._get_config()
for nonce in range(self.MAX_ATTEMPTS):
value = self._run_check(start_time, seed, difficulty, config, nonce)
if value:
return "gAAAAAB" + value
return "gAAAAAB" + self.ERROR_PREFIX + self._base64_encode(str(None))
def generate_requirements_token(self):
config = self._get_config()
config[3] = 1
config[9] = round(random.uniform(5, 50))
return "gAAAAAC" + self._base64_encode(config)
def fetch_sentinel_challenge(
session,
device_id,
flow="authorize_continue",
user_agent=None,
sec_ch_ua=None,
impersonate=None,
request_p=None,
):
generator = SentinelTokenGenerator(device_id=device_id, user_agent=user_agent)
req_body = {
"p": str(request_p or "").strip() or generator.generate_requirements_token(),
"id": device_id,
"flow": flow,
}
headers = {
"Content-Type": "text/plain;charset=UTF-8",
"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 = {"data": json.dumps(req_body), "headers": headers, "timeout": 20}
if impersonate:
kwargs["impersonate"] = impersonate
try:
response = session.post(SENTINEL_REQ_URL, **kwargs)
if response.status_code == 200:
return response.json()
except Exception:
return None
return None
def _build_sentinel_token_python(
session,
device_id,
*,
flow="authorize_continue",
user_agent=None,
sec_ch_ua=None,
impersonate=None,
):
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
c_value = str(challenge.get("token") or "").strip()
if not c_value:
return None
generator = SentinelTokenGenerator(device_id=device_id, user_agent=user_agent)
pow_data = challenge.get("proofofwork") or {}
if pow_data.get("required") and pow_data.get("seed"):
p_value = generator.generate_token(
seed=pow_data.get("seed"),
difficulty=pow_data.get("difficulty", "0"),
)
else:
p_value = generator.generate_requirements_token()
return json.dumps(
{
"p": p_value,
"t": "",
"c": c_value,
"id": device_id,
"flow": flow,
},
separators=(",", ":"),
)
def build_sentinel_token(
session,
device_id,
flow="authorize_continue",
user_agent=None,
sec_ch_ua=None,
impersonate=None,
):
"""默认 Sentinel token 构造:纯 Python。"""
return _build_sentinel_token_python(
session,
device_id,
flow=flow,
user_agent=user_agent,
sec_ch_ua=sec_ch_ua,
impersonate=impersonate,
)
def build_sentinel_token_vm_only(
session,
device_id,
flow="authorize_continue",
user_agent=None,
sec_ch_ua=None,
impersonate=None,
):
"""
VM 分支专用构造器(命名保持不变,内部使用纯 Python
"""
return _build_sentinel_token_python(
session,
device_id,
flow=flow,
user_agent=user_agent,
sec_ch_ua=sec_ch_ua,
impersonate=impersonate,
)