This commit is contained in:
2026-01-21 22:24:36 +08:00
parent 6600195a3f
commit 421caca1b7
3 changed files with 335 additions and 1 deletions

View File

@@ -48,8 +48,9 @@ from config import (
S2A_GROUP_IDS,
S2A_ADMIN_KEY,
BROWSER_RANDOM_FINGERPRINT,
batch_remove_teams_by_names,
)
from utils import load_team_tracker, get_all_incomplete_accounts
from utils import load_team_tracker, get_all_incomplete_accounts, save_team_tracker, get_completed_teams, batch_remove_completed_teams
from bot_notifier import BotNotifier, set_notifier, progress_finish
from s2a_service import (
s2a_get_dashboard_stats, format_dashboard_stats, s2a_get_keys_with_usage, format_keys_usage,
@@ -135,6 +136,7 @@ class ProvisionerBot:
("s2a_config", self.cmd_s2a_config),
("clean", self.cmd_clean),
("clean_errors", self.cmd_clean_errors),
("clean_teams", self.cmd_clean_teams),
("keys_usage", self.cmd_keys_usage),
]
for cmd, handler in handlers:
@@ -155,6 +157,10 @@ class ProvisionerBot:
self.callback_clean_errors,
pattern="^clean_errors:"
))
self.app.add_handler(CallbackQueryHandler(
self.callback_clean_teams,
pattern="^clean_teams:"
))
# 注册定时检查任务
if TELEGRAM_CHECK_INTERVAL > 0 and AUTH_PROVIDER == "s2a":
@@ -265,6 +271,10 @@ class ProvisionerBot:
/s2a_config - 配置 S2A 参数
/clean_errors - 清理错误状态账号
<b>🧹 清理管理:</b>
/clean - 清理已完成账号 (team.json)
/clean_teams - 清理已完成 Team (tracker)
<b>📤 导入账号:</b>
/import - 导入账号到 team.json
或直接发送 JSON 文件
@@ -1584,6 +1594,155 @@ class ProvisionerBot:
await query.edit_message_text("\n".join(lines), parse_mode="HTML")
@admin_only
async def cmd_clean_teams(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""清理处理成功的 Team"""
# 获取已完成的 teams
tracker = load_team_tracker()
completed_teams = get_completed_teams(tracker)
if not completed_teams:
await update.message.reply_text("✅ 没有处理成功的 Team 需要清理")
return
# 存储数据供分页使用
context.bot_data["clean_teams_data"] = completed_teams
context.bot_data["clean_teams_total"] = len(completed_teams)
# 显示第一页
text, keyboard = self._build_clean_teams_page(completed_teams, page=0)
await update.message.reply_text(text, reply_markup=keyboard, parse_mode="HTML")
def _build_clean_teams_page(self, teams: list, page: int = 0, page_size: int = 10):
"""构建已完成 Team 预览页面"""
total = len(teams)
total_pages = (total + page_size - 1) // page_size
start_idx = page * page_size
end_idx = min(start_idx + page_size, total)
page_teams = teams[start_idx:end_idx]
# 统计总账号数
total_accounts = sum(t["total"] for t in teams)
lines = [
"<b>🧹 清理已完成 Team (预览)</b>",
"",
f"共发现 <b>{total}</b> 个已完成的 Team",
f"涉及 <b>{total_accounts}</b> 个账号记录",
"",
f"<b>Team 列表 (第 {page + 1}/{total_pages} 页):</b>",
]
# 显示当前页的 Team
for i, team in enumerate(page_teams, start=start_idx + 1):
name = team["name"][:25]
count = team["total"]
lines.append(f"{i}. ✅ {name} ({count} 个账号)")
lines.extend([
"",
"⚠️ <b>此操作将同时清理:</b>",
"• team_tracker.json (账号处理记录)",
"• team.json (Team 配置)",
])
text = "\n".join(lines)
# 构建分页按钮
nav_buttons = []
if page > 0:
nav_buttons.append(InlineKeyboardButton("⬅️ 上一页", callback_data=f"clean_teams:page:{page - 1}"))
if page < total_pages - 1:
nav_buttons.append(InlineKeyboardButton("下一页 ➡️", callback_data=f"clean_teams:page:{page + 1}"))
keyboard = [
nav_buttons,
[InlineKeyboardButton(f"🧹 确认清理全部 ({total})", callback_data="clean_teams:confirm")],
[InlineKeyboardButton("❌ 取消", callback_data="clean_teams:cancel")],
]
# 过滤空行
keyboard = [row for row in keyboard if row]
return text, InlineKeyboardMarkup(keyboard)
async def callback_clean_teams(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""处理清理已完成 Team 的回调"""
query = update.callback_query
await query.answer()
# 验证权限
user_id = update.effective_user.id
if user_id not in TELEGRAM_ADMIN_CHAT_IDS:
await query.edit_message_text("⛔ 无权限")
return
# 解析回调数据
data = query.data.replace("clean_teams:", "")
if data.startswith("page:"):
# 分页浏览
page = int(data.replace("page:", ""))
teams = context.bot_data.get("clean_teams_data", [])
if not teams:
await query.edit_message_text("❌ 数据已过期,请重新使用 /clean_teams")
return
text, keyboard = self._build_clean_teams_page(teams, page)
await query.edit_message_text(text, reply_markup=keyboard, parse_mode="HTML")
elif data == "cancel":
# 取消操作
context.bot_data.pop("clean_teams_data", None)
context.bot_data.pop("clean_teams_total", None)
await query.edit_message_text("✅ 已取消清理操作")
elif data == "confirm":
# 执行清理
teams_data = context.bot_data.get("clean_teams_data", [])
total = context.bot_data.get("clean_teams_total", 0)
await query.edit_message_text(
f"<b>🧹 正在清理 {total} 个已完成 Team...</b>",
parse_mode="HTML"
)
# 获取要删除的 team 名称列表
team_names = [t["name"] for t in teams_data]
# 1. 清理 tracker
tracker = load_team_tracker()
tracker_results = batch_remove_completed_teams(tracker)
save_team_tracker(tracker)
# 2. 清理 team.json
json_results = batch_remove_teams_by_names(team_names)
# 清理缓存数据
context.bot_data.pop("clean_teams_data", None)
context.bot_data.pop("clean_teams_total", None)
# 统计清理的账号数
total_accounts = sum(d.get("accounts", 0) for d in tracker_results.get("details", []) if d.get("status") == "success")
# 显示结果
lines = [
"<b>✅ 清理完成</b>",
"",
f"清理 Team: <b>{tracker_results['success']}</b>",
f"清理账号记录: <b>{total_accounts}</b>",
"",
"<b>📁 文件清理:</b>",
f"• tracker: {tracker_results['success']} 个 Team",
f"• team.json: {json_results['success']} 个 Team",
]
if tracker_results['failed'] > 0 or json_results['failed'] > 0:
lines.append("")
lines.append(f"<b>失败:</b> tracker={tracker_results['failed']}, json={json_results['failed']}")
await query.edit_message_text("\n".join(lines), parse_mode="HTML")
@admin_only
async def cmd_import(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""上传账号到 team.json"""