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""" """Sora API client module"""
import asyncio
import base64 import base64
import hashlib import hashlib
import json import json
@@ -10,6 +11,8 @@ import re
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from typing import Optional, Dict, Any, Tuple from typing import Optional, Dict, Any, Tuple
from uuid import uuid4 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.requests import AsyncSession
from curl_cffi import CurlMime from curl_cffi import CurlMime
from .proxy_manager import ProxyManager from .proxy_manager import ProxyManager
@@ -46,6 +49,27 @@ POW_WINDOW_KEYS = [
"fetch", "setTimeout", "setInterval", "console", "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: class SoraClient:
"""Sora API client with proxy support""" """Sora API client with proxy support"""
@@ -145,6 +169,9 @@ class SoraClient:
if not success: if not success:
debug_logger.log_warning("PoW calculation failed, using error token") 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 = { token_payload = {
"p": final_pow_token, "p": final_pow_token,
"t": resp.get("turnstile", {}).get("dx", ""), "t": resp.get("turnstile", {}).get("dx", ""),
@@ -154,10 +181,59 @@ class SoraClient:
} }
return json.dumps(token_payload, ensure_ascii=False, separators=(",", ":")) 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: async def _generate_sentinel_token(self, token: Optional[str] = None) -> str:
"""Generate openai-sentinel-token by calling /backend-api/sentinel/req and solving PoW""" """Generate openai-sentinel-token by calling /backend-api/sentinel/req and solving PoW"""
req_id = str(uuid4()) 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) pow_token = self._get_pow_token(user_agent)
proxy_url = await self.proxy_manager.get_proxy_url() proxy_url = await self.proxy_manager.get_proxy_url()
@@ -175,27 +251,17 @@ class SoraClient:
if token: if token:
headers["Authorization"] = f"Bearer {token}" headers["Authorization"] = f"Bearer {token}"
async with AsyncSession() as session: try:
kwargs = { resp = await asyncio.to_thread(
"headers": headers, self._post_json_sync, url, headers, payload, 10, proxy_url
"json": payload, )
"timeout": 10, except Exception as e:
"impersonate": "chrome" debug_logger.log_error(
} error_message=f"Sentinel request failed: {str(e)}",
if proxy_url: status_code=0,
kwargs["proxy"] = proxy_url response_text=str(e)
)
response = await session.post(url, **kwargs) raise
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()
# Build final sentinel token # Build final sentinel token
sentinel_token = self._build_sentinel_token( sentinel_token = self._build_sentinel_token(
@@ -486,7 +552,9 @@ class SoraClient:
} }
# 生成请求需要添加 sentinel token # 生成请求需要添加 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"] return result["id"]
async def get_image_tasks(self, token: str, limit: int = 20, token_id: Optional[int] = None) -> Dict[str, Any]: 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 "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") return result.get("id")
async def generate_storyboard(self, prompt: str, token: str, orientation: str = "landscape", async def generate_storyboard(self, prompt: str, token: str, orientation: str = "landscape",