fix: pow逻辑

This commit is contained in:
TheSmallHanCat
2026-01-21 01:56:59 +08:00
parent 0cc1c2e32d
commit a93d81bfc0
2 changed files with 97 additions and 25 deletions

View File

@@ -582,7 +582,8 @@ for line in response.iter_lines():
---
## 🙏 致谢
* 感谢 [@庚崽](https://github.com/genz27) 提供的POW验证解决方案
* 感谢 [@星火集市~小鑫学渣(93418328)](http://linggan10s.shop/) 提供的新的pow验证解决方案
感谢所有贡献者和使用者的支持!
---

View File

@@ -1,4 +1,5 @@
"""Sora API client module"""
import asyncio
import base64
import hashlib
import json
@@ -10,6 +11,8 @@ import re
from datetime import datetime, timedelta, timezone
from typing import Optional, Dict, Any, Tuple
from uuid import uuid4
from urllib.request import Request, urlopen, build_opener, ProxyHandler
from urllib.error import HTTPError, URLError
from curl_cffi.requests import AsyncSession
from curl_cffi import CurlMime
from .proxy_manager import ProxyManager
@@ -46,6 +49,27 @@ POW_WINDOW_KEYS = [
"fetch", "setTimeout", "setInterval", "console",
]
# User-Agent pools
DESKTOP_USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
]
MOBILE_USER_AGENTS = [
"Sora/1.2026.007 (Android 15; 24122RKC7C; build 2600700)",
"Sora/1.2026.007 (Android 14; SM-G998B; build 2600700)",
"Sora/1.2026.007 (Android 15; Pixel 8 Pro; build 2600700)",
"Sora/1.2026.007 (Android 14; Pixel 7; build 2600700)",
"Sora/1.2026.007 (Android 15; 2211133C; build 2600700)",
"Sora/1.2026.007 (Android 14; SM-S918B; build 2600700)",
"Sora/1.2026.007 (Android 15; OnePlus 12; build 2600700)",
]
class SoraClient:
"""Sora API client with proxy support"""
@@ -145,6 +169,9 @@ class SoraClient:
if not success:
debug_logger.log_warning("PoW calculation failed, using error token")
if not final_pow_token.endswith("~S"):
final_pow_token = final_pow_token + "~S"
token_payload = {
"p": final_pow_token,
"t": resp.get("turnstile", {}).get("dx", ""),
@@ -154,10 +181,59 @@ class SoraClient:
}
return json.dumps(token_payload, ensure_ascii=False, separators=(",", ":"))
@staticmethod
def _post_json_sync(url: str, headers: dict, payload: dict, timeout: int, proxy: Optional[str]) -> Dict[str, Any]:
data = json.dumps(payload).encode("utf-8")
req = Request(url, data=data, headers=headers, method="POST")
try:
if proxy:
opener = build_opener(ProxyHandler({"http": proxy, "https": proxy}))
resp = opener.open(req, timeout=timeout)
else:
resp = urlopen(req, timeout=timeout)
resp_text = resp.read().decode("utf-8")
if resp.status not in (200, 201):
raise Exception(f"Request failed: {resp.status} {resp_text}")
return json.loads(resp_text)
except HTTPError as exc:
body = exc.read().decode("utf-8", errors="ignore")
raise Exception(f"HTTP Error: {exc.code} {body}") from exc
except URLError as exc:
raise Exception(f"URL Error: {exc}") from exc
async def _nf_create_urllib(self, token: str, payload: dict, sentinel_token: str,
proxy_url: Optional[str], token_id: Optional[int] = None) -> Dict[str, Any]:
url = f"{self.base_url}/nf/create"
user_agent = random.choice(MOBILE_USER_AGENTS)
headers = {
"Authorization": f"Bearer {token}",
"openai-sentinel-token": sentinel_token,
"Content-Type": "application/json",
"User-Agent": user_agent,
"Origin": "https://sora.chatgpt.com",
"Referer": "https://sora.chatgpt.com/",
}
try:
result = await asyncio.to_thread(
self._post_json_sync, url, headers, payload, 30, proxy_url
)
return result
except Exception as e:
debug_logger.log_error(
error_message=f"nf/create request failed: {str(e)}",
status_code=0,
response_text=str(e)
)
raise
async def _generate_sentinel_token(self, token: Optional[str] = None) -> str:
"""Generate openai-sentinel-token by calling /backend-api/sentinel/req and solving PoW"""
req_id = str(uuid4())
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
user_agent = random.choice(DESKTOP_USER_AGENTS)
pow_token = self._get_pow_token(user_agent)
proxy_url = await self.proxy_manager.get_proxy_url()
@@ -175,27 +251,17 @@ class SoraClient:
if token:
headers["Authorization"] = f"Bearer {token}"
async with AsyncSession() as session:
kwargs = {
"headers": headers,
"json": payload,
"timeout": 10,
"impersonate": "chrome"
}
if proxy_url:
kwargs["proxy"] = proxy_url
response = await session.post(url, **kwargs)
if response.status_code not in [200, 201]:
debug_logger.log_error(
error_message=f"Sentinel request failed: {response.status_code}",
status_code=response.status_code,
response_text=response.text
)
raise Exception(f"Sentinel request failed: {response.status_code}")
resp = response.json()
try:
resp = await asyncio.to_thread(
self._post_json_sync, url, headers, payload, 10, proxy_url
)
except Exception as e:
debug_logger.log_error(
error_message=f"Sentinel request failed: {str(e)}",
status_code=0,
response_text=str(e)
)
raise
# Build final sentinel token
sentinel_token = self._build_sentinel_token(
@@ -486,7 +552,9 @@ class SoraClient:
}
# 生成请求需要添加 sentinel token
result = await self._make_request("POST", "/nf/create", token, json_data=json_data, add_sentinel_token=True, token_id=token_id)
proxy_url = await self.proxy_manager.get_proxy_url(token_id)
sentinel_token = await self._generate_sentinel_token(token)
result = await self._nf_create_urllib(token, json_data, sentinel_token, proxy_url, token_id)
return result["id"]
async def get_image_tasks(self, token: str, limit: int = 20, token_id: Optional[int] = None) -> Dict[str, Any]:
@@ -887,7 +955,10 @@ class SoraClient:
"style_id": style_id
}
result = await self._make_request("POST", "/nf/create", token, json_data=json_data, add_sentinel_token=True)
# Generate sentinel token and call /nf/create using urllib
proxy_url = await self.proxy_manager.get_proxy_url()
sentinel_token = await self._generate_sentinel_token(token)
result = await self._nf_create_urllib(token, json_data, sentinel_token, proxy_url)
return result.get("id")
async def generate_storyboard(self, prompt: str, token: str, orientation: str = "landscape",