import hashlib import json import random import re import time from datetime import datetime, timedelta, timezone from html.parser import HTMLParser from typing import Any, Sequence import pybase64 DEFAULT_POW_SCRIPT = "https://chatgpt.com/backend-api/sentinel/sdk.js" from utils.helper import new_uuid CORES = [8, 16, 24, 32] DOCUMENT_KEYS = ["_reactListeningo743lnnpvdg", "location"] class ScriptSrcParser(HTMLParser): def __init__(self) -> None: super().__init__() self.script_sources: list[str] = [] self.data_build = "" def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None: if tag != "script": return attrs_dict = dict(attrs) src = attrs_dict.get("src") if not src: return self.script_sources.append(src) match = re.search(r"c/[^/]*/_", src) if match: self.data_build = match.group(0) def parse_pow_resources(html_content: str) -> tuple[list[str], str]: parser = ScriptSrcParser() parser.feed(html_content) script_sources = parser.script_sources or [DEFAULT_POW_SCRIPT] data_build = parser.data_build if not data_build: match = re.search(r']*data-build="([^"]*)"', html_content) if match: data_build = match.group(1) return script_sources, data_build def _legacy_parse_time() -> str: now = datetime.now(timezone(timedelta(hours=-5))) return now.strftime("%a %b %d %Y %H:%M:%S") + " GMT-0500 (Eastern Standard Time)" def build_pow_config( user_agent: str, script_sources: Sequence[str] | None = None, data_build: str = "", ) -> list[Any]: navigator_key = random.choice([ "registerProtocolHandler−function registerProtocolHandler() { [native code] }", "storage−[object StorageManager]", "locks−[object LockManager]", "appCodeName−Mozilla", "permissions−[object Permissions]", "share−function share() { [native code] }", "webdriver−false", "managed−[object NavigatorManagedData]", "canShare−function canShare() { [native code] }", "vendor−Google Inc.", "mediaDevices−[object MediaDevices]", "vibrate−function vibrate() { [native code] }", "storageBuckets−[object StorageBucketManager]", "mediaCapabilities−[object MediaCapabilities]", "cookieEnabled−true", "virtualKeyboard−[object VirtualKeyboard]", "product−Gecko", "presentation−[object Presentation]", "onLine−true", "mimeTypes−[object MimeTypeArray]", "credentials−[object CredentialsContainer]", "serviceWorker−[object ServiceWorkerContainer]", "keyboard−[object Keyboard]", "gpu−[object GPU]", "doNotTrack", "serial−[object Serial]", "pdfViewerEnabled−true", "language−zh-CN", "geolocation−[object Geolocation]", "userAgentData−[object NavigatorUAData]", "getUserMedia−function getUserMedia() { [native code] }", "sendBeacon−function sendBeacon() { [native code] }", "hardwareConcurrency−32", "windowControlsOverlay−[object WindowControlsOverlay]", ]) window_key = random.choice([ "0", "window", "self", "document", "name", "location", "customElements", "history", "navigation", "innerWidth", "innerHeight", "scrollX", "scrollY", "visualViewport", "screenX", "screenY", "outerWidth", "outerHeight", "devicePixelRatio", "screen", "chrome", "navigator", "onresize", "performance", "crypto", "indexedDB", "sessionStorage", "localStorage", "scheduler", "alert", "atob", "btoa", "fetch", "matchMedia", "postMessage", "queueMicrotask", "requestAnimationFrame", "setInterval", "setTimeout", "caches", "__NEXT_DATA__", "__BUILD_MANIFEST", "__NEXT_PRELOADREADY", ]) script_source = random.choice(list(script_sources)) if script_sources else DEFAULT_POW_SCRIPT return [ random.choice([3000, 4000, 5000]), _legacy_parse_time(), 4294705152, 0, user_agent, script_source, data_build, "en-US", "en-US,es-US,en,es", 0, navigator_key, random.choice(DOCUMENT_KEYS), window_key, time.perf_counter() * 1000, new_uuid(), "", random.choice(CORES), time.time() * 1000 - (time.perf_counter() * 1000), ] def _pow_generate(seed: str, difficulty: str, config: list[Any], limit: int = 500000) -> tuple[str, bool]: target = bytes.fromhex(difficulty) diff_len = len(difficulty) // 2 seed_bytes = seed.encode() static_1 = (json.dumps(config[:3], separators=(",", ":"), ensure_ascii=False)[:-1] + ",").encode() static_2 = ("," + json.dumps(config[4:9], separators=(",", ":"), ensure_ascii=False)[1:-1] + ",").encode() static_3 = ("," + json.dumps(config[10:], separators=(",", ":"), ensure_ascii=False)[1:]).encode() for i in range(limit): final_json = static_1 + str(i).encode() + static_2 + str(i >> 1).encode() + static_3 encoded = pybase64.b64encode(final_json) digest = hashlib.sha3_512(seed_bytes + encoded).digest() if digest[:diff_len] <= target: return encoded.decode(), True fallback = "wQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D" + pybase64.b64encode(f'"{seed}"'.encode()).decode() return fallback, False def build_legacy_requirements_token( user_agent: str, script_sources: Sequence[str] | None = None, data_build: str = "", ) -> str: seed = format(random.random()) config = build_pow_config(user_agent, script_sources=script_sources, data_build=data_build) answer, _ = _pow_generate(seed, "0fffff", config) return "gAAAAAC" + answer def build_proof_token( seed: str, difficulty: str, user_agent: str, script_sources: Sequence[str] | None = None, data_build: str = "", ) -> str: config = build_pow_config(user_agent, script_sources=script_sources, data_build=data_build) answer, solved = _pow_generate(seed, difficulty, config) if not solved: raise RuntimeError(f"failed to solve proof token: difficulty={difficulty}") return "gAAAAAB" + answer