update
This commit is contained in:
@@ -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:
|
Args:
|
||||||
@@ -23,19 +23,16 @@ def make_progress_bar(current: int, total: int, width: int = 10) -> str:
|
|||||||
width: 进度条宽度 (字符数)
|
width: 进度条宽度 (字符数)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
进度条字符串,如 "████████░░ 80%"
|
进度条字符串,如 "▓▓▓▓░░░░░░"
|
||||||
"""
|
"""
|
||||||
if total <= 0:
|
if total <= 0:
|
||||||
return "░" * width + " 0%"
|
return "░" * width
|
||||||
|
|
||||||
percent = min(current / total, 1.0)
|
percent = min(current / total, 1.0)
|
||||||
filled = int(width * percent)
|
filled = int(width * percent)
|
||||||
empty = width - filled
|
empty = width - filled
|
||||||
|
|
||||||
bar = "█" * filled + "░" * empty
|
return "▓" * filled + "░" * empty
|
||||||
percent_text = f"{int(percent * 100)}%"
|
|
||||||
|
|
||||||
return f"{bar} {percent_text}"
|
|
||||||
|
|
||||||
|
|
||||||
class ProgressTracker:
|
class ProgressTracker:
|
||||||
@@ -54,6 +51,7 @@ class ProgressTracker:
|
|||||||
self.success = 0
|
self.success = 0
|
||||||
self.failed = 0
|
self.failed = 0
|
||||||
self.current_account = ""
|
self.current_account = ""
|
||||||
|
self.current_phase = "" # 当前阶段 (注册/授权/验证)
|
||||||
self.current_step = ""
|
self.current_step = ""
|
||||||
self.current_role = "" # 当前账号角色 (member/owner)
|
self.current_role = "" # 当前账号角色 (member/owner)
|
||||||
self.messages: Dict[int, Message] = {} # chat_id -> Message
|
self.messages: Dict[int, Message] = {} # chat_id -> Message
|
||||||
@@ -63,29 +61,34 @@ class ProgressTracker:
|
|||||||
|
|
||||||
def _get_progress_text(self) -> str:
|
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 序号
|
# 标题行:显示 Team 序号
|
||||||
if self.teams_total > 0:
|
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:
|
else:
|
||||||
title = f"<b>📦 正在处理: {self.team_name}</b>"
|
title = f"<b>◈ 正在处理: {self.team_name}</b>"
|
||||||
|
|
||||||
|
owner_tag = " (含 Owner)" if self.include_owner else ""
|
||||||
lines = [
|
lines = [
|
||||||
title,
|
title,
|
||||||
"",
|
"",
|
||||||
f"进度: {bar}",
|
f"🔄 注册进度 {self.current}/{self.total}{owner_tag}",
|
||||||
f"账号: {self.current}/{self.total}" + (f" (含 Owner)" if self.include_owner else ""),
|
bar,
|
||||||
f"成功: {self.success} | 失败: {self.failed}",
|
"",
|
||||||
|
f"✅ 成功: {self.success}",
|
||||||
|
f"❌ 失败: {self.failed}",
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.current_account:
|
if self.current_account:
|
||||||
lines.append("")
|
lines.append("")
|
||||||
role_tag = " 👑" if self.current_role == "owner" else ""
|
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:
|
if self.current_phase and self.current_step:
|
||||||
lines.append(f"步骤: {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)
|
return "\n".join(lines)
|
||||||
|
|
||||||
@@ -137,12 +140,14 @@ class ProgressTracker:
|
|||||||
self._loop = loop
|
self._loop = loop
|
||||||
asyncio.run_coroutine_threadsafe(self._send_initial_message(), 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:
|
if current is not None:
|
||||||
self.current = current
|
self.current = current
|
||||||
if account is not None:
|
if account is not None:
|
||||||
self.current_account = account
|
self.current_account = account
|
||||||
|
if phase is not None:
|
||||||
|
self.current_phase = phase
|
||||||
if step is not None:
|
if step is not None:
|
||||||
self.current_step = step
|
self.current_step = step
|
||||||
if role is not None:
|
if role is not None:
|
||||||
@@ -158,6 +163,7 @@ class ProgressTracker:
|
|||||||
else:
|
else:
|
||||||
self.failed += 1
|
self.failed += 1
|
||||||
self.current_account = ""
|
self.current_account = ""
|
||||||
|
self.current_phase = ""
|
||||||
self.current_step = ""
|
self.current_step = ""
|
||||||
self.current_role = ""
|
self.current_role = ""
|
||||||
# 最后一个账号完成时强制更新,确保显示 100%
|
# 最后一个账号完成时强制更新,确保显示 100%
|
||||||
@@ -440,10 +446,17 @@ def progress_start(team_name: str, total: int, team_index: int = 0,
|
|||||||
return None
|
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():
|
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):
|
def progress_account_done(email: str, success: bool):
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ from s2a_service import (
|
|||||||
)
|
)
|
||||||
from logger import log
|
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):
|
class ShutdownRequested(Exception):
|
||||||
@@ -1106,6 +1112,7 @@ def register_openai_account(page, email: str, password: str) -> bool:
|
|||||||
|
|
||||||
# 步骤1: 输入邮箱 (在 log-in-or-create-account 页面)
|
# 步骤1: 输入邮箱 (在 log-in-or-create-account 页面)
|
||||||
if "auth.openai.com/log-in-or-create-account" in current_url:
|
if "auth.openai.com/log-in-or-create-account" in current_url:
|
||||||
|
progress_update(phase="注册", step="输入邮箱...")
|
||||||
log.step("等待邮箱输入框...")
|
log.step("等待邮箱输入框...")
|
||||||
email_input = wait_for_element(page, 'css:input[type="email"]', timeout=15)
|
email_input = wait_for_element(page, 'css:input[type="email"]', timeout=15)
|
||||||
if not email_input:
|
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)
|
# 步骤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:
|
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:
|
try:
|
||||||
error_text = page.ele('text:Incorrect email address or password', timeout=1)
|
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:
|
if "auth.openai.com/about-you" in current_url:
|
||||||
|
progress_update(phase="注册", step="补充个人信息...")
|
||||||
log_current_url(page, "个人信息页面")
|
log_current_url(page, "个人信息页面")
|
||||||
log.info("检测到姓名输入页面,账号已存在,补充信息...")
|
log.info("检测到姓名输入页面,账号已存在,补充信息...")
|
||||||
|
|
||||||
@@ -1282,6 +1291,7 @@ def register_openai_account(page, email: str, password: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# 获取验证码
|
# 获取验证码
|
||||||
|
progress_update(phase="注册", step="等待验证码...")
|
||||||
log.step("等待验证码邮件...")
|
log.step("等待验证码邮件...")
|
||||||
verification_code, error, email_time = unified_get_verification_code(email)
|
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
|
max_code_retries = 3
|
||||||
for code_attempt in range(max_code_retries):
|
for code_attempt in range(max_code_retries):
|
||||||
# 输入验证码
|
# 输入验证码
|
||||||
|
progress_update(phase="注册", step="输入验证码...")
|
||||||
log.step(f"输入验证码: {verification_code}")
|
log.step(f"输入验证码: {verification_code}")
|
||||||
while check_and_handle_error(page):
|
while check_and_handle_error(page):
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@@ -2473,6 +2484,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
|
|||||||
bool: 授权是否成功
|
bool: 授权是否成功
|
||||||
"""
|
"""
|
||||||
log.info(f"开始 S2A 授权: {email}", icon="code")
|
log.info(f"开始 S2A 授权: {email}", icon="code")
|
||||||
|
progress_update(phase="授权", step="开始 S2A 授权...")
|
||||||
|
|
||||||
# 生成授权 URL
|
# 生成授权 URL
|
||||||
auth_url, session_id = s2a_generate_auth_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
|
return False
|
||||||
|
|
||||||
# 打开授权页面
|
# 打开授权页面
|
||||||
|
progress_update(phase="授权", step="打开授权页面...")
|
||||||
log.step("打开 S2A 授权页面...")
|
log.step("打开 S2A 授权页面...")
|
||||||
log.info(f"[URL] S2A授权URL: {auth_url}", icon="browser")
|
log.info(f"[URL] S2A授权URL: {auth_url}", icon="browser")
|
||||||
page.get(auth_url)
|
page.get(auth_url)
|
||||||
@@ -2492,6 +2505,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# 输入邮箱
|
# 输入邮箱
|
||||||
|
progress_update(phase="授权", step="输入邮箱...")
|
||||||
log.step("输入邮箱...")
|
log.step("输入邮箱...")
|
||||||
email_input = wait_for_element(page, 'css:input[type="email"]', timeout=10)
|
email_input = wait_for_element(page, 'css:input[type="email"]', timeout=10)
|
||||||
if not email_input:
|
if not email_input:
|
||||||
@@ -2516,6 +2530,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
|
|||||||
current_url = page.url
|
current_url = page.url
|
||||||
if "/password" in current_url:
|
if "/password" in current_url:
|
||||||
try:
|
try:
|
||||||
|
progress_update(phase="授权", step="输入密码...")
|
||||||
log.step("输入密码...")
|
log.step("输入密码...")
|
||||||
password_input = wait_for_element(page, 'css:input[type="password"]', timeout=10)
|
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
|
callback_url = None
|
||||||
progress_shown = False
|
progress_shown = False
|
||||||
last_url_in_loop = None
|
last_url_in_loop = None
|
||||||
|
progress_update(phase="授权", step="等待回调...")
|
||||||
log.step(f"等待 S2A 授权回调 (最多 {max_wait}s)...")
|
log.step(f"等待 S2A 授权回调 (最多 {max_wait}s)...")
|
||||||
|
|
||||||
while time.time() - start_time < max_wait:
|
while time.time() - start_time < max_wait:
|
||||||
@@ -2604,6 +2620,7 @@ def perform_s2a_authorization(page, email: str, password: str) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# S2A 特有流程: 用授权码创建账号 (传入完整邮箱用于验证)
|
# S2A 特有流程: 用授权码创建账号 (传入完整邮箱用于验证)
|
||||||
|
progress_update(phase="授权", step="提交授权码...")
|
||||||
log.step("正在提交 S2A 授权码...")
|
log.step("正在提交 S2A 授权码...")
|
||||||
result = s2a_create_account_from_oauth(code, session_id, name=email)
|
result = s2a_create_account_from_oauth(code, session_id, name=email)
|
||||||
if result:
|
if result:
|
||||||
|
|||||||
14
run.py
14
run.py
@@ -48,7 +48,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
# 如果没有 bot_notifier,使用空函数
|
# 如果没有 bot_notifier,使用空函数
|
||||||
def progress_start(team_name, total): pass
|
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_account_done(email, success): pass
|
||||||
def progress_finish(): pass
|
def progress_finish(): pass
|
||||||
def notify_team_completed_sync(team_name, results): 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)
|
log.separator("#", 50)
|
||||||
|
|
||||||
# 更新进度: 当前账号
|
# 更新进度: 当前账号
|
||||||
progress_update(account=email, step="Starting...", role=role)
|
progress_update(account=email, phase="准备", step="开始处理...", role=role)
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"team": team_name,
|
"team": team_name,
|
||||||
@@ -370,13 +370,13 @@ def process_accounts(accounts: list, team_name: str, team_index: int = 0,
|
|||||||
if is_team_owner_otp:
|
if is_team_owner_otp:
|
||||||
# 旧格式 Team Owner: 使用 OTP 登录授权
|
# 旧格式 Team Owner: 使用 OTP 登录授权
|
||||||
log.info("Team Owner 账号 (旧格式),使用一次性验证码登录...", icon="auth")
|
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)
|
auth_success, codex_data = login_and_authorize_with_otp(email)
|
||||||
register_success = auth_success
|
register_success = auth_success
|
||||||
elif need_crs_only:
|
elif need_crs_only:
|
||||||
# 已授权但未入库: 跳过授权,直接尝试入库
|
# 已授权但未入库: 跳过授权,直接尝试入库
|
||||||
log.info(f"已授权账号 (状态: {account_status}),跳过授权,直接入库...", icon="auth")
|
log.info(f"已授权账号 (状态: {account_status}),跳过授权,直接入库...", icon="auth")
|
||||||
progress_update(step="Adding to CRS...")
|
progress_update(phase="入库", step="添加到 CRS...")
|
||||||
register_success = True
|
register_success = True
|
||||||
codex_data = None # CPA/S2A 模式不需要 codex_data
|
codex_data = None # CPA/S2A 模式不需要 codex_data
|
||||||
# CRS 模式下,由于没有 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:
|
elif need_auth_only:
|
||||||
# 已注册账号 (包括新格式 Owner): 使用密码登录授权
|
# 已注册账号 (包括新格式 Owner): 使用密码登录授权
|
||||||
log.info(f"已注册账号 (状态: {account_status}, 角色: {account_role}),使用密码登录授权...", icon="auth")
|
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)
|
auth_success, codex_data = authorize_only(email, password)
|
||||||
register_success = True
|
register_success = True
|
||||||
else:
|
else:
|
||||||
# 新账号: 注册 + Codex 授权
|
# 新账号: 注册 + Codex 授权
|
||||||
progress_update(step="Registering...")
|
progress_update(phase="注册", step="注册 OpenAI...")
|
||||||
register_success, codex_data = register_and_authorize(email, password)
|
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 账号入库状态...")
|
log.step("正在验证 S2A 账号入库状态...")
|
||||||
progress_update(step="验证入库...")
|
progress_update(phase="验证", step="检查入库状态...")
|
||||||
verified, account_data = s2a_verify_account_in_pool(email)
|
verified, account_data = s2a_verify_account_in_pool(email)
|
||||||
|
|
||||||
if verified:
|
if verified:
|
||||||
|
|||||||
@@ -1207,19 +1207,19 @@ class ProvisionerBot:
|
|||||||
# 创建时间选择按钮
|
# 创建时间选择按钮
|
||||||
keyboard = [
|
keyboard = [
|
||||||
[
|
[
|
||||||
InlineKeyboardButton("📅 今天", callback_data="keys_usage:today"),
|
InlineKeyboardButton("📍 今天", callback_data="keys_usage:today"),
|
||||||
InlineKeyboardButton("📅 昨天", callback_data="keys_usage:yesterday"),
|
InlineKeyboardButton("◀ 昨天", callback_data="keys_usage:yesterday"),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
InlineKeyboardButton("📆 近 7 天", callback_data="keys_usage:7d"),
|
InlineKeyboardButton("◀ 近 7 天", callback_data="keys_usage:7d"),
|
||||||
InlineKeyboardButton("📆 近 14 天", callback_data="keys_usage:14d"),
|
InlineKeyboardButton("◀ 近 14 天", callback_data="keys_usage:14d"),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
InlineKeyboardButton("📆 近 30 天", callback_data="keys_usage:30d"),
|
InlineKeyboardButton("◀ 近 30 天", callback_data="keys_usage:30d"),
|
||||||
InlineKeyboardButton("📆 本月", callback_data="keys_usage:this_month"),
|
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)
|
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||||
|
|||||||
Reference in New Issue
Block a user