This commit is contained in:
2026-01-20 21:09:54 +08:00
parent a7835170f7
commit 6559e4b84a
4 changed files with 64 additions and 34 deletions

View File

@@ -14,7 +14,7 @@ from config import (
)
def make_progress_bar(current: int, total: int, width: int = 10) -> str:
def make_progress_bar(current: int, total: int, width: int = 20) -> str:
"""生成文本进度条
Args:
@@ -23,19 +23,16 @@ def make_progress_bar(current: int, total: int, width: int = 10) -> str:
width: 进度条宽度 (字符数)
Returns:
进度条字符串,如 "████████░░ 80%"
进度条字符串,如 "▓▓▓▓░░░░░░"
"""
if total <= 0:
return "" * width + " 0%"
return "" * width
percent = min(current / total, 1.0)
filled = int(width * percent)
empty = width - filled
bar = "" * filled + "" * empty
percent_text = f"{int(percent * 100)}%"
return f"{bar} {percent_text}"
return "" * filled + "" * empty
class ProgressTracker:
@@ -54,6 +51,7 @@ class ProgressTracker:
self.success = 0
self.failed = 0
self.current_account = ""
self.current_phase = "" # 当前阶段 (注册/授权/验证)
self.current_step = ""
self.current_role = "" # 当前账号角色 (member/owner)
self.messages: Dict[int, Message] = {} # chat_id -> Message
@@ -63,29 +61,34 @@ class ProgressTracker:
def _get_progress_text(self) -> str:
"""生成进度消息文本"""
bar = make_progress_bar(self.current, self.total, 12)
bar = make_progress_bar(self.current, self.total, 20)
# 标题行:显示 Team 序号
if self.teams_total > 0:
title = f"<b>📦 Team [{self.team_index}/{self.teams_total}]: {self.team_name}</b>"
title = f"<b> Team [{self.team_index}/{self.teams_total}]: {self.team_name}</b>"
else:
title = f"<b>📦 正在处理: {self.team_name}</b>"
title = f"<b> 正在处理: {self.team_name}</b>"
owner_tag = " (含 Owner)" if self.include_owner else ""
lines = [
title,
"",
f"进度: {bar}",
f"账号: {self.current}/{self.total}" + (f" (含 Owner)" if self.include_owner else ""),
f"成功: {self.success} | 失败: {self.failed}",
f"🔄 注册进度 {self.current}/{self.total}{owner_tag}",
bar,
"",
f"✅ 成功: {self.success}",
f"❌ 失败: {self.failed}",
]
if self.current_account:
lines.append("")
role_tag = " 👑" if self.current_role == "owner" else ""
lines.append(f"当前: <code>{self.current_account}</code>{role_tag}")
lines.append(f"⏳ 正在处理: <code>{self.current_account}</code>{role_tag}")
if self.current_step:
lines.append(f"步骤: {self.current_step}")
if self.current_phase and self.current_step:
lines.append(f" ▸ [{self.current_phase}] {self.current_step}")
elif self.current_step:
lines.append(f"{self.current_step}")
return "\n".join(lines)
@@ -137,12 +140,14 @@ class ProgressTracker:
self._loop = loop
asyncio.run_coroutine_threadsafe(self._send_initial_message(), loop)
def update(self, current: int = None, account: str = None, step: str = None, role: str = None):
def update(self, current: int = None, account: str = None, phase: str = None, step: str = None, role: str = None):
"""更新进度 (供同步代码调用)"""
if current is not None:
self.current = current
if account is not None:
self.current_account = account
if phase is not None:
self.current_phase = phase
if step is not None:
self.current_step = step
if role is not None:
@@ -158,6 +163,7 @@ class ProgressTracker:
else:
self.failed += 1
self.current_account = ""
self.current_phase = ""
self.current_step = ""
self.current_role = ""
# 最后一个账号完成时强制更新,确保显示 100%
@@ -440,10 +446,17 @@ def progress_start(team_name: str, total: int, team_index: int = 0,
return None
def progress_update(account: str = None, step: str = None, role: str = None):
"""更新当前进度"""
def progress_update(account: str = None, phase: str = None, step: str = None, role: str = None):
"""更新当前进度
Args:
account: 当前处理的账号
phase: 当前阶段 (注册/授权/验证)
step: 当前步骤
role: 账号角色
"""
if _notifier and _notifier.get_progress():
_notifier.get_progress().update(account=account, step=step, role=role)
_notifier.get_progress().update(account=account, phase=phase, step=step, role=role)
def progress_account_done(email: str, success: bool):

View File

@@ -34,6 +34,12 @@ from s2a_service import (
)
from logger import log
# 进度更新 (Telegram Bot 使用)
try:
from bot_notifier import progress_update
except ImportError:
def progress_update(account=None, phase=None, step=None, role=None): pass
# ==================== 停止检查 ====================
class ShutdownRequested(Exception):
@@ -1106,6 +1112,7 @@ def register_openai_account(page, email: str, password: str) -> bool:
# 步骤1: 输入邮箱 (在 log-in-or-create-account 页面)
if "auth.openai.com/log-in-or-create-account" in current_url:
progress_update(phase="注册", step="输入邮箱...")
log.step("等待邮箱输入框...")
email_input = wait_for_element(page, 'css:input[type="email"]', timeout=15)
if not email_input:
@@ -1129,6 +1136,7 @@ def register_openai_account(page, email: str, password: str) -> bool:
# 步骤2: 输入密码 (在密码页面: log-in/password 或 create-account/password)
if "auth.openai.com/log-in/password" in current_url or "auth.openai.com/create-account/password" in current_url:
progress_update(phase="注册", step="输入密码...")
# 先检查是否有密码错误提示,如果有则使用一次性验证码登录
try:
error_text = page.ele('text:Incorrect email address or password', timeout=1)
@@ -1229,6 +1237,7 @@ def register_openai_account(page, email: str, password: str) -> bool:
# 检测到姓名/年龄输入页面 (账号已存在,只需补充信息)
if "auth.openai.com/about-you" in current_url:
progress_update(phase="注册", step="补充个人信息...")
log_current_url(page, "个人信息页面")
log.info("检测到姓名输入页面,账号已存在,补充信息...")
@@ -1282,6 +1291,7 @@ def register_openai_account(page, email: str, password: str) -> bool:
return False
# 获取验证码
progress_update(phase="注册", step="等待验证码...")
log.step("等待验证码邮件...")
verification_code, error, email_time = unified_get_verification_code(email)
@@ -1296,6 +1306,7 @@ def register_openai_account(page, email: str, password: str) -> bool:
max_code_retries = 3
for code_attempt in range(max_code_retries):
# 输入验证码
progress_update(phase="注册", step="输入验证码...")
log.step(f"输入验证码: {verification_code}")
while check_and_handle_error(page):
time.sleep(1)
@@ -2473,6 +2484,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
bool: 授权是否成功
"""
log.info(f"开始 S2A 授权: {email}", icon="code")
progress_update(phase="授权", step="开始 S2A 授权...")
# 生成授权 URL
auth_url, session_id = s2a_generate_auth_url()
@@ -2481,6 +2493,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
return False
# 打开授权页面
progress_update(phase="授权", step="打开授权页面...")
log.step("打开 S2A 授权页面...")
log.info(f"[URL] S2A授权URL: {auth_url}", icon="browser")
page.get(auth_url)
@@ -2492,6 +2505,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
try:
# 输入邮箱
progress_update(phase="授权", step="输入邮箱...")
log.step("输入邮箱...")
email_input = wait_for_element(page, 'css:input[type="email"]', timeout=10)
if not email_input:
@@ -2516,6 +2530,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
current_url = page.url
if "/password" in current_url:
try:
progress_update(phase="授权", step="输入密码...")
log.step("输入密码...")
password_input = wait_for_element(page, 'css:input[type="password"]', timeout=10)
@@ -2540,6 +2555,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
callback_url = None
progress_shown = False
last_url_in_loop = None
progress_update(phase="授权", step="等待回调...")
log.step(f"等待 S2A 授权回调 (最多 {max_wait}s)...")
while time.time() - start_time < max_wait:
@@ -2604,6 +2620,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
return False
# S2A 特有流程: 用授权码创建账号 (传入完整邮箱用于验证)
progress_update(phase="授权", step="提交授权码...")
log.step("正在提交 S2A 授权码...")
result = s2a_create_account_from_oauth(code, session_id, name=email)
if result:

14
run.py
View File

@@ -48,7 +48,7 @@ try:
except ImportError:
# 如果没有 bot_notifier使用空函数
def progress_start(team_name, total): pass
def progress_update(account=None, step=None): pass
def progress_update(account=None, phase=None, step=None, role=None): pass
def progress_account_done(email, success): pass
def progress_finish(): pass
def notify_team_completed_sync(team_name, results): pass
@@ -319,7 +319,7 @@ def process_accounts(accounts: list, team_name: str, team_index: int = 0,
log.separator("#", 50)
# 更新进度: 当前账号
progress_update(account=email, step="Starting...", role=role)
progress_update(account=email, phase="准备", step="开始处理...", role=role)
result = {
"team": team_name,
@@ -370,13 +370,13 @@ def process_accounts(accounts: list, team_name: str, team_index: int = 0,
if is_team_owner_otp:
# 旧格式 Team Owner: 使用 OTP 登录授权
log.info("Team Owner 账号 (旧格式),使用一次性验证码登录...", icon="auth")
progress_update(step="OTP Login...")
progress_update(phase="授权", step="OTP 登录...")
auth_success, codex_data = login_and_authorize_with_otp(email)
register_success = auth_success
elif need_crs_only:
# 已授权但未入库: 跳过授权,直接尝试入库
log.info(f"已授权账号 (状态: {account_status}),跳过授权,直接入库...", icon="auth")
progress_update(step="Adding to CRS...")
progress_update(phase="入库", step="添加到 CRS...")
register_success = True
codex_data = None # CPA/S2A 模式不需要 codex_data
# CRS 模式下,由于没有 codex_data无法入库需要重新授权
@@ -387,12 +387,12 @@ def process_accounts(accounts: list, team_name: str, team_index: int = 0,
elif need_auth_only:
# 已注册账号 (包括新格式 Owner): 使用密码登录授权
log.info(f"已注册账号 (状态: {account_status}, 角色: {account_role}),使用密码登录授权...", icon="auth")
progress_update(step="Authorizing...")
progress_update(phase="授权", step="密码登录授权...")
auth_success, codex_data = authorize_only(email, password)
register_success = True
else:
# 新账号: 注册 + Codex 授权
progress_update(step="Registering...")
progress_update(phase="注册", step="注册 OpenAI...")
register_success, codex_data = register_and_authorize(email, password)
# 检查是否是域名黑名单错误
@@ -436,7 +436,7 @@ def process_accounts(accounts: list, team_name: str, team_index: int = 0,
# 验证账号是否成功入库
log.step("正在验证 S2A 账号入库状态...")
progress_update(step="验证入库...")
progress_update(phase="验证", step="检查入库状态...")
verified, account_data = s2a_verify_account_in_pool(email)
if verified:

View File

@@ -1207,19 +1207,19 @@ class ProvisionerBot:
# 创建时间选择按钮
keyboard = [
[
InlineKeyboardButton("📅 今天", callback_data="keys_usage:today"),
InlineKeyboardButton("📅 昨天", callback_data="keys_usage:yesterday"),
InlineKeyboardButton("📍 今天", callback_data="keys_usage:today"),
InlineKeyboardButton(" 昨天", callback_data="keys_usage:yesterday"),
],
[
InlineKeyboardButton("📆 近 7 天", callback_data="keys_usage:7d"),
InlineKeyboardButton("📆 近 14 天", callback_data="keys_usage:14d"),
InlineKeyboardButton(" 近 7 天", callback_data="keys_usage:7d"),
InlineKeyboardButton(" 近 14 天", callback_data="keys_usage:14d"),
],
[
InlineKeyboardButton("📆 近 30 天", callback_data="keys_usage:30d"),
InlineKeyboardButton("📆 本月", callback_data="keys_usage:this_month"),
InlineKeyboardButton(" 近 30 天", callback_data="keys_usage:30d"),
InlineKeyboardButton("📅 本月", callback_data="keys_usage:this_month"),
],
[
InlineKeyboardButton("📆 上月", callback_data="keys_usage:last_month"),
InlineKeyboardButton("📅 上月", callback_data="keys_usage:last_month"),
],
]
reply_markup = InlineKeyboardMarkup(keyboard)