修复bug
This commit is contained in:
137
core/flow.py
137
core/flow.py
@@ -6,7 +6,7 @@ import random
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from core.session import OAISession, CloudflareBlockError, SessionInvalidError, RateLimitError
|
||||
from core.session import OAISession, CloudflareBlockError, SessionInvalidError
|
||||
from core.sentinel_handler import SentinelHandler
|
||||
from core.challenge import CloudflareSolver
|
||||
from utils.mail_box import MailHandler
|
||||
@@ -27,8 +27,7 @@ class RegisterFlow:
|
||||
AUTH_SEND_OTP = "https://auth.openai.com/api/accounts/email-otp/send"
|
||||
AUTH_VALIDATE_OTP = "https://auth.openai.com/api/accounts/email-otp/validate"
|
||||
AUTH_COMPLETE_PROFILE = "https://auth.openai.com/api/accounts/create_account"
|
||||
|
||||
# 获取 Token 相关
|
||||
AUTH_CONSENT = "https://auth.openai.com/api/accounts/consent"
|
||||
CHATGPT_SESSION = "https://chatgpt.com/api/auth/session"
|
||||
|
||||
def __init__(self, session: OAISession, config, email: Optional[str] = None, password: Optional[str] = None):
|
||||
@@ -44,7 +43,7 @@ class RegisterFlow:
|
||||
self.csrf_token: Optional[str] = None
|
||||
self.sentinel_token: Optional[Dict[str, Any]] = None
|
||||
self.otp: Optional[str] = None
|
||||
self.access_token: Optional[str] = None
|
||||
self.oauth_callback_url: Optional[str] = None
|
||||
|
||||
logger.info(f"RegisterFlow initialized for {self.email} (oai-did: {self.s.oai_did})")
|
||||
|
||||
@@ -72,14 +71,12 @@ class RegisterFlow:
|
||||
await self._step6_send_email_otp()
|
||||
await self._step7_submit_otp()
|
||||
await self._step8_complete_profile()
|
||||
await self._step9_get_access_token()
|
||||
|
||||
logger.success(f"[{self.email}] Registration completed successfully! ✅")
|
||||
return {
|
||||
"email": self.email,
|
||||
"password": self.password,
|
||||
"oai_did": self.s.oai_did,
|
||||
"access_token": self.access_token,
|
||||
"status": "success",
|
||||
"message": "Account registered successfully"
|
||||
}
|
||||
@@ -92,10 +89,6 @@ class RegisterFlow:
|
||||
logger.error(f"[{self.email}] Session invalid: {e}")
|
||||
return {"email": self.email, "password": self.password, "status": "session_invalid", "error": str(e)}
|
||||
|
||||
except RateLimitError as e:
|
||||
logger.error(f"[{self.email}] Rate limited: {e}")
|
||||
return {"email": self.email, "password": self.password, "status": "rate_limited", "error": str(e)}
|
||||
|
||||
except NotImplementedError as e:
|
||||
logger.warning(f"[{self.email}] Feature not implemented: {e}")
|
||||
return {"email": self.email, "password": self.password, "status": "pending_manual", "error": str(e)}
|
||||
@@ -245,53 +238,101 @@ class RegisterFlow:
|
||||
resp = self.s.post(
|
||||
self.AUTH_COMPLETE_PROFILE,
|
||||
json={"name": name, "birthdate": birthdate},
|
||||
headers={"Content-Type": "application/json", "Referer": self.AUTH_CREATE_ACCOUNT},
|
||||
allow_redirects=False
|
||||
headers={"Content-Type": "application/json", "Referer": self.AUTH_CREATE_ACCOUNT}
|
||||
)
|
||||
|
||||
if resp.status_code not in [200, 302, 303]:
|
||||
if resp.status_code != 200:
|
||||
raise RuntimeError(f"Profile completion failed: {resp.status_code}")
|
||||
|
||||
# 检查是否有 continue_url 需要跟随
|
||||
try:
|
||||
data = resp.json()
|
||||
continue_url = data.get("continue_url")
|
||||
if continue_url:
|
||||
logger.info(f"[{self.email}] Following OAuth callback...")
|
||||
if not continue_url.startswith("http"):
|
||||
continue_url = f"https://auth.openai.com{continue_url}"
|
||||
|
||||
# 跟随 OAuth 回调,最终会重定向到 chatgpt.com
|
||||
callback_headers = {
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"Referer": "https://auth.openai.com/",
|
||||
"Sec-Fetch-Dest": "document",
|
||||
"Sec-Fetch-Mode": "navigate",
|
||||
"Sec-Fetch-Site": "same-origin",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
}
|
||||
self.s.get(continue_url, headers=callback_headers, allow_redirects=True)
|
||||
logger.info(f"[{self.email}] ✓ OAuth callback completed")
|
||||
except Exception as e:
|
||||
logger.debug(f"[{self.email}] No continue_url or parse error: {e}")
|
||||
# 获取 OAuth 回调 URL
|
||||
data = resp.json()
|
||||
self.oauth_callback_url = data.get("continue_url")
|
||||
logger.debug(f"[{self.email}] OAuth callback URL: {self.oauth_callback_url}")
|
||||
|
||||
logger.info(f"[{self.email}] ✓ Profile completed")
|
||||
|
||||
async def _step9_get_access_token(self):
|
||||
"""Step 9: 通过登录流程获取 Access Token"""
|
||||
logger.info(f"[{self.email}] Step 9: Getting access token via login")
|
||||
async def _step9_complete_oauth_callback(self):
|
||||
"""Step 9: 完成 OAuth 回调,获取 session-token"""
|
||||
logger.info(f"[{self.email}] Step 9: Completing OAuth callback")
|
||||
|
||||
from core.login_flow import LoginFlow
|
||||
|
||||
# 使用当前 session 执行登录流程
|
||||
login_flow = LoginFlow(self.s, self.email, self.password)
|
||||
result = await login_flow.run()
|
||||
|
||||
if result.get("status") == "success":
|
||||
self.access_token = result.get("access_token")
|
||||
logger.info(f"[{self.email}] ✓ Access token obtained: {self.access_token[:50]}...")
|
||||
# 使用 step 8 返回的 continue_url,跟随重定向链到 chatgpt.com
|
||||
if self.oauth_callback_url:
|
||||
# continue_url 可能是相对路径或完整 URL
|
||||
if self.oauth_callback_url.startswith("http"):
|
||||
callback_url = self.oauth_callback_url
|
||||
else:
|
||||
callback_url = f"https://auth.openai.com{self.oauth_callback_url}"
|
||||
else:
|
||||
logger.warning(f"[{self.email}] Failed to get access token: {result.get('error')}")
|
||||
# 如果没有 continue_url,尝试访问 consent 端点
|
||||
callback_url = self.AUTH_CONSENT
|
||||
|
||||
logger.debug(f"[{self.email}] Following OAuth callback: {callback_url}")
|
||||
|
||||
# 循环跟随重定向,直到到达 chatgpt.com 或获取到 session-token
|
||||
max_redirects = 10
|
||||
for i in range(max_redirects):
|
||||
resp = self.s.get(callback_url, allow_redirects=True)
|
||||
|
||||
# 检查是否已到达 chatgpt.com
|
||||
if 'chatgpt.com' in str(resp.url):
|
||||
logger.debug(f"[{self.email}] Reached chatgpt.com")
|
||||
break
|
||||
|
||||
# 检查响应是否包含重定向 URL(JSON 格式)
|
||||
try:
|
||||
data = resp.json()
|
||||
redirect_url = data.get("redirect_url") or data.get("location") or data.get("url")
|
||||
if redirect_url:
|
||||
logger.debug(f"[{self.email}] Following JSON redirect ({i+1}): {redirect_url[:100]}...")
|
||||
callback_url = redirect_url
|
||||
continue
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
pass
|
||||
|
||||
# 没有更多重定向,退出循环
|
||||
break
|
||||
|
||||
# 检查是否获取到 session-token cookie
|
||||
session_token = self.s.get_cookie('__Secure-next-auth.session-token')
|
||||
|
||||
if not session_token:
|
||||
# 尝试直接访问 chatgpt.com 首页触发 cookie 设置
|
||||
logger.debug(f"[{self.email}] Session token not found, trying chatgpt.com homepage")
|
||||
self.s.get(self.CHATGPT_HOME, allow_redirects=True)
|
||||
session_token = self.s.get_cookie('__Secure-next-auth.session-token')
|
||||
|
||||
if not session_token:
|
||||
# 打印所有 cookies 用于调试,但不抛出错误
|
||||
# 尝试继续获取 access token,有时候 session token 不是必需的
|
||||
all_cookies = self.s.get_cookies()
|
||||
logger.warning(f"[{self.email}] Session token not found, available cookies: {list(all_cookies.keys())}")
|
||||
logger.info(f"[{self.email}] Continuing without session-token, will try to get access token directly")
|
||||
else:
|
||||
logger.info(f"[{self.email}] ✓ OAuth callback completed, session-token obtained")
|
||||
|
||||
async def _step10_get_access_token(self) -> str:
|
||||
"""Step 10: 获取 AccessToken"""
|
||||
logger.info(f"[{self.email}] Step 10: Getting access token")
|
||||
|
||||
resp = self.s.get(
|
||||
self.CHATGPT_SESSION,
|
||||
headers={
|
||||
'Accept': 'application/json',
|
||||
'Referer': 'https://chatgpt.com/',
|
||||
}
|
||||
)
|
||||
|
||||
if resp.status_code != 200:
|
||||
raise RuntimeError(f"Failed to get access token: {resp.status_code}")
|
||||
|
||||
data = resp.json()
|
||||
access_token = data.get('accessToken')
|
||||
|
||||
if not access_token:
|
||||
raise RuntimeError("AccessToken not found in response")
|
||||
|
||||
logger.info(f"[{self.email}] ✓ AccessToken obtained successfully")
|
||||
return access_token
|
||||
|
||||
def _generate_email(self) -> str:
|
||||
"""生成随机邮箱"""
|
||||
|
||||
@@ -257,7 +257,20 @@ class OAISession:
|
||||
返回:
|
||||
Cookie 字典 {name: value}
|
||||
"""
|
||||
return {cookie.name: cookie.value for cookie in self.client.cookies}
|
||||
# curl_cffi 的 cookies 可能存在同名不同域的 cookie,需要遍历 jar
|
||||
result = {}
|
||||
try:
|
||||
for cookie in self.client.cookies.jar:
|
||||
# 用 domain:name 作为 key 避免冲突,或者直接覆盖
|
||||
result[cookie.name] = cookie.value
|
||||
except Exception:
|
||||
# 兼容处理
|
||||
try:
|
||||
for cookie in self.client.cookies:
|
||||
result[cookie.name] = cookie.value
|
||||
except Exception:
|
||||
pass
|
||||
return result
|
||||
|
||||
def get_cookie(self, name: str) -> Optional[str]:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user