协议
This commit is contained in:
405
auto_gpt_team.py
405
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()
|
||||
|
||||
Reference in New Issue
Block a user