This commit is contained in:
2026-01-18 05:38:14 +08:00
parent e510c568b4
commit 7e92e12c79
5 changed files with 438 additions and 132 deletions

136
run.py
View File

@@ -26,7 +26,7 @@ from team_service import batch_invite_to_team, print_team_summary, check_availab
from crs_service import crs_add_account, crs_sync_team_owners, crs_verify_token
from cpa_service import cpa_verify_connection
from s2a_service import s2a_verify_connection
from browser_automation import register_and_authorize, login_and_authorize_with_otp, authorize_only, login_and_authorize_team_owner
from browser_automation import register_and_authorize, login_and_authorize_with_otp, authorize_only, login_and_authorize_team_owner, ShutdownRequested
from utils import (
save_to_csv,
load_team_tracker,
@@ -44,13 +44,14 @@ from logger import log
# 进度更新 (Telegram Bot 使用,导入失败时忽略)
try:
from bot_notifier import progress_start, progress_update, progress_account_done, progress_finish
from bot_notifier import progress_start, progress_update, progress_account_done, progress_finish, notify_team_completed_sync
except ImportError:
# 如果没有 bot_notifier使用空函数
def progress_start(team_name, total): pass
def progress_update(account=None, step=None): pass
def progress_account_done(email, success): pass
def progress_finish(): pass
def notify_team_completed_sync(team_name, results): pass
# ==================== 全局状态 ====================
@@ -96,10 +97,13 @@ if threading.current_thread() is threading.main_thread():
def process_single_team(team: dict) -> tuple[list, list]:
def process_single_team(team: dict, team_index: int = 0, teams_total: int = 0) -> tuple[list, list]:
"""处理单个 Team 的完整流程
Args:
team: Team 配置
team_index: 当前 Team 序号 (从 1 开始)
teams_total: Team 总数
Returns:
tuple: (处理结果列表, 待处理的 Owner 列表)
@@ -202,21 +206,42 @@ def process_single_team(team: dict) -> tuple[list, list]:
else:
log.warning(f"Team {team_name} 没有可用席位,无法邀请新成员")
# ========== 阶段 3: 处理普通成员 (注册 + Codex 授权 + CRS) ==========
if invited_accounts:
log.section(f"阶段 3: 逐个注册 OpenAI + Codex 授权 + CRS 入库")
member_results = process_accounts(invited_accounts, team_name)
results.extend(member_results)
# ========== 合并成员和 Owner 一起处理 ==========
all_to_process = invited_accounts.copy()
# 添加未完成的 Owner 到处理列表
from config import INCLUDE_TEAM_OWNERS
include_owner = INCLUDE_TEAM_OWNERS and len(owner_accounts) > 0
if include_owner:
for owner in owner_accounts:
all_to_process.append({
"email": owner["email"],
"password": owner.get("password", DEFAULT_PASSWORD),
"status": owner.get("status", ""),
"role": "owner"
})
log.info(f"包含 {len(owner_accounts)} 个 Owner 账号一起处理")
# Owner 不在这里处理,统一放到所有 Team 处理完后
# ========== 阶段 3: 处理所有账号 (注册 + Codex 授权 + 入库) ==========
if all_to_process:
log.section(f"阶段 3: 逐个注册 OpenAI + Codex 授权 + 入库")
all_results = process_accounts(
all_to_process, team_name,
team_index=team_index, teams_total=teams_total,
include_owner=include_owner
)
results.extend(all_results)
# ========== Team 处理完成 ==========
success_count = sum(1 for r in results if r["status"] == "success")
if results:
log.success(f"{team_name} 成员处理完成: {success_count}/{len(results)} 成功")
log.success(f"{team_name} 处理完成: {success_count}/{len(results)} 成功")
# 发送 Team 完成报告到 Telegram
notify_team_completed_sync(team_name, results)
# 返回未完成的 Owner 列表供后续统一处理
return results, owner_accounts
# 返回空列表,因为 Owner 已经在这里处理
return results, []
def _get_team_by_name(team_name: str) -> dict:
@@ -227,12 +252,16 @@ def _get_team_by_name(team_name: str) -> dict:
return {}
def process_accounts(accounts: list, team_name: str) -> list:
def process_accounts(accounts: list, team_name: str, team_index: int = 0,
teams_total: int = 0, include_owner: bool = False) -> list:
"""处理账号列表 (注册/授权/CRS)
Args:
accounts: 账号列表 [{"email", "password", "status", "role"}]
team_name: Team 名称
team_index: 当前 Team 序号 (从 1 开始)
teams_total: Team 总数
include_owner: 是否包含 Owner
Returns:
list: 处理结果
@@ -242,7 +271,7 @@ def process_accounts(accounts: list, team_name: str) -> list:
results = []
# 启动进度跟踪 (Telegram Bot)
progress_start(team_name, len(accounts))
progress_start(team_name, len(accounts), team_index, teams_total, include_owner)
for i, account in enumerate(accounts):
if _shutdown_requested:
@@ -291,7 +320,7 @@ def process_accounts(accounts: list, team_name: str) -> list:
log.separator("#", 50)
# 更新进度: 当前账号
progress_update(account=email, step="Starting...")
progress_update(account=email, step="Starting...", role=role)
result = {
"team": team_name,
@@ -331,10 +360,11 @@ def process_accounts(accounts: list, team_name: str) -> list:
update_account_status(_tracker, team_name, email, "processing")
save_team_tracker(_tracker)
with Timer(f"账号 {email}"):
if is_team_owner_otp:
# 旧格式 Team Owner: 使用 OTP 登录授权
log.info("Team Owner 账号 (旧格式),使用一次性验证码登录...", icon="auth")
try:
with Timer(f"账号 {email}"):
if is_team_owner_otp:
# 旧格式 Team Owner: 使用 OTP 登录授权
log.info("Team Owner 账号 (旧格式),使用一次性验证码登录...", icon="auth")
progress_update(step="OTP Login...")
auth_success, codex_data = login_and_authorize_with_otp(email)
register_success = auth_success
@@ -469,6 +499,13 @@ def process_accounts(accounts: list, team_name: str) -> list:
update_account_status(_tracker, team_name, email, "register_failed")
save_team_tracker(_tracker)
except ShutdownRequested:
# 用户请求停止,保存当前状态并退出
log.warning(f"用户请求停止,当前账号: {email}")
# 不改变账号状态,保持中断前的状态,下次继续处理
save_team_tracker(_tracker)
break
# 保存到 CSV
save_to_csv(
email=email,
@@ -514,10 +551,10 @@ def run_all_teams():
log.warning(f"发现 {total_incomplete} 个未完成账号,将优先处理")
_current_results = []
all_pending_owners = [] # 收集所有待处理的 Owner
teams_total = len(TEAMS)
with Timer("全部流程"):
# ========== 第一阶段: 处理所有 Team 的普通成员 ==========
# ========== 处理所有 Team (成员 + Owner 一起) ==========
for i, team in enumerate(TEAMS):
if _shutdown_requested:
log.warning("检测到中断请求,停止处理...")
@@ -525,51 +562,18 @@ def run_all_teams():
log.separator("", 60)
team_email = team.get('account') or team.get('owner_email', '')
log.highlight(f"Team {i + 1}/{len(TEAMS)}: {team['name']} ({team_email})", icon="team")
log.highlight(f"Team {i + 1}/{teams_total}: {team['name']} ({team_email})", icon="team")
log.separator("", 60)
results, pending_owners = process_single_team(team)
# 收集待处理的 Owner
if pending_owners:
for owner in pending_owners:
all_pending_owners.append({
"team_name": team["name"],
"email": owner["email"],
"password": owner.get("password", DEFAULT_PASSWORD),
"status": owner.get("status", "team_owner"),
"role": "owner"
})
# 传递 Team 序号信息
results, _ = process_single_team(team, team_index=i + 1, teams_total=teams_total)
_current_results.extend(results)
# Team 之间的间隔
if i < len(TEAMS) - 1 and not _shutdown_requested:
if i < teams_total - 1 and not _shutdown_requested:
wait_time = 3
log.countdown(wait_time, "下一个 Team")
# ========== 第二阶段: 统一处理所有 Team Owner 的 CRS 授权 ==========
if all_pending_owners and not _shutdown_requested:
log.separator("", 60)
log.header(f"统一处理 Team Owner CRS 授权 ({len(all_pending_owners)} 个)")
log.separator("", 60)
for i, owner in enumerate(all_pending_owners):
if _shutdown_requested:
log.warning("检测到中断请求,停止处理...")
break
log.separator("#", 50)
log.info(f"Owner {i + 1}/{len(all_pending_owners)}: {owner['email']} ({owner['team_name']})", icon="account")
log.separator("#", 50)
owner_results = process_accounts([owner], owner["team_name"])
_current_results.extend(owner_results)
# Owner 之间的间隔
if i < len(all_pending_owners) - 1 and not _shutdown_requested:
wait_time = random.randint(5, 15)
log.info(f"等待 {wait_time}s 后处理下一个 Owner...", icon="wait")
time.sleep(wait_time)
# 打印总结
print_summary(_current_results)
@@ -592,22 +596,10 @@ def run_single_team(team_index: int = 0):
log.info(f"单 Team 模式: {team['name']}", icon="start")
_current_results = []
results, pending_owners = process_single_team(team)
# 单 Team 模式:序号为 1/1
results, _ = process_single_team(team, team_index=1, teams_total=1)
_current_results.extend(results)
# 单 Team 模式下也处理 Owner
if pending_owners:
log.section(f"处理 Team Owner ({len(pending_owners)} 个)")
for owner in pending_owners:
owner_data = {
"email": owner["email"],
"password": owner.get("password", DEFAULT_PASSWORD),
"status": owner.get("status", "team_owner"),
"role": "owner"
}
owner_results = process_accounts([owner_data], team["name"])
_current_results.extend(owner_results)
print_summary(_current_results)
return _current_results