diff --git a/.gitignore b/.gitignore
index 5edfdb1..cad2b74 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,5 @@ nul
.claude/settings.local.json
autogptplus_drission.py
+autogptplus_drission_oai.py
+accounts.json
diff --git a/api_register.py b/api_register.py
new file mode 100644
index 0000000..541b5c8
--- /dev/null
+++ b/api_register.py
@@ -0,0 +1,624 @@
+"""
+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 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
+
+ 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
+ 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: 验证码,失败返回空字符串
+ """
+ log_status("API监听", "正在监听邮件...")
+ headers = {"Authorization": mail_api_token, "Content-Type": "application/json"}
+ start_time = time.time()
+
+ for i in range(max_retries):
+ 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)
+ 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
+ """
+ def log_cb(msg):
+ if progress_callback:
+ progress_callback(msg)
+ 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.register(email, password):
+ log_cb("[X] 注册失败")
+ return None
+ log_cb("[OK] 账户注册成功")
+
+ log_status("API注册", "发送验证邮件...")
+ if not reg.send_verification_email():
+ log_cb("[X] 发送验证邮件失败")
+ return None
+ log_cb("[OK] 验证邮件已发送")
+
+ # 获取验证码
+ otp_code = get_verification_code_api(email, mail_api_base, mail_api_token)
+ if not otp_code:
+ log_cb("[X] 未能获取验证码")
+ return None
+
+ log_status("API注册", f"验证 OTP: {otp_code}")
+ if not reg.validate_otp(otp_code):
+ log_cb("[X] OTP 验证失败")
+ return None
+ log_cb("[OK] OTP 验证成功")
+
+ log_status("API注册", "创建账户...")
+ if not reg.create_account(real_name, birthdate):
+ log_cb("[X] 创建账户失败")
+ return None
+ log_cb("[OK] 账户创建成功")
+
+ # 验证 session 是否有效
+ token = reg.get_session_token()
+ if token:
+ log_cb(f"[OK] Session 有效,Token: {token[:30]}...")
+ else:
+ log_cb("[!] 注册完成但 session 可能未完全建立")
+
+ return reg
+
+ 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)
+ 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)
+ 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注册", "创建账户...")
+ 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
diff --git a/auto_gpt_team.py b/auto_gpt_team.py
index 68d5d22..9aeee9f 100644
--- a/auto_gpt_team.py
+++ b/auto_gpt_team.py
@@ -2,7 +2,11 @@
Author: muyyg
Project: Subscription Automation (DrissionPage Version)
Created: 2026-01-12
-Version: 3.0-drission
+Version: 3.1-hybrid (支持协议模式)
+
+模式说明:
+- browser: 浏览器自动化模式 (默认),全程使用 DrissionPage 浏览器自动化
+- api: 协议模式,使用 API 快速完成注册,仅支付环节使用浏览器
"""
import time
@@ -13,10 +17,27 @@ import sys
import os
import platform
import subprocess
+import tempfile
import requests
from pathlib import Path
from DrissionPage import ChromiumPage, ChromiumOptions
+# 导入协议模式模块
+try:
+ from api_register import (
+ ChatGPTAPIRegister,
+ api_register_flow,
+ api_login_flow,
+ is_api_mode_available,
+ get_verification_code_api,
+ )
+ API_MODE_AVAILABLE = is_api_mode_available()
+except ImportError:
+ API_MODE_AVAILABLE = False
+ ChatGPTAPIRegister = None
+ api_register_flow = None
+ api_login_flow = None
+
# ================= 配置加载 =================
try:
import tomllib
@@ -63,6 +84,13 @@ SEPA_IBANS = _autogptplus.get("sepa_ibans", [])
# 6. 随机指纹开关
RANDOM_FINGERPRINT = _autogptplus.get("random_fingerprint", True)
+# 7. 注册模式: "browser" (浏览器自动化) 或 "api" (协议模式)
+# 默认使用 API 模式(更快),如果 curl_cffi 不可用则自动回退到浏览器模式
+REGISTER_MODE = _autogptplus.get("register_mode", "api")
+
+# 8. 协议模式代理 (仅协议模式使用)
+API_PROXY = _autogptplus.get("api_proxy", "")
+
# ================= 浏览器指纹 =================
FINGERPRINTS = [
# NVIDIA 显卡
@@ -1198,6 +1226,166 @@ def run_payment_flow(page, email, step_callback=None):
log_status("错误", f"[X] 支付流程异常: {e}")
return None
+
+def browser_pay_with_cookies(reg, email: str, proxy: str = None, headless: bool = True, step_callback=None):
+ """使用 API 会话的 cookies 注入浏览器完成支付 (协议模式专用)
+
+ Args:
+ reg: ChatGPTAPIRegister 对象
+ email: 邮箱
+ proxy: 代理地址
+ headless: 是否无头模式
+ step_callback: 步骤回调
+
+ Returns:
+ dict: {"token": ..., "account_id": ...} 或 None
+ """
+ def step_cb(step):
+ if step_callback:
+ step_callback(step)
+
+ step_cb("获取支付页 URL...")
+
+ # 通过 API 获取支付页 URL
+ checkout_url = reg.get_checkout_url()
+ if not checkout_url:
+ log_status("失败", "无法获取支付页 URL")
+ return None
+
+ log_progress(f"[OK] 支付页: {checkout_url[:60]}...")
+
+ # 获取 cookies
+ cookies = reg.get_cookies()
+ log_status("Cookie", f"获取到 {len(cookies)} 个 cookies")
+
+ # 启动浏览器
+ step_cb("启动浏览器...")
+ temp_user_data = tempfile.mkdtemp(prefix="chrome_api_")
+
+ # 检测操作系统
+ is_linux = platform.system() == "Linux"
+
+ # 获取随机指纹
+ fingerprint = None
+ if RANDOM_FINGERPRINT:
+ fingerprint = get_random_fingerprint()
+ else:
+ fingerprint = {
+ "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36",
+ "platform": "Win32",
+ "webgl_vendor": "Google Inc. (NVIDIA)",
+ "webgl_renderer": "ANGLE (NVIDIA, NVIDIA GeForce RTX 3060 Direct3D11 vs_5_0 ps_5_0)",
+ "screen": {"width": 1920, "height": 1080}
+ }
+
+ co = ChromiumOptions()
+ co.set_argument('--no-first-run')
+ co.set_argument('--no-default-browser-check')
+ co.set_argument(f'--user-data-dir={temp_user_data}')
+ co.set_argument('--disable-blink-features=AutomationControlled')
+ co.set_argument('--disable-infobars')
+ co.set_argument('--disable-dev-shm-usage')
+ co.set_argument('--no-sandbox')
+ co.set_argument(f'--user-agent={fingerprint["user_agent"]}')
+
+ screen = fingerprint.get("screen", {"width": 1920, "height": 1080})
+
+ if headless:
+ co.set_argument('--headless=new')
+ co.set_argument(f'--window-size={screen["width"]},{screen["height"]}')
+ else:
+ co.set_argument(f'--window-size={screen["width"]},{screen["height"]}')
+
+ if proxy:
+ co.set_argument(f'--proxy-server={proxy}')
+
+ if is_linux:
+ co.set_argument('--disable-software-rasterizer')
+ co.set_argument('--disable-extensions')
+ co.set_argument('--disable-setuid-sandbox')
+ co.set_argument('--single-process')
+ co.set_argument('--remote-debugging-port=0')
+ chrome_paths = [
+ '/usr/bin/google-chrome',
+ '/usr/bin/google-chrome-stable',
+ '/usr/bin/chromium-browser',
+ '/usr/bin/chromium',
+ '/snap/bin/chromium',
+ ]
+ for chrome_path in chrome_paths:
+ if os.path.exists(chrome_path):
+ co.set_browser_path(chrome_path)
+ break
+ else:
+ co.auto_port(True)
+ co.set_local_port(random.randint(19222, 29999))
+
+ log_status("浏览器", f"正在启动 ({'无头' if headless else '有头'}模式)...")
+ page = ChromiumPage(co)
+
+ try:
+ # 注入指纹
+ if RANDOM_FINGERPRINT:
+ inject_fingerprint(page, fingerprint)
+
+ # 先访问 chatgpt.com 注入 cookies
+ step_cb("注入登录状态...")
+ log_status("Cookie", "注入登录状态...")
+ page.get("https://chatgpt.com")
+
+ injected_count = 0
+ for cookie in cookies:
+ try:
+ if 'chatgpt.com' in cookie.get('domain', ''):
+ page.set.cookies({
+ 'name': cookie['name'],
+ 'value': cookie['value'],
+ 'domain': cookie['domain'].lstrip('.'),
+ 'path': cookie.get('path', '/'),
+ })
+ injected_count += 1
+ except:
+ pass
+
+ log_progress(f"[OK] 已注入 {injected_count} 个 cookies")
+
+ # 刷新页面确保 cookies 生效
+ time.sleep(1)
+ page.refresh()
+ time.sleep(1)
+
+ # 直接跳转到支付页
+ step_cb("跳转到支付页...")
+ log_status("订阅", "跳转到支付页...")
+ page.get(checkout_url)
+
+ try:
+ page.wait.url_change('pay.openai.com', timeout=15)
+ log_progress("✓ 已跳转到支付页")
+ except:
+ time.sleep(2)
+
+ # 执行支付流程
+ step_cb("执行 SEPA 支付...")
+ result = run_payment_flow(page, email, step_cb)
+ return result
+
+ except Exception as e:
+ log_status("错误", f"浏览器流程异常: {e}")
+ return None
+ finally:
+ try:
+ page.quit()
+ except:
+ pass
+ # 清理临时目录
+ try:
+ import shutil
+ shutil.rmtree(temp_user_data, ignore_errors=True)
+ except:
+ pass
+
+
def fetch_account_id(page, access_token: str) -> str:
"""通过 API 获取 account_id (使用 requests 直接请求,更快)"""
log_status("获取", "正在获取 account_id...")
@@ -2013,5 +2201,220 @@ def run_single_registration(progress_callback=None, step_callback=None) -> dict:
cleanup_chrome_processes()
+def run_single_registration_api(progress_callback=None, step_callback=None, proxy: str = None) -> dict:
+ """执行单次注册流程 - 协议模式 (API + Cookie 注入浏览器)
+
+ Args:
+ progress_callback: 进度回调函数 (message: str)
+ step_callback: 步骤回调函数 (step: str)
+ proxy: 代理地址
+
+ Returns:
+ dict: {"success": bool, "account": str, "password": str, "token": str, "account_id": str, "error": str}
+ """
+ def log_cb(msg):
+ if progress_callback:
+ progress_callback(msg)
+ log_progress(msg)
+
+ def step_cb(step):
+ if step_callback:
+ step_callback(step)
+
+ # 检查协议模式是否可用
+ if not API_MODE_AVAILABLE:
+ return {"success": False, "error": "协议模式不可用,请安装 curl_cffi: pip install curl_cffi"}
+
+ # 检查必要配置
+ if not MAIL_API_TOKEN or not MAIL_API_BASE:
+ return {"success": False, "error": "配置错误: 请在 config.toml 中配置 [autogptplus] 段"}
+
+ # 检查域名
+ domains = get_email_domains()
+ if not domains:
+ return {"success": False, "error": "没有可用的邮箱域名,请先通过 /domain_add 导入"}
+
+ # 检查 IBAN
+ ibans = get_sepa_ibans()
+ if not ibans:
+ return {"success": False, "error": "没有可用的 IBAN,请先通过 /iban_add 导入"}
+
+ step_cb("生成账号信息...")
+
+ # 生成账号信息
+ random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=15))
+ email_domain = random.choice(domains)
+ email = f"{random_str}{email_domain}"
+ password = ''.join(random.choices(string.ascii_uppercase, k=2)) + \
+ ''.join(random.choices(string.ascii_lowercase, k=8)) + \
+ ''.join(random.choices(string.digits, k=2)) + \
+ random.choice('!@#$%')
+ real_name = f"{random.choice(FIRST_NAMES)} {random.choice(LAST_NAMES)}"
+
+ # 生成生日
+ year = random.randint(2000, 2004)
+ month = random.randint(1, 12)
+ if month in [1, 3, 5, 7, 8, 10, 12]:
+ max_day = 31
+ elif month in [4, 6, 9, 11]:
+ max_day = 30
+ else:
+ max_day = 29 if year % 4 == 0 else 28
+ day = random.randint(1, max_day)
+ birthdate = f"{year}-{month:02d}-{day:02d}"
+
+ log_status("初始化", f"生成账号: {email}")
+ log_status("初始化", f"设置密码: {password}")
+ log_status("初始化", f"姓名: {real_name} | 生日: {birthdate}")
+ log_status("模式", "协议模式 (API + Cookie 注入)")
+
+ # 使用配置的代理或传入的代理
+ use_proxy = proxy or API_PROXY or None
+ if use_proxy:
+ log_status("代理", f"使用代理: {use_proxy}")
+
+ try:
+ # 阶段 1: API 注册
+ step_cb("API 快速注册...")
+ log_status("阶段 1", "========== API 快速注册 ==========")
+
+ reg = api_register_flow(
+ email=email,
+ password=password,
+ real_name=real_name,
+ birthdate=birthdate,
+ mail_api_base=MAIL_API_BASE,
+ mail_api_token=MAIL_API_TOKEN,
+ proxy=use_proxy,
+ progress_callback=log_cb
+ )
+
+ if not reg:
+ log_status("失败", "API 注册失败")
+ return {"success": False, "error": "API 注册失败", "account": email, "password": password}
+
+ log_status("完成", "[OK] API 注册成功!")
+
+ # 阶段 2: Cookie 注入浏览器 + 支付
+ step_cb("Cookie 注入浏览器...")
+ log_status("阶段 2", "========== Cookie 注入浏览器 + 订阅支付 ==========")
+
+ result = browser_pay_with_cookies(
+ reg=reg,
+ email=email,
+ proxy=use_proxy,
+ headless=True,
+ step_callback=step_cb
+ )
+
+ if result and result.get("stopped"):
+ log_status("停止", "⚠ 注册被用户停止")
+ return {"success": False, "error": "用户停止", "stopped": True, "account": email, "password": password}
+ elif result and result.get("token"):
+ step_cb("注册成功!")
+ log_status("完成", "✓ 全部流程完成!")
+ return {
+ "success": True,
+ "account": email,
+ "password": password,
+ "token": result["token"],
+ "account_id": result.get("account_id", "")
+ }
+
+ # 如果 Cookie 注入失败,尝试 API 登录方式
+ log_status("重试", "Cookie 注入失败,尝试 API 登录...")
+ step_cb("尝试 API 登录...")
+
+ reg2 = api_login_flow(
+ email=email,
+ password=password,
+ proxy=use_proxy,
+ progress_callback=log_cb
+ )
+
+ if reg2:
+ result = browser_pay_with_cookies(
+ reg=reg2,
+ email=email,
+ proxy=use_proxy,
+ headless=True,
+ step_callback=step_cb
+ )
+
+ if result and result.get("token"):
+ step_cb("注册成功!")
+ log_status("完成", "✓ 全部流程完成!")
+ return {
+ "success": True,
+ "account": email,
+ "password": password,
+ "token": result["token"],
+ "account_id": result.get("account_id", "")
+ }
+
+ log_status("失败", "注册成功但支付/获取token失败")
+ return {"success": False, "error": "支付流程失败", "account": email, "password": password}
+
+ except Exception as e:
+ error_msg = str(e)
+ if _is_connection_lost(error_msg) or _is_shutdown_requested():
+ log_status("停止", "⚠ 注册被用户停止")
+ return {"success": False, "error": "用户停止", "stopped": True, "account": email, "password": password}
+ log_status("错误", f"注册异常: {e}")
+ return {"success": False, "error": str(e), "account": email, "password": password}
+ finally:
+ cleanup_chrome_processes()
+
+
+def run_single_registration_auto(progress_callback=None, step_callback=None, mode: str = None) -> dict:
+ """自动选择模式执行注册
+
+ Args:
+ progress_callback: 进度回调
+ step_callback: 步骤回调
+ mode: 强制指定模式 ("browser" / "api"),None 则使用配置
+
+ Returns:
+ dict: 注册结果
+ """
+ use_mode = mode or REGISTER_MODE
+
+ if use_mode == "api":
+ if not API_MODE_AVAILABLE:
+ log_status("警告", "协议模式不可用,回退到浏览器模式")
+ return run_single_registration(progress_callback, step_callback)
+ return run_single_registration_api(progress_callback, step_callback)
+ else:
+ return run_single_registration(progress_callback, step_callback)
+
+
+def get_register_mode() -> str:
+ """获取当前注册模式"""
+ return REGISTER_MODE
+
+
+def set_register_mode(mode: str) -> bool:
+ """设置注册模式 (运行时)
+
+ Args:
+ mode: "browser" 或 "api"
+
+ Returns:
+ bool: 是否设置成功
+ """
+ global REGISTER_MODE
+ if mode in ("browser", "api"):
+ if mode == "api" and not API_MODE_AVAILABLE:
+ return False
+ REGISTER_MODE = mode
+ return True
+ return False
+
+
+def is_api_mode_supported() -> bool:
+ """检查协议模式是否支持"""
+ return API_MODE_AVAILABLE
+
+
if __name__ == "__main__":
run_main_process()
diff --git a/browser_automation.py b/browser_automation.py
index eef05b7..7134cc3 100644
--- a/browser_automation.py
+++ b/browser_automation.py
@@ -1,6 +1,7 @@
# ==================== 浏览器自动化模块 ====================
# 处理 OpenAI 注册、Codex 授权等浏览器自动化操作
# 使用 DrissionPage 替代 Selenium
+# 支持协议模式 (API) 和浏览器模式
import time
import random
@@ -34,6 +35,17 @@ from s2a_service import (
)
from logger import log
+# 导入协议模式模块
+try:
+ from api_register import (
+ api_register_account_only,
+ is_api_mode_available as _is_api_mode_available,
+ )
+ API_MODE_AVAILABLE = _is_api_mode_available()
+except ImportError:
+ API_MODE_AVAILABLE = False
+ api_register_account_only = None
+
# 进度更新 (Telegram Bot 使用)
try:
from bot_notifier import progress_update
@@ -945,6 +957,91 @@ def is_logged_in(page, timeout: int = 5) -> bool:
return False
+def register_openai_account_api(email: str, password: str, proxy: str = None) -> bool:
+ """使用协议模式 (API) 注册 OpenAI 账号
+
+ Args:
+ email: 邮箱地址
+ password: 密码
+ proxy: 代理地址 (可选)
+
+ Returns:
+ bool: 是否成功
+ """
+ if not API_MODE_AVAILABLE:
+ log.warning("协议模式不可用,回退到浏览器模式")
+ return None # 返回 None 表示需要回退
+
+ log.info(f"[API模式] 开始注册 OpenAI 账号: {email}", icon="account")
+
+ # 生成随机姓名和生日
+ random_name = get_random_name()
+ birthday = get_random_birthday()
+ birthdate = f"{birthday['year']}-{birthday['month']}-{birthday['day']}"
+
+ log.step(f"姓名: {random_name}, 生日: {birthdate}")
+
+ # 定义获取验证码的函数
+ def get_code(target_email):
+ progress_update(phase="注册", step="等待验证码...")
+ log.step("等待验证码邮件...")
+ code, error, email_time = unified_get_verification_code(target_email)
+ if code:
+ log.success(f"获取到验证码: {code}")
+ return code
+
+ # 执行 API 注册
+ try:
+ result = api_register_account_only(
+ email=email,
+ password=password,
+ real_name=random_name,
+ birthdate=birthdate,
+ get_verification_code_func=get_code,
+ proxy=proxy,
+ progress_callback=lambda msg: log.step(msg)
+ )
+
+ if result:
+ log.success(f"[API模式] 注册完成: {email}")
+ return True
+ else:
+ log.warning("[API模式] 注册失败,可能需要回退到浏览器模式")
+ return False
+
+ except Exception as e:
+ log.error(f"[API模式] 注册异常: {e}")
+ return False
+
+
+def register_openai_account_auto(page, email: str, password: str, use_api: bool = True, proxy: str = None) -> bool:
+ """自动选择模式注册 OpenAI 账号
+
+ 优先使用 API 模式,失败则回退到浏览器模式
+
+ Args:
+ page: 浏览器实例 (用于浏览器模式回退)
+ email: 邮箱地址
+ password: 密码
+ use_api: 是否优先使用 API 模式
+ proxy: 代理地址 (API 模式使用)
+
+ Returns:
+ bool: 是否成功
+ """
+ # 如果启用 API 模式且可用
+ if use_api and API_MODE_AVAILABLE:
+ result = register_openai_account_api(email, password, proxy)
+ if result is True:
+ return True
+ elif result is False:
+ log.warning("API 模式注册失败,回退到浏览器模式...")
+ # result is None 表示 API 模式不可用,直接使用浏览器模式
+
+ # 使用浏览器模式
+ return register_openai_account(page, email, password)
+
+
def register_openai_account(page, email: str, password: str) -> bool:
"""使用浏览器注册 OpenAI 账号
@@ -1976,12 +2073,13 @@ def login_and_authorize_with_otp(email: str) -> tuple[bool, dict]:
return False, None
-def register_and_authorize(email: str, password: str) -> tuple:
+def register_and_authorize(email: str, password: str, use_api_register: bool = True) -> tuple:
"""完整流程: 注册 OpenAI + Codex 授权 (带重试机制)
Args:
email: 邮箱地址
password: 密码
+ use_api_register: 是否优先使用 API 模式注册 (默认 True)
Returns:
tuple: (register_success, codex_data)
@@ -1992,8 +2090,11 @@ def register_and_authorize(email: str, password: str) -> tuple:
with browser_context_with_retry(max_browser_retries=2) as ctx:
for attempt in ctx.attempts():
try:
- # 注册 OpenAI
- register_result = register_openai_account(ctx.page, email, password)
+ # 注册 OpenAI (优先使用 API 模式)
+ register_result = register_openai_account_auto(
+ ctx.page, email, password,
+ use_api=use_api_register
+ )
# 检查是否是域名黑名单错误
if register_result == "domain_blacklisted":
diff --git a/config.toml.example b/config.toml.example
index fc0821e..1b555e1 100644
--- a/config.toml.example
+++ b/config.toml.example
@@ -234,3 +234,12 @@ email_domains = ["@example.com", "@example.org"]
sepa_ibans = []
# 是否启用随机指纹 (User-Agent, WebGL, 分辨率等)
random_fingerprint = true
+
+# 注册模式选择:
+# - "api": 协议模式 (默认),使用 API 快速完成注册,仅支付环节使用浏览器
+# 协议模式更快,需要安装 curl_cffi: pip install curl_cffi
+# - "browser": 浏览器自动化模式,全程使用 DrissionPage 浏览器自动化
+register_mode = "api"
+
+# 协议模式代理 (仅协议模式使用,格式: http://127.0.0.1:7890)
+api_proxy = ""
diff --git a/pyproject.toml b/pyproject.toml
index 31b584b..48181f2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,6 +5,7 @@ description = "OpenAI Team 账号自动批量注册 & CRS 入库工具"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
+ "curl-cffi>=0.14.0",
"drissionpage>=4.1.1.2",
"python-telegram-bot[job-queue]>=22.5",
"requests>=2.32.5",
diff --git a/requirements.txt b/requirements.txt
index 898208d..45c5b21 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,3 +4,6 @@ requests>=2.32.5
rich>=14.2.0
setuptools>=80.9.0
tomli>=2.3.0
+
+# 协议模式依赖 (可选,用于 API 快速注册)
+curl_cffi>=0.7.0
diff --git a/telegram_bot.py b/telegram_bot.py
index bd521cb..4925087 100644
--- a/telegram_bot.py
+++ b/telegram_bot.py
@@ -2862,7 +2862,7 @@ class ProvisionerBot:
async def _run_team_registration(self, chat_id: int, count: int, output_type: str):
"""执行 GPT Team 注册任务"""
- from auto_gpt_team import run_single_registration, cleanup_chrome_processes
+ from auto_gpt_team import run_single_registration_auto, cleanup_chrome_processes, get_register_mode
import json
import threading
@@ -2870,6 +2870,10 @@ class ProvisionerBot:
success_count = 0
fail_count = 0
+ # 获取当前注册模式
+ current_mode = get_register_mode()
+ mode_display = "🌐 协议模式" if current_mode == "api" else "🖥️ 浏览器模式"
+
# 当前步骤 (用于显示)
current_step = ["初始化..."]
current_account = [""]
@@ -2884,6 +2888,7 @@ class ProvisionerBot:
progress_msg = await self.app.bot.send_message(
chat_id,
f"🚀 开始注册\n\n"
+ f"模式: {mode_display}\n"
f"进度: 0/{count}\n"
f"{'▱' * 20}",
parse_mode="HTML"
@@ -2908,6 +2913,7 @@ class ProvisionerBot:
text = (
f"🚀 注册中...\n\n"
+ f"模式: {mode_display}\n"
f"进度: {success_count + fail_count}/{count}\n"
f"{progress_bar}\n\n"
f"✅ 成功: {success_count}\n"
@@ -2947,7 +2953,7 @@ class ProvisionerBot:
import functools
def run_with_callback():
- return run_single_registration(
+ return run_single_registration_auto(
progress_callback=None,
step_callback=step_callback
)
@@ -3094,7 +3100,16 @@ class ProvisionerBot:
def _get_autogptplus_main_keyboard(self):
"""获取 AutoGPTPlus 主菜单键盘"""
- return InlineKeyboardMarkup([
+ # 检查协议模式是否可用
+ try:
+ from auto_gpt_team import is_api_mode_supported, get_register_mode
+ api_supported = is_api_mode_supported()
+ current_mode = get_register_mode()
+ except ImportError:
+ api_supported = False
+ current_mode = "browser"
+
+ keyboard = [
[
InlineKeyboardButton("📋 查看配置", callback_data="autogptplus:config"),
InlineKeyboardButton("🔑 设置 Token", callback_data="autogptplus:set_token"),
@@ -3111,10 +3126,20 @@ class ProvisionerBot:
InlineKeyboardButton("📧 测试邮件", callback_data="autogptplus:test_email"),
InlineKeyboardButton("🔄 测试 API", callback_data="autogptplus:test_api"),
],
- [
- InlineKeyboardButton("🚀 开始注册", callback_data="autogptplus:register"),
- ],
+ ]
+
+ # 添加注册模式选择按钮
+ mode_icon = "🌐" if current_mode == "api" else "🖥️"
+ mode_text = "协议模式" if current_mode == "api" else "浏览器模式"
+ keyboard.append([
+ InlineKeyboardButton(f"⚙️ 注册模式: {mode_icon} {mode_text}", callback_data="autogptplus:select_mode"),
])
+
+ keyboard.append([
+ InlineKeyboardButton("🚀 开始注册", callback_data="autogptplus:register"),
+ ])
+
+ return InlineKeyboardMarkup(keyboard)
async def callback_autogptplus(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""处理 AutoGPTPlus 回调"""
@@ -3148,6 +3173,11 @@ class ProvisionerBot:
await self._toggle_autogptplus_fingerprint(query)
elif action == "stats":
await self._show_autogptplus_stats(query)
+
+ elif action == "select_mode":
+ await self._show_autogptplus_mode_selection(query)
+ elif action == "set_mode":
+ await self._set_autogptplus_mode(query, sub_action)
elif action == "register":
await self._start_autogptplus_register(query, context)
elif action == "back":
@@ -3203,7 +3233,8 @@ class ProvisionerBot:
try:
from auto_gpt_team import (
MAIL_API_TOKEN, MAIL_API_BASE, EMAIL_DOMAINS,
- SEPA_IBANS, RANDOM_FINGERPRINT, get_email_domains, get_sepa_ibans
+ SEPA_IBANS, RANDOM_FINGERPRINT, get_email_domains, get_sepa_ibans,
+ REGISTER_MODE, API_PROXY, is_api_mode_supported
)
# 脱敏显示 Token
@@ -3226,6 +3257,19 @@ class ProvisionerBot:
# 随机指纹状态
fingerprint_status = "✅ 已开启" if RANDOM_FINGERPRINT else "❌ 已关闭"
+ # 注册模式
+ api_supported = is_api_mode_supported()
+ if REGISTER_MODE == "api":
+ mode_display = "🌐 协议模式 (API)"
+ else:
+ mode_display = "🖥️ 浏览器模式"
+
+ if not api_supported:
+ mode_display += " (协议模式不可用)"
+
+ # 代理配置
+ proxy_display = API_PROXY if API_PROXY else "未配置"
+
lines = [
"📋 AutoGPTPlus 配置",
"",
@@ -3240,8 +3284,10 @@ class ProvisionerBot:
"💳 SEPA IBAN",
f" 数量: {len(ibans)} 个",
"",
- "🎭 随机指纹",
- f" 状态: {fingerprint_status}",
+ "⚙️ 注册设置",
+ f" 模式: {mode_display}",
+ f" 随机指纹: {fingerprint_status}",
+ f" API 代理: {proxy_display}",
]
# 配置状态检查
@@ -3731,6 +3777,208 @@ class ProvisionerBot:
reply_markup=reply_markup
)
+ async def _show_autogptplus_mode_selection(self, query):
+ """显示注册模式选择界面"""
+ try:
+ from auto_gpt_team import is_api_mode_supported, get_register_mode
+
+ api_supported = is_api_mode_supported()
+ current_mode = get_register_mode()
+
+ lines = [
+ "⚙️ 选择注册模式\n",
+ "请选择 ChatGPT Team 注册使用的方式:\n",
+ ]
+
+ # 浏览器模式说明
+ browser_check = "✅" if current_mode == "browser" else "⬜"
+ lines.append(f"{browser_check} 🖥️ 浏览器模式")
+ lines.append("全程使用 DrissionPage 浏览器自动化")
+ lines.append("• 兼容性好,无需额外依赖")
+ lines.append("• 速度较慢,资源占用较高")
+ lines.append("")
+
+ # 协议模式说明
+ api_check = "✅" if current_mode == "api" else "⬜"
+ api_status = "" if api_supported else " (不可用)"
+ lines.append(f"{api_check} 🌐 协议模式{api_status}")
+ lines.append("使用 API 快速注册,仅支付环节用浏览器")
+ lines.append("• 速度快,资源占用少")
+ lines.append("• 需要 curl_cffi 依赖")
+ if not api_supported:
+ lines.append("• 请安装: pip install curl_cffi")
+
+ # 构建按钮
+ keyboard = []
+
+ # 浏览器模式按钮
+ browser_icon = "✅" if current_mode == "browser" else "🖥️"
+ keyboard.append([
+ InlineKeyboardButton(
+ f"{browser_icon} 浏览器模式",
+ callback_data="autogptplus:set_mode:browser"
+ )
+ ])
+
+ # 协议模式按钮
+ if api_supported:
+ api_icon = "✅" if current_mode == "api" else "🌐"
+ keyboard.append([
+ InlineKeyboardButton(
+ f"{api_icon} 协议模式 (推荐)",
+ callback_data="autogptplus:set_mode:api"
+ )
+ ])
+ else:
+ keyboard.append([
+ InlineKeyboardButton(
+ "🌐 协议模式 (需安装依赖)",
+ callback_data="autogptplus:set_mode:api_unavailable"
+ )
+ ])
+
+ keyboard.append([
+ InlineKeyboardButton("◀️ 返回", callback_data="autogptplus:back")
+ ])
+
+ reply_markup = InlineKeyboardMarkup(keyboard)
+
+ await query.edit_message_text(
+ "\n".join(lines),
+ parse_mode="HTML",
+ reply_markup=reply_markup
+ )
+
+ except ImportError as e:
+ keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="autogptplus:back")]]
+ reply_markup = InlineKeyboardMarkup(keyboard)
+ await query.edit_message_text(
+ f"❌ 模块导入失败\n\n{e}",
+ parse_mode="HTML",
+ reply_markup=reply_markup
+ )
+ except Exception as e:
+ keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="autogptplus:back")]]
+ reply_markup = InlineKeyboardMarkup(keyboard)
+ await query.edit_message_text(
+ f"❌ 获取配置失败\n\n{e}",
+ parse_mode="HTML",
+ reply_markup=reply_markup
+ )
+
+ async def _set_autogptplus_mode(self, query, mode: str):
+ """设置注册模式"""
+ import tomli_w
+
+ try:
+ from auto_gpt_team import is_api_mode_supported, get_register_mode, set_register_mode
+
+ # 处理协议模式不可用的情况
+ if mode == "api_unavailable":
+ keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="autogptplus:select_mode")]]
+ reply_markup = InlineKeyboardMarkup(keyboard)
+ await query.edit_message_text(
+ "❌ 协议模式不可用\n\n"
+ "需要安装 curl_cffi 依赖:\n"
+ "pip install curl_cffi\n\n"
+ "安装后重启程序即可使用协议模式",
+ parse_mode="HTML",
+ reply_markup=reply_markup
+ )
+ return
+
+ # 检查是否已经是当前模式
+ current_mode = get_register_mode()
+ if mode == current_mode:
+ await query.answer(f"当前已是{'协议' if mode == 'api' else '浏览器'}模式", show_alert=False)
+ return
+
+ # 检查协议模式是否可用
+ if mode == "api" and not is_api_mode_supported():
+ keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="autogptplus:select_mode")]]
+ reply_markup = InlineKeyboardMarkup(keyboard)
+ await query.edit_message_text(
+ "❌ 协议模式不可用\n\n"
+ "需要安装 curl_cffi 依赖:\n"
+ "pip install curl_cffi\n\n"
+ "安装后重启程序即可使用协议模式",
+ parse_mode="HTML",
+ reply_markup=reply_markup
+ )
+ return
+
+ # 读取当前配置
+ with open(CONFIG_FILE, "rb") as f:
+ import tomllib
+ config = tomllib.load(f)
+
+ # 确保 autogptplus section 存在
+ if "autogptplus" not in config:
+ config["autogptplus"] = {}
+
+ # 更新配置
+ config["autogptplus"]["register_mode"] = mode
+
+ # 写回文件
+ with open(CONFIG_FILE, "wb") as f:
+ tomli_w.dump(config, f)
+
+ # 更新运行时配置
+ set_register_mode(mode)
+
+ # 重新加载模块
+ import importlib
+ import auto_gpt_team
+ importlib.reload(auto_gpt_team)
+
+ # 显示成功消息
+ if mode == "api":
+ mode_name = "🌐 协议模式"
+ mode_desc = (
+ "使用 API 快速完成注册流程,仅支付环节使用浏览器\n\n"
+ "特点:\n"
+ "• 注册速度更快\n"
+ "• 资源占用更少\n"
+ "• 更稳定可靠"
+ )
+ else:
+ mode_name = "🖥️ 浏览器模式"
+ mode_desc = (
+ "全程使用 DrissionPage 浏览器自动化\n\n"
+ "特点:\n"
+ "• 兼容性更好\n"
+ "• 无需额外依赖\n"
+ "• 可视化调试方便"
+ )
+
+ keyboard = [[InlineKeyboardButton("◀️ 返回主菜单", callback_data="autogptplus:back")]]
+ reply_markup = InlineKeyboardMarkup(keyboard)
+
+ await query.edit_message_text(
+ f"✅ 注册模式已设置\n\n"
+ f"当前模式: {mode_name}\n\n"
+ f"{mode_desc}",
+ parse_mode="HTML",
+ reply_markup=reply_markup
+ )
+
+ except ImportError as e:
+ keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="autogptplus:back")]]
+ reply_markup = InlineKeyboardMarkup(keyboard)
+ await query.edit_message_text(
+ f"❌ 模块导入失败\n\n{e}",
+ parse_mode="HTML",
+ reply_markup=reply_markup
+ )
+ except Exception as e:
+ keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="autogptplus:back")]]
+ reply_markup = InlineKeyboardMarkup(keyboard)
+ await query.edit_message_text(
+ f"❌ 设置失败\n\n{e}",
+ parse_mode="HTML",
+ reply_markup=reply_markup
+ )
+
async def _show_autogptplus_stats(self, query):
"""显示统计信息"""
try:
diff --git a/uv.lock b/uv.lock
index 4e3c5cf..ba7de27 100644
--- a/uv.lock
+++ b/uv.lock
@@ -36,6 +36,63 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/7d/9bc192684cea499815ff478dfcdc13835ddf401365057044fb721ec6bddb/certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b", size = 159438, upload-time = "2025-11-12T02:54:49.735Z" },
]
+[[package]]
+name = "cffi"
+version = "2.0.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "pycparser", marker = "implementation_name != 'PyPy'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ea/47/4f61023ea636104d4f16ab488e268b93008c3d0bb76893b1b31db1f96802/cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d", size = 185271, upload-time = "2025-09-08T23:22:44.795Z" },
+ { url = "https://files.pythonhosted.org/packages/df/a2/781b623f57358e360d62cdd7a8c681f074a71d445418a776eef0aadb4ab4/cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c", size = 181048, upload-time = "2025-09-08T23:22:45.938Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/95/7a135d52a50dfa7c882ab0ac17e8dc11cec9d55d2c18dda414c051c5e69e/cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e", size = 207983, upload-time = "2025-09-08T23:22:50.06Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/c8/15cb9ada8895957ea171c62dc78ff3e99159ee7adb13c0123c001a2546c1/cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037", size = 206519, upload-time = "2025-09-08T23:22:51.364Z" },
+ { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" },
+ { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/2b/2b6435f76bfeb6bbf055596976da087377ede68df465419d192acf00c437/cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18", size = 172932, upload-time = "2025-09-08T23:22:57.188Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/ed/13bd4418627013bec4ed6e54283b1959cf6db888048c7cf4b4c3b5b36002/cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5", size = 183557, upload-time = "2025-09-08T23:22:58.351Z" },
+ { url = "https://files.pythonhosted.org/packages/95/31/9f7f93ad2f8eff1dbc1c3656d7ca5bfd8fb52c9d786b4dcf19b2d02217fa/cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6", size = 177762, upload-time = "2025-09-08T23:22:59.668Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" },
+ { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" },
+ { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" },
+ { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" },
+ { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" },
+ { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
+ { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
+ { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
+ { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
+ { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
+ { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
+ { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
+]
+
[[package]]
name = "charset-normalizer"
version = "3.4.4"
@@ -123,6 +180,29 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ee/58/257350f7db99b4ae12b614a36256d9cc870d71d9e451e79c2dc3b23d7c3c/cssselect-1.3.0-py3-none-any.whl", hash = "sha256:56d1bf3e198080cc1667e137bc51de9cadfca259f03c2d4e09037b3e01e30f0d", size = 18786, upload-time = "2025-03-10T09:30:28.048Z" },
]
+[[package]]
+name = "curl-cffi"
+version = "0.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "certifi" },
+ { name = "cffi" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/9b/c9/0067d9a25ed4592b022d4558157fcdb6e123516083700786d38091688767/curl_cffi-0.14.0.tar.gz", hash = "sha256:5ffbc82e59f05008ec08ea432f0e535418823cda44178ee518906a54f27a5f0f", size = 162633, upload-time = "2025-12-16T03:25:07.931Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/aa/f0/0f21e9688eaac85e705537b3a87a5588d0cefb2f09d83e83e0e8be93aa99/curl_cffi-0.14.0-cp39-abi3-macosx_14_0_arm64.whl", hash = "sha256:e35e89c6a69872f9749d6d5fda642ed4fc159619329e99d577d0104c9aad5893", size = 3087277, upload-time = "2025-12-16T03:24:49.607Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/a3/0419bd48fce5b145cb6a2344c6ac17efa588f5b0061f212c88e0723da026/curl_cffi-0.14.0-cp39-abi3-macosx_15_0_x86_64.whl", hash = "sha256:5945478cd28ad7dfb5c54473bcfb6743ee1d66554d57951fdf8fc0e7d8cf4e45", size = 5804650, upload-time = "2025-12-16T03:24:51.518Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/07/a238dd062b7841b8caa2fa8a359eb997147ff3161288f0dd46654d898b4d/curl_cffi-0.14.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c42e8fa3c667db9ccd2e696ee47adcd3cd5b0838d7282f3fc45f6c0ef3cfdfa7", size = 8231918, upload-time = "2025-12-16T03:24:52.862Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/d2/ce907c9b37b5caf76ac08db40cc4ce3d9f94c5500db68a195af3513eacbc/curl_cffi-0.14.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:060fe2c99c41d3cb7f894de318ddf4b0301b08dca70453d769bd4e74b36b8483", size = 8654624, upload-time = "2025-12-16T03:24:54.579Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/ae/6256995b18c75e6ef76b30753a5109e786813aa79088b27c8eabb1ef85c9/curl_cffi-0.14.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b158c41a25388690dd0d40b5bc38d1e0f512135f17fdb8029868cbc1993d2e5b", size = 8010654, upload-time = "2025-12-16T03:24:56.507Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/10/ff64249e516b103cb762e0a9dca3ee0f04cf25e2a1d5d9838e0f1273d071/curl_cffi-0.14.0-cp39-abi3-manylinux_2_28_i686.whl", hash = "sha256:1439fbef3500fb723333c826adf0efb0e2e5065a703fb5eccce637a2250db34a", size = 7781969, upload-time = "2025-12-16T03:24:57.885Z" },
+ { url = "https://files.pythonhosted.org/packages/51/76/d6f7bb76c2d12811aa7ff16f5e17b678abdd1b357b9a8ac56310ceccabd5/curl_cffi-0.14.0-cp39-abi3-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e7176f2c2d22b542e3cf261072a81deb018cfa7688930f95dddef215caddb469", size = 7969133, upload-time = "2025-12-16T03:24:59.261Z" },
+ { url = "https://files.pythonhosted.org/packages/23/7c/cca39c0ed4e1772613d3cba13091c0e9d3b89365e84b9bf9838259a3cd8f/curl_cffi-0.14.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:03f21ade2d72978c2bb8670e9b6de5260e2755092b02d94b70b906813662998d", size = 9080167, upload-time = "2025-12-16T03:25:00.946Z" },
+ { url = "https://files.pythonhosted.org/packages/75/03/a942d7119d3e8911094d157598ae0169b1c6ca1bd3f27d7991b279bcc45b/curl_cffi-0.14.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:58ebf02de64ee5c95613209ddacb014c2d2f86298d7080c0a1c12ed876ee0690", size = 9520464, upload-time = "2025-12-16T03:25:02.922Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/77/78900e9b0833066d2274bda75cba426fdb4cef7fbf6a4f6a6ca447607bec/curl_cffi-0.14.0-cp39-abi3-win_amd64.whl", hash = "sha256:6e503f9a103f6ae7acfb3890c843b53ec030785a22ae7682a22cc43afb94123e", size = 1677416, upload-time = "2025-12-16T03:25:04.902Z" },
+ { url = "https://files.pythonhosted.org/packages/5c/7c/d2ba86b0b3e1e2830bd94163d047de122c69a8df03c5c7c36326c456ad82/curl_cffi-0.14.0-cp39-abi3-win_arm64.whl", hash = "sha256:2eed50a969201605c863c4c31269dfc3e0da52916086ac54553cfa353022425c", size = 1425067, upload-time = "2025-12-16T03:25:06.454Z" },
+]
+
[[package]]
name = "datarecorder"
version = "3.6.2"
@@ -337,6 +417,7 @@ name = "oai-team-auto-provisioner"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
+ { name = "curl-cffi" },
{ name = "drissionpage" },
{ name = "python-telegram-bot", extra = ["job-queue"] },
{ name = "requests" },
@@ -348,6 +429,7 @@ dependencies = [
[package.metadata]
requires-dist = [
+ { name = "curl-cffi", specifier = ">=0.14.0" },
{ name = "drissionpage", specifier = ">=4.1.1.2" },
{ name = "python-telegram-bot", extras = ["job-queue"], specifier = ">=22.5" },
{ name = "requests", specifier = ">=2.32.5" },
@@ -395,6 +477,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c9/ad/33b2ccec09bf96c2b2ef3f9a6f66baac8253d7565d8839e024a6b905d45d/psutil-7.1.3-cp37-abi3-win_arm64.whl", hash = "sha256:bd0d69cee829226a761e92f28140bec9a5ee9d5b4fb4b0cc589068dbfff559b1", size = 244608, upload-time = "2025-11-02T12:26:36.136Z" },
]
+[[package]]
+name = "pycparser"
+version = "3.0"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
+]
+
[[package]]
name = "pygments"
version = "2.19.2"