""" ChatGPT API 注册模块 (协议模式) - 使用 curl_cffi 通过 API 快速完成注册 - 支持 Cookie 注入到浏览器完成支付 """ import time import random import re import uuid from urllib.parse import unquote try: from curl_cffi import requests as curl_requests CURL_CFFI_AVAILABLE = True except ImportError: CURL_CFFI_AVAILABLE = False curl_requests = None import requests def _is_shutdown_requested(): """检查是否收到停止请求""" try: import run return run._shutdown_requested except Exception: return False class ShutdownRequested(Exception): """用户请求停止异常""" pass def log_status(step, message): """日志输出""" timestamp = time.strftime("%H:%M:%S") print(f"[{timestamp}] [{step}] {message}") def log_progress(message): """进度输出""" print(f" {message}") def request_with_retry(func, *args, max_retries=3, **kwargs): """带重试的请求""" for i in range(max_retries): try: return func(*args, **kwargs) except Exception as e: if i == max_retries - 1: raise e time.sleep(1) class ChatGPTAPIRegister: """ChatGPT API 注册类 (协议模式)""" def __init__(self, proxy=None): """初始化 Args: proxy: 代理地址,如 "http://127.0.0.1:7890" """ if not CURL_CFFI_AVAILABLE: raise ImportError("协议模式需要安装 curl_cffi: pip install curl_cffi") self.session = curl_requests.Session( impersonate="edge", verify=False, proxies={"http": proxy, "https": proxy} if proxy else {} ) self.auth_session_logging_id = str(uuid.uuid4()) self.oai_did = "" self.csrf_token = "" self.authorize_url = "" self.access_token = "" def init_session(self) -> bool: """初始化会话,获取必要的 cookies 和 tokens""" try: resp = request_with_retry(self.session.get, "https://chatgpt.com") if resp.status_code != 200: log_progress(f"[X] 初始化失败: HTTP {resp.status_code}") return False self.oai_did = self.session.cookies.get("oai-did") csrf_cookie = self.session.cookies.get("__Host-next-auth.csrf-token") if csrf_cookie: self.csrf_token = unquote(csrf_cookie).split("|")[0] else: log_progress("[X] 未获取到 CSRF token") return False # 访问登录页面 request_with_retry( self.session.get, f"https://chatgpt.com/auth/login?openaicom-did={self.oai_did}" ) return True except Exception as e: log_progress(f"[X] 初始化异常: {e}") return False def get_authorize_url(self, email: str) -> bool: """获取授权 URL""" try: url = f"https://chatgpt.com/api/auth/signin/openai?prompt=login&ext-oai-did={self.oai_did}&auth_session_logging_id={self.auth_session_logging_id}&screen_hint=login_or_signup&login_hint={email}" payload = { "callbackUrl": "https://chatgpt.com/", "csrfToken": self.csrf_token, "json": "true" } resp = request_with_retry( self.session.post, url, data=payload, headers={"Origin": "https://chatgpt.com"} ) data = resp.json() if data.get("url") and "auth.openai.com" in data["url"]: self.authorize_url = data["url"] return True log_progress(f"[X] 授权 URL 无效: {data}") return False except Exception as e: log_progress(f"[X] 获取授权 URL 异常: {e}") return False def start_authorize(self) -> bool: """启动授权流程""" try: resp = request_with_retry( self.session.get, self.authorize_url, allow_redirects=True ) return "create-account" in resp.url or "log-in" in resp.url except Exception as e: log_progress(f"[X] 启动授权异常: {e}") return False def register(self, email: str, password: str) -> bool: """注册账户""" try: resp = request_with_retry( self.session.post, "https://auth.openai.com/api/accounts/user/register", json={"password": password, "username": email}, headers={ "Content-Type": "application/json", "Origin": "https://auth.openai.com" } ) if resp.status_code == 200: return True log_progress(f"[X] 注册失败: {resp.status_code} - {resp.text[:200]}") return False except Exception as e: log_progress(f"[X] 注册异常: {e}") return False def send_verification_email(self) -> bool: """发送验证邮件""" try: resp = request_with_retry( self.session.get, "https://auth.openai.com/api/accounts/email-otp/send", allow_redirects=True ) return resp.status_code == 200 except Exception as e: log_progress(f"[X] 发送验证邮件异常: {e}") return False def validate_otp(self, otp_code: str) -> bool: """验证 OTP 码""" try: resp = request_with_retry( self.session.post, "https://auth.openai.com/api/accounts/email-otp/validate", json={"code": otp_code}, headers={ "Content-Type": "application/json", "Origin": "https://auth.openai.com" } ) if resp.status_code == 200: return True log_progress(f"[X] OTP 验证失败: {resp.status_code} - {resp.text[:200]}") return False except Exception as e: log_progress(f"[X] OTP 验证异常: {e}") return False def create_account(self, name: str, birthdate: str) -> bool: """创建账户 (填写姓名和生日) Args: name: 姓名 birthdate: 生日,格式 "YYYY-MM-DD" """ try: resp = request_with_retry( self.session.post, "https://auth.openai.com/api/accounts/create_account", json={"name": name, "birthdate": birthdate}, headers={ "Content-Type": "application/json", "Origin": "https://auth.openai.com" } ) if resp.status_code != 200: log_progress(f"[X] 创建账户失败: {resp.status_code} - {resp.text[:200]}") return False # 检查响应是否为空 resp_text = resp.text.strip() if not resp_text: log_progress("[X] 创建账户失败: 服务器返回空响应") return False try: data = resp.json() except Exception as json_err: log_progress(f"[X] 创建账户失败: JSON 解析错误 - {json_err}") return False continue_url = data.get("continue_url") if continue_url: request_with_retry(self.session.get, continue_url, allow_redirects=True) return True except Exception as e: log_progress(f"[X] 创建账户异常: {e}") return False def login(self, email: str, password: str) -> bool: """使用密码登录 (复用注册时建立的会话)""" try: resp = request_with_retry( self.session.post, "https://auth.openai.com/api/accounts/password/verify", json={"username": email, "password": password}, headers={ "Content-Type": "application/json", "Origin": "https://auth.openai.com" } ) if resp.status_code == 200: data = resp.json() continue_url = data.get("continue_url") if continue_url: request_with_retry(self.session.get, continue_url, allow_redirects=True) return True log_progress(f"[X] 登录失败: {resp.status_code} - {resp.text[:200]}") return False except Exception as e: log_progress(f"[X] 登录异常: {e}") return False def get_session_token(self) -> str: """获取 access token""" try: resp = request_with_retry(self.session.get, "https://chatgpt.com/api/auth/session") if resp.status_code == 200: data = resp.json() token = data.get("accessToken") if token: self.access_token = token return token log_progress(f"[X] Session 响应无 token: {str(data)[:200]}") else: log_progress(f"[X] Session 请求失败: {resp.status_code}") return "" except Exception as e: log_progress(f"[X] 获取 token 异常: {e}") return "" def get_checkout_url(self) -> str: """通过 API 获取支付页 URL""" try: token = self.access_token or self.get_session_token() if not token: log_progress("[X] 无法获取 access token") return "" payload = { "plan_name": "chatgptteamplan", "team_plan_data": { "workspace_name": "Sepa", "price_interval": "month", "seat_quantity": 5 }, "billing_details": { "country": "DE", "currency": "EUR" }, "promo_campaign": { "promo_campaign_id": "team-1-month-free", "is_coupon_from_query_param": True }, "checkout_ui_mode": "redirect" } resp = request_with_retry( self.session.post, "https://chatgpt.com/backend-api/payments/checkout", json=payload, headers={ "Authorization": f"Bearer {token}", "Content-Type": "application/json", "Origin": "https://chatgpt.com" } ) if resp.status_code == 200: data = resp.json() checkout_url = data.get("url") if checkout_url: return checkout_url log_progress(f"[X] 响应无 URL: {resp.text[:200]}") else: log_progress(f"[X] 获取支付页失败: {resp.status_code} - {resp.text[:200]}") return "" except Exception as e: log_progress(f"[X] 获取支付页异常: {e}") return "" def get_cookies(self) -> list: """获取所有 cookies 用于注入浏览器""" cookies = [] for cookie in self.session.cookies.jar: cookies.append({ "name": cookie.name, "value": cookie.value, "domain": cookie.domain, "path": cookie.path or "/", "secure": cookie.secure, }) return cookies def get_verification_code_api(target_email: str, mail_api_base: str, mail_api_token: str, max_retries: int = 90) -> str: """通过 API 获取验证码 Args: target_email: 目标邮箱 mail_api_base: 邮件 API 地址 mail_api_token: 邮件 API Token max_retries: 最大重试次数 Returns: str: 验证码,失败返回空字符串 Raises: ShutdownRequested: 用户请求停止时抛出 """ log_status("API监听", "正在监听邮件...") headers = {"Authorization": mail_api_token, "Content-Type": "application/json"} start_time = time.time() for i in range(max_retries): # 检查停止请求 if _is_shutdown_requested(): log_status("停止", "[!] 检测到停止请求,中断邮件监听") raise ShutdownRequested("用户请求停止") elapsed = int(time.time() - start_time) try: url = f"{mail_api_base}/api/public/emailList" payload = {"toEmail": target_email, "timeSort": "desc", "size": 20} resp = requests.post(url, headers=headers, json=payload, timeout=10) if resp.status_code == 200: data = resp.json() if data.get('code') == 200: mails = data.get('data', []) if mails: for mail in mails: html_body = mail.get('content') or mail.get('text') or str(mail) code_match = re.search(r'\b(\d{6})\b', html_body) if code_match: code = code_match.group(1) # 如果是已知的无效验证码,跳过继续等待新的 if code == "783500": log_status("跳过", f"[!] 检测到旧验证码 {code},继续等待新验证码...") continue log_status("捕获", f"[OK] 提取到验证码: {code}") return code except Exception: pass if i % 5 == 0: print(f" [监听中] 已耗时 {elapsed}秒...") time.sleep(2) log_status("超时", "[X] 未能获取验证码") return "" def api_register_flow( email: str, password: str, real_name: str, birthdate: str, mail_api_base: str, mail_api_token: str, proxy: str = None, progress_callback=None ) -> ChatGPTAPIRegister: """执行 API 注册流程 Args: email: 邮箱 password: 密码 real_name: 姓名 birthdate: 生日 (YYYY-MM-DD) mail_api_base: 邮件 API 地址 mail_api_token: 邮件 API Token proxy: 代理地址 progress_callback: 进度回调 Returns: ChatGPTAPIRegister: 成功返回 reg 对象,失败返回 None Raises: ShutdownRequested: 用户请求停止时抛出 """ def log_cb(msg): if progress_callback: progress_callback(msg) else: log_progress(msg) def check_shutdown(): """检查停止请求""" if _is_shutdown_requested(): log_cb("[!] 检测到停止请求") raise ShutdownRequested("用户请求停止") reg = ChatGPTAPIRegister(proxy=proxy) try: check_shutdown() log_status("API注册", "初始化会话...") if not reg.init_session(): log_cb("[X] 初始化失败") return None log_cb("[OK] 会话初始化成功") check_shutdown() log_status("API注册", "获取授权 URL...") if not reg.get_authorize_url(email): log_cb("[X] 获取授权 URL 失败") return None log_cb("[OK] 授权 URL 获取成功") check_shutdown() log_status("API注册", "开始授权流程...") if not reg.start_authorize(): log_cb("[X] 授权流程启动失败") return None log_cb("[OK] 授权流程已启动") check_shutdown() log_status("API注册", "注册账户...") if not reg.register(email, password): log_cb("[X] 注册失败") return None log_cb("[OK] 账户注册成功") check_shutdown() log_status("API注册", "发送验证邮件...") if not reg.send_verification_email(): log_cb("[X] 发送验证邮件失败") return None log_cb("[OK] 验证邮件已发送") check_shutdown() # 获取验证码 otp_code = get_verification_code_api(email, mail_api_base, mail_api_token) if not otp_code: log_cb("[X] 未能获取验证码") return None check_shutdown() log_status("API注册", f"验证 OTP: {otp_code}") if not reg.validate_otp(otp_code): log_cb("[X] OTP 验证失败") return None log_cb("[OK] OTP 验证成功") check_shutdown() # 创建账户(带重试) log_status("API注册", "创建账户...") create_success = reg.create_account(real_name, birthdate) # 如果创建失败,重新获取验证码再试一次 if not create_success: check_shutdown() log_cb("[!] 创建账户失败,尝试重新验证...") # 重新发送验证邮件 log_status("API注册", "重新发送验证邮件...") if not reg.send_verification_email(): log_cb("[X] 重新发送验证邮件失败") return None log_cb("[OK] 验证邮件已重新发送") check_shutdown() # 重新获取验证码 time.sleep(2) # 等待新邮件 otp_code = get_verification_code_api(email, mail_api_base, mail_api_token) if not otp_code: log_cb("[X] 未能获取新验证码") return None check_shutdown() log_status("API注册", f"重新验证 OTP: {otp_code}") if not reg.validate_otp(otp_code): log_cb("[X] OTP 重新验证失败") return None log_cb("[OK] OTP 重新验证成功") check_shutdown() # 再次尝试创建账户 log_status("API注册", "重新创建账户...") if not reg.create_account(real_name, birthdate): log_cb("[X] 创建账户仍然失败") return None log_cb("[OK] 账户创建成功") check_shutdown() # 验证 session 是否有效 token = reg.get_session_token() if token: log_cb(f"[OK] Session 有效,Token: {token[:30]}...") else: log_cb("[!] 注册完成但 session 可能未完全建立") return reg except ShutdownRequested: raise # 重新抛出停止请求异常 except Exception as e: log_status("错误", f"注册异常: {e}") return None def api_login_flow( email: str, password: str, proxy: str = None, progress_callback=None ) -> ChatGPTAPIRegister: """执行 API 登录流程 Args: email: 邮箱 password: 密码 proxy: 代理地址 progress_callback: 进度回调 Returns: ChatGPTAPIRegister: 成功返回 reg 对象,失败返回 None """ def log_cb(msg): if progress_callback: progress_callback(msg) else: log_progress(msg) reg = ChatGPTAPIRegister(proxy=proxy) try: log_status("API登录", "初始化会话...") if not reg.init_session(): log_cb("[X] 初始化失败") return None log_cb("[OK] 初始化成功") log_status("API登录", "获取授权 URL...") if not reg.get_authorize_url(email): log_cb("[X] 获取授权 URL 失败") return None log_cb("[OK] 获取授权 URL 成功") log_status("API登录", "开始授权流程...") if not reg.start_authorize(): log_cb("[X] 授权流程失败") return None log_cb("[OK] 授权流程成功") log_status("API登录", "密码验证...") if not reg.login(email, password): log_cb("[X] 登录失败") return None log_cb("[OK] 登录成功") # 获取 token token = reg.get_session_token() if token: log_status("API登录", f"Token: {token[:50]}...") return reg except Exception as e: log_status("错误", f"登录异常: {e}") return None def is_api_mode_available() -> bool: """检查协议模式是否可用""" return CURL_CFFI_AVAILABLE def api_register_account_only( email: str, password: str, real_name: str, birthdate: str, get_verification_code_func, proxy: str = None, progress_callback=None ) -> bool: """仅执行 API 注册流程(不含支付,用于邀请邮箱注册) Args: email: 邮箱 password: 密码 real_name: 姓名 birthdate: 生日 (YYYY-MM-DD) get_verification_code_func: 获取验证码的函数,签名: func(email) -> str proxy: 代理地址 progress_callback: 进度回调 Returns: bool: 是否注册成功 """ def log_cb(msg): if progress_callback: progress_callback(msg) else: log_progress(msg) if not CURL_CFFI_AVAILABLE: log_status("错误", "协议模式不可用,请安装 curl_cffi") return False reg = ChatGPTAPIRegister(proxy=proxy) try: log_status("API注册", "初始化会话...") if not reg.init_session(): log_cb("[X] 初始化失败") return False log_cb("[OK] 会话初始化成功") log_status("API注册", "获取授权 URL...") if not reg.get_authorize_url(email): log_cb("[X] 获取授权 URL 失败") return False log_cb("[OK] 授权 URL 获取成功") log_status("API注册", "开始授权流程...") if not reg.start_authorize(): log_cb("[X] 授权流程启动失败") return False log_cb("[OK] 授权流程已启动") log_status("API注册", "注册账户...") if not reg.register(email, password): log_cb("[X] 注册失败") return False log_cb("[OK] 账户注册成功") log_status("API注册", "发送验证邮件...") if not reg.send_verification_email(): log_cb("[X] 发送验证邮件失败") return False log_cb("[OK] 验证邮件已发送") # 使用传入的函数获取验证码 log_status("API注册", "等待验证码...") otp_code = get_verification_code_func(email) if not otp_code: log_cb("[X] 未能获取验证码") return False log_status("API注册", f"验证 OTP: {otp_code}") if not reg.validate_otp(otp_code): log_cb("[X] OTP 验证失败") return False log_cb("[OK] OTP 验证成功") log_status("API注册", "创建账户...") create_success = reg.create_account(real_name, birthdate) # 如果创建失败,重新获取验证码再试一次 if not create_success: log_cb("[!] 创建账户失败,尝试重新验证...") # 重新发送验证邮件 log_status("API注册", "重新发送验证邮件...") if not reg.send_verification_email(): log_cb("[X] 重新发送验证邮件失败") return False log_cb("[OK] 验证邮件已重新发送") # 重新获取验证码 time.sleep(2) # 等待新邮件 otp_code = get_verification_code_func(email) if not otp_code: log_cb("[X] 未能获取新验证码") return False log_status("API注册", f"重新验证 OTP: {otp_code}") if not reg.validate_otp(otp_code): log_cb("[X] OTP 重新验证失败") return False log_cb("[OK] OTP 重新验证成功") # 再次尝试创建账户 log_status("API注册", "重新创建账户...") if not reg.create_account(real_name, birthdate): log_cb("[X] 创建账户仍然失败") return False log_cb("[OK] 账户创建成功") # 验证 session 是否有效 token = reg.get_session_token() if token: log_cb(f"[OK] 注册完成,Token: {token[:30]}...") return True else: log_cb("[!] 注册完成但 session 可能未完全建立") return True # 仍然返回成功,因为注册流程已完成 except Exception as e: log_status("错误", f"注册异常: {e}") return False