a
This commit is contained in:
190
auto_gpt_team.py
190
auto_gpt_team.py
@@ -161,6 +161,7 @@ def get_random_fingerprint() -> dict:
|
|||||||
|
|
||||||
def inject_fingerprint(page, fingerprint: dict):
|
def inject_fingerprint(page, fingerprint: dict):
|
||||||
"""注入浏览器指纹伪装脚本"""
|
"""注入浏览器指纹伪装脚本"""
|
||||||
|
global _last_log_message, _last_log_time
|
||||||
try:
|
try:
|
||||||
webgl_vendor = fingerprint.get("webgl_vendor", "Google Inc. (NVIDIA)")
|
webgl_vendor = fingerprint.get("webgl_vendor", "Google Inc. (NVIDIA)")
|
||||||
webgl_renderer = fingerprint.get("webgl_renderer", "ANGLE (NVIDIA)")
|
webgl_renderer = fingerprint.get("webgl_renderer", "ANGLE (NVIDIA)")
|
||||||
@@ -204,7 +205,20 @@ def inject_fingerprint(page, fingerprint: dict):
|
|||||||
}});
|
}});
|
||||||
'''
|
'''
|
||||||
page.run_js(js_script)
|
page.run_js(js_script)
|
||||||
log_status("指纹", f"已注入: {webgl_renderer[:40]}...")
|
|
||||||
|
# 获取浏览器语言设置
|
||||||
|
try:
|
||||||
|
browser_lang = page.run_js('return navigator.language || navigator.userLanguage || "unknown"')
|
||||||
|
except:
|
||||||
|
browser_lang = "unknown"
|
||||||
|
|
||||||
|
# 避免重复日志:1秒内相同消息不重复输出
|
||||||
|
current_time = time.time()
|
||||||
|
log_msg = f"已注入: {webgl_renderer} | {screen['width']}x{screen['height']} | 语言: {browser_lang}"
|
||||||
|
if log_msg != _last_log_message or current_time - _last_log_time > 1:
|
||||||
|
log_status("指纹", log_msg)
|
||||||
|
_last_log_message = log_msg
|
||||||
|
_last_log_time = current_time
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_status("指纹", f"注入失败: {e}")
|
log_status("指纹", f"注入失败: {e}")
|
||||||
|
|
||||||
@@ -561,8 +575,14 @@ LAST_NAMES = [
|
|||||||
|
|
||||||
# ================= 工具函数 =================
|
# ================= 工具函数 =================
|
||||||
|
|
||||||
|
# 日志去重缓存
|
||||||
|
_last_log_message = ""
|
||||||
|
_last_log_time = 0
|
||||||
|
|
||||||
|
|
||||||
def cleanup_chrome_processes():
|
def cleanup_chrome_processes():
|
||||||
"""清理残留的 Chrome 进程 (跨平台支持)"""
|
"""清理残留的 Chrome 进程 (跨平台支持)"""
|
||||||
|
global _last_log_message, _last_log_time
|
||||||
try:
|
try:
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
# Windows: 使用 taskkill 清理 chromedriver 和 chrome
|
# Windows: 使用 taskkill 清理 chromedriver 和 chrome
|
||||||
@@ -573,7 +593,7 @@ def cleanup_chrome_processes():
|
|||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 清理无头模式的 chrome 进程 (带 --headless 参数的)
|
# 清理无头模式的 chrome 进程 (带 --headless 参数的)
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
@@ -586,8 +606,6 @@ def cleanup_chrome_processes():
|
|||||||
subprocess.run(['taskkill', '/F', '/PID', pid], capture_output=True, timeout=5)
|
subprocess.run(['taskkill', '/F', '/PID', pid], capture_output=True, timeout=5)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
log_status("清理", "已清理 Chrome 残留进程")
|
|
||||||
else:
|
else:
|
||||||
# Linux/Mac: 使用 pkill
|
# Linux/Mac: 使用 pkill
|
||||||
try:
|
try:
|
||||||
@@ -597,7 +615,7 @@ def cleanup_chrome_processes():
|
|||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 清理无头模式的 chrome 进程
|
# 清理无头模式的 chrome 进程
|
||||||
try:
|
try:
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
@@ -606,8 +624,14 @@ def cleanup_chrome_processes():
|
|||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
log_status("清理", "已清理 Chrome 残留进程")
|
# 避免重复日志:1秒内相同消息不重复输出
|
||||||
|
current_time = time.time()
|
||||||
|
log_msg = "已清理 Chrome 残留进程"
|
||||||
|
if log_msg != _last_log_message or current_time - _last_log_time > 1:
|
||||||
|
log_status("清理", log_msg)
|
||||||
|
_last_log_message = log_msg
|
||||||
|
_last_log_time = current_time
|
||||||
except Exception:
|
except Exception:
|
||||||
pass # 静默处理,不影响主流程
|
pass # 静默处理,不影响主流程
|
||||||
|
|
||||||
@@ -1059,24 +1083,28 @@ def run_payment_flow(page, email, step_callback=None):
|
|||||||
# ========== 步骤 9: 获取 token 和 account_id ==========
|
# ========== 步骤 9: 获取 token 和 account_id ==========
|
||||||
step_cb("获取 Token...")
|
step_cb("获取 Token...")
|
||||||
log_status("获取", "正在获取 access token...")
|
log_status("获取", "正在获取 access token...")
|
||||||
time.sleep(2)
|
time.sleep(1)
|
||||||
page.get("https://chatgpt.com/api/auth/session")
|
page.get("https://chatgpt.com/api/auth/session")
|
||||||
time.sleep(2)
|
time.sleep(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 获取页面内容(JSON)
|
# 获取页面内容(JSON)
|
||||||
session_text = page.ele('tag:pre', timeout=5).text
|
session_text = page.ele('tag:pre', timeout=5).text
|
||||||
import json
|
import json
|
||||||
session_data = json.loads(session_text)
|
session_data = json.loads(session_text)
|
||||||
access_token = session_data.get('accessToken', '')
|
access_token = session_data.get('accessToken', '')
|
||||||
|
|
||||||
if access_token:
|
if access_token:
|
||||||
log_status("成功", f"获取到 token: {access_token[:50]}...")
|
log_status("成功", f"获取到 token: {access_token[:50]}...")
|
||||||
|
|
||||||
# 获取 account_id
|
# 优先从 session 数据直接提取 account_id(最快)
|
||||||
step_cb("获取 Account ID...")
|
step_cb("获取 Account ID...")
|
||||||
account_id = fetch_account_id(page, access_token)
|
account_id = fetch_account_id_from_session(session_data)
|
||||||
|
|
||||||
|
# 如果 session 中没有,再通过 API 获取
|
||||||
|
if not account_id:
|
||||||
|
account_id = fetch_account_id(page, access_token)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"token": access_token,
|
"token": access_token,
|
||||||
"account_id": account_id
|
"account_id": account_id
|
||||||
@@ -1098,30 +1126,25 @@ def run_payment_flow(page, email, step_callback=None):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def fetch_account_id(page, access_token: str) -> str:
|
def fetch_account_id(page, access_token: str) -> str:
|
||||||
"""通过 API 获取 account_id"""
|
"""通过 API 获取 account_id (使用 requests 直接请求,更快)"""
|
||||||
log_status("获取", "正在获取 account_id...")
|
log_status("获取", "正在获取 account_id...")
|
||||||
try:
|
try:
|
||||||
page.get("https://chatgpt.com/backend-api/accounts/check/v4-2023-04-27")
|
# 直接使用 requests 请求 API,比 page.get() + JS fetch 快得多
|
||||||
time.sleep(2)
|
headers = {
|
||||||
|
"Authorization": f"Bearer {access_token}",
|
||||||
# 使用 JS 请求 API
|
"Content-Type": "application/json",
|
||||||
result = page.run_js(f'''
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
|
||||||
return fetch("https://chatgpt.com/backend-api/accounts/check/v4-2023-04-27", {{
|
}
|
||||||
headers: {{
|
resp = requests.get(
|
||||||
"Authorization": "Bearer {access_token}",
|
"https://chatgpt.com/backend-api/accounts/check/v4-2023-04-27",
|
||||||
"Content-Type": "application/json"
|
headers=headers,
|
||||||
}}
|
timeout=10
|
||||||
}})
|
)
|
||||||
.then(r => r.json())
|
|
||||||
.then(data => JSON.stringify(data))
|
if resp.status_code == 200:
|
||||||
.catch(e => "error:" + e);
|
data = resp.json()
|
||||||
''')
|
|
||||||
|
|
||||||
if result and not result.startswith("error:"):
|
|
||||||
import json
|
|
||||||
data = json.loads(result)
|
|
||||||
accounts = data.get("accounts", {})
|
accounts = data.get("accounts", {})
|
||||||
|
|
||||||
# 优先查找 Team 账户
|
# 优先查找 Team 账户
|
||||||
for acc_id, acc_info in accounts.items():
|
for acc_id, acc_info in accounts.items():
|
||||||
if acc_id == "default":
|
if acc_id == "default":
|
||||||
@@ -1131,15 +1154,30 @@ def fetch_account_id(page, access_token: str) -> str:
|
|||||||
if "team" in plan_type.lower():
|
if "team" in plan_type.lower():
|
||||||
log_status("成功", f"获取到 account_id: {acc_id[:8]}...")
|
log_status("成功", f"获取到 account_id: {acc_id[:8]}...")
|
||||||
return acc_id
|
return acc_id
|
||||||
|
|
||||||
# 取第一个非 default 的
|
# 取第一个非 default 的
|
||||||
for acc_id in accounts.keys():
|
for acc_id in accounts.keys():
|
||||||
if acc_id != "default":
|
if acc_id != "default":
|
||||||
log_status("成功", f"获取到 account_id: {acc_id[:8]}...")
|
log_status("成功", f"获取到 account_id: {acc_id[:8]}...")
|
||||||
return acc_id
|
return acc_id
|
||||||
|
else:
|
||||||
|
log_progress(f"API 请求失败: {resp.status_code}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_progress(f"获取 account_id 失败: {e}")
|
log_progress(f"获取 account_id 失败: {e}")
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_account_id_from_session(session_data: dict) -> str:
|
||||||
|
"""直接从 session 数据中提取 account_id (最快方式)"""
|
||||||
|
try:
|
||||||
|
account = session_data.get("account", {})
|
||||||
|
account_id = account.get("id", "")
|
||||||
|
if account_id:
|
||||||
|
log_status("成功", f"获取到 account_id: {account_id[:8]}...")
|
||||||
|
return account_id
|
||||||
|
except Exception as e:
|
||||||
|
log_progress(f"从 session 提取 account_id 失败: {e}")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
@@ -1418,43 +1456,29 @@ def run_main_process():
|
|||||||
# =======================================================
|
# =======================================================
|
||||||
log_status("订阅", "等待进入 ChatGPT 主页...")
|
log_status("订阅", "等待进入 ChatGPT 主页...")
|
||||||
|
|
||||||
# 等待页面跳转完成
|
# 快速检测循环(减少等待时间)
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
entered_main = False
|
entered_main = False
|
||||||
for i in range(30):
|
for i in range(20): # 最多等待 10 秒
|
||||||
current_url = page.url
|
current_url = page.url
|
||||||
# 更宽松的判断:只要不在 auth/login 页面就认为进入了主页
|
# 更宽松的判断:只要不在 auth/login 页面就认为进入了主页
|
||||||
if 'chatgpt.com' in current_url:
|
if 'chatgpt.com' in current_url:
|
||||||
if '/auth' not in current_url and '/login' not in current_url and 'auth0' not in current_url:
|
if '/auth' not in current_url and '/login' not in current_url and 'auth0' not in current_url:
|
||||||
log_progress(f"✓ 已进入主页: {current_url[:60]}...")
|
log_progress(f"✓ 已进入主页: {current_url}")
|
||||||
entered_main = True
|
entered_main = True
|
||||||
break
|
break
|
||||||
time.sleep(1)
|
time.sleep(0.5) # 减少等待间隔
|
||||||
if i % 10 == 0 and i > 0:
|
|
||||||
log_progress(f"等待中... ({i}秒)")
|
|
||||||
|
|
||||||
if not entered_main:
|
if not entered_main:
|
||||||
log_progress("⚠ 等待主页超时,尝试继续...")
|
log_progress("⚠ 等待主页超时,尝试继续...")
|
||||||
log_progress(f"当前URL: {page.url}")
|
log_progress(f"当前URL: {page.url}")
|
||||||
# 尝试直接访问主页
|
# 尝试直接访问主页
|
||||||
page.get("https://chatgpt.com/")
|
page.get("https://chatgpt.com/")
|
||||||
time.sleep(3)
|
time.sleep(2)
|
||||||
log_progress(f"跳转后URL: {page.url}")
|
log_progress(f"跳转后URL: {page.url}")
|
||||||
|
|
||||||
# 额外等待页面稳定
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
log_status("订阅", "执行 JS 跳转到支付页...")
|
log_status("订阅", "执行 JS 跳转到支付页...")
|
||||||
|
|
||||||
# 先检查是否已登录
|
|
||||||
try:
|
|
||||||
session_check = page.run_js('return fetch("/api/auth/session").then(r => r.json())')
|
|
||||||
time.sleep(1)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 直接执行 JS 跳转到支付页
|
# 直接执行 JS 跳转到支付页(无需额外等待,JS 会自动获取 session)
|
||||||
checkout_js = '''
|
checkout_js = '''
|
||||||
(async function(){
|
(async function(){
|
||||||
try {
|
try {
|
||||||
@@ -1501,10 +1525,6 @@ def run_main_process():
|
|||||||
'''
|
'''
|
||||||
result = page.run_js(checkout_js)
|
result = page.run_js(checkout_js)
|
||||||
log_progress(f"JS 执行结果: {result}")
|
log_progress(f"JS 执行结果: {result}")
|
||||||
|
|
||||||
# 等待 JS 执行完成
|
|
||||||
time.sleep(2)
|
|
||||||
log_progress(f"当前URL: {page.url}")
|
|
||||||
|
|
||||||
# 等待跳转到支付页(使用 URL 检测代替固定等待)
|
# 等待跳转到支付页(使用 URL 检测代替固定等待)
|
||||||
try:
|
try:
|
||||||
@@ -1512,9 +1532,9 @@ def run_main_process():
|
|||||||
log_progress("✓ 已跳转到支付页")
|
log_progress("✓ 已跳转到支付页")
|
||||||
log_progress(f"支付页URL: {page.url}")
|
log_progress(f"支付页URL: {page.url}")
|
||||||
except:
|
except:
|
||||||
time.sleep(2)
|
time.sleep(1)
|
||||||
log_progress(f"当前URL: {page.url}")
|
log_progress(f"当前URL: {page.url}")
|
||||||
|
|
||||||
# 执行支付流程
|
# 执行支付流程
|
||||||
result = run_payment_flow(page, email)
|
result = run_payment_flow(page, email)
|
||||||
|
|
||||||
@@ -1817,48 +1837,35 @@ def run_single_registration(progress_callback=None, step_callback=None) -> dict:
|
|||||||
if not submit_success:
|
if not submit_success:
|
||||||
log_progress(f"⚠ 提交后仍在 about-you 页面,当前URL: {page.url}")
|
log_progress(f"⚠ 提交后仍在 about-you 页面,当前URL: {page.url}")
|
||||||
|
|
||||||
# 等待进入主页 - 改进检测逻辑
|
# 等待进入主页 - 快速检测
|
||||||
step_cb("等待进入主页...")
|
step_cb("等待进入主页...")
|
||||||
log_status("订阅", "等待进入 ChatGPT 主页...")
|
log_status("订阅", "等待进入 ChatGPT 主页...")
|
||||||
|
|
||||||
# 等待页面跳转完成
|
# 快速检测循环(减少等待时间)
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
entered_main = False
|
entered_main = False
|
||||||
for i in range(30):
|
for i in range(20): # 最多等待 10 秒
|
||||||
current_url = page.url
|
current_url = page.url
|
||||||
# 更宽松的判断:只要不在 auth/login 页面就认为进入了主页
|
# 更宽松的判断:只要不在 auth/login 页面就认为进入了主页
|
||||||
if 'chatgpt.com' in current_url:
|
if 'chatgpt.com' in current_url:
|
||||||
if '/auth' not in current_url and '/login' not in current_url and 'auth0' not in current_url:
|
if '/auth' not in current_url and '/login' not in current_url and 'auth0' not in current_url:
|
||||||
log_progress(f"✓ 已进入主页: {current_url[:60]}...")
|
log_progress(f"✓ 已进入主页: {current_url}")
|
||||||
entered_main = True
|
entered_main = True
|
||||||
break
|
break
|
||||||
time.sleep(1)
|
time.sleep(0.5) # 减少等待间隔
|
||||||
if i % 10 == 0 and i > 0:
|
|
||||||
log_progress(f"等待中... ({i}秒)")
|
|
||||||
|
|
||||||
if not entered_main:
|
if not entered_main:
|
||||||
log_progress("⚠ 等待主页超时,尝试继续...")
|
log_progress("⚠ 等待主页超时,尝试继续...")
|
||||||
log_progress(f"当前URL: {page.url}")
|
log_progress(f"当前URL: {page.url}")
|
||||||
# 尝试直接访问主页
|
# 尝试直接访问主页
|
||||||
page.get("https://chatgpt.com/")
|
page.get("https://chatgpt.com/")
|
||||||
time.sleep(3)
|
time.sleep(2)
|
||||||
log_progress(f"跳转后URL: {page.url}")
|
log_progress(f"跳转后URL: {page.url}")
|
||||||
|
|
||||||
# 额外等待页面稳定
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
# 跳转到支付页
|
# 跳转到支付页
|
||||||
step_cb("跳转到支付页...")
|
step_cb("跳转到支付页...")
|
||||||
log_status("订阅", "执行 JS 跳转到支付页...")
|
log_status("订阅", "执行 JS 跳转到支付页...")
|
||||||
|
|
||||||
# 先检查是否已登录
|
# 直接执行 JS 跳转到支付页(无需额外等待)
|
||||||
try:
|
|
||||||
session_check = page.run_js('return fetch("/api/auth/session").then(r => r.json())')
|
|
||||||
time.sleep(1)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
checkout_js = '''
|
checkout_js = '''
|
||||||
(async function(){
|
(async function(){
|
||||||
try {
|
try {
|
||||||
@@ -1884,17 +1891,14 @@ def run_single_registration(progress_callback=None, step_callback=None) -> dict:
|
|||||||
'''
|
'''
|
||||||
result = page.run_js(checkout_js)
|
result = page.run_js(checkout_js)
|
||||||
log_progress(f"JS 执行结果: {result}")
|
log_progress(f"JS 执行结果: {result}")
|
||||||
|
|
||||||
# 等待 JS 执行完成
|
|
||||||
time.sleep(2)
|
|
||||||
log_progress(f"当前URL: {page.url}")
|
|
||||||
|
|
||||||
|
# 等待跳转到支付页
|
||||||
try:
|
try:
|
||||||
page.wait.url_change('pay.openai.com', timeout=15)
|
page.wait.url_change('pay.openai.com', timeout=15)
|
||||||
log_progress("✓ 已跳转到支付页")
|
log_progress("✓ 已跳转到支付页")
|
||||||
log_progress(f"支付页URL: {page.url}")
|
log_progress(f"支付页URL: {page.url}")
|
||||||
except:
|
except:
|
||||||
time.sleep(2)
|
time.sleep(1)
|
||||||
log_progress(f"当前URL: {page.url}")
|
log_progress(f"当前URL: {page.url}")
|
||||||
|
|
||||||
# 执行支付流程
|
# 执行支付流程
|
||||||
|
|||||||
Reference in New Issue
Block a user