diff --git a/telegram_bot.py b/telegram_bot.py
index 8bc3f70..b6ae917 100644
--- a/telegram_bot.py
+++ b/telegram_bot.py
@@ -149,7 +149,6 @@ class ProvisionerBot:
("clean_teams", self.cmd_clean_teams),
("keys_usage", self.cmd_keys_usage),
("autogptplus", self.cmd_autogptplus),
- ("s2a", self.cmd_s2a_panel),
]
for cmd, handler in handlers:
self.app.add_handler(CommandHandler(cmd, handler))
@@ -181,10 +180,6 @@ class ProvisionerBot:
self.callback_autogptplus,
pattern="^autogptplus:"
))
- self.app.add_handler(CallbackQueryHandler(
- self.callback_s2a_panel,
- pattern="^s2a:"
- ))
# 注册自定义数量输入处理器 (GPT Team 注册)
self.app.add_handler(MessageHandler(
@@ -283,8 +278,6 @@ class ProvisionerBot:
BotCommand("team_register", "GPT Team 自动注册"),
# AutoGPTPlus
BotCommand("autogptplus", "AutoGPTPlus 管理面板"),
- # S2A
- BotCommand("s2a", "S2A 服务管理面板"),
]
try:
await self.app.bot.set_my_commands(commands)
@@ -3065,574 +3058,6 @@ class ProvisionerBot:
self.current_task = None
self.current_team = None
- # ==================== S2A 管理面板 ====================
-
- @admin_only
- async def cmd_s2a_panel(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
- """S2A 服务管理面板"""
- if AUTH_PROVIDER != "s2a":
- await update.message.reply_text(
- f"⚠️ S2A 面板仅在 S2A 模式下可用\n\n"
- f"当前模式: {AUTH_PROVIDER}\n\n"
- f"请在 config.toml 中设置:\n"
- f"auth_provider = \"s2a\"",
- parse_mode="HTML"
- )
- return
-
- keyboard = self._get_s2a_main_keyboard()
- await update.message.reply_text(
- "📊 S2A 服务管理面板\n\n"
- "Shared Account 服务配置管理\n\n"
- "请选择功能:",
- parse_mode="HTML",
- reply_markup=keyboard
- )
-
- def _get_s2a_main_keyboard(self):
- """获取 S2A 主菜单键盘"""
- return InlineKeyboardMarkup([
- [
- InlineKeyboardButton("📊 仪表盘", callback_data="s2a:dashboard"),
- InlineKeyboardButton("📦 库存查询", callback_data="s2a:stock"),
- ],
- [
- InlineKeyboardButton("🔑 密钥用量", callback_data="s2a:keys_usage"),
- InlineKeyboardButton("⚙️ 服务配置", callback_data="s2a:config"),
- ],
- [
- InlineKeyboardButton("🧹 清理错误", callback_data="s2a:clean_errors"),
- InlineKeyboardButton("🗑️ 清理Teams", callback_data="s2a:clean_teams"),
- ],
- [
- InlineKeyboardButton("📤 导入账号", callback_data="s2a:import"),
- InlineKeyboardButton("🔄 测试连接", callback_data="s2a:test"),
- ],
- [
- InlineKeyboardButton("🚀 开始处理", callback_data="s2a:run"),
- ],
- ])
-
- async def callback_s2a_panel(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
- """处理 S2A 面板回调"""
- query = update.callback_query
-
- # 权限检查
- user_id = update.effective_user.id
- if user_id not in TELEGRAM_ADMIN_CHAT_IDS:
- await query.answer("⛔ 无权限", show_alert=True)
- return
-
- await query.answer()
-
- data = query.data.split(":")
- action = data[1] if len(data) > 1 else ""
- sub_action = data[2] if len(data) > 2 else ""
-
- if action == "dashboard":
- await self._s2a_show_dashboard(query)
- elif action == "stock":
- await self._s2a_show_stock(query)
- elif action == "keys_usage":
- await self._s2a_show_keys_usage(query, sub_action)
- elif action == "config":
- await self._s2a_show_config(query)
- elif action == "clean_errors":
- await self._s2a_clean_errors(query, sub_action)
- elif action == "clean_teams":
- await self._s2a_clean_teams(query, sub_action)
- elif action == "import":
- await self._s2a_show_import(query)
- elif action == "test":
- await self._s2a_test_connection(query)
- elif action == "run":
- await self._s2a_show_run_options(query)
- elif action == "run_team":
- await self._s2a_run_team(query, context, sub_action)
- elif action == "run_all":
- await self._s2a_run_all(query, context)
- elif action == "resume":
- await self._s2a_resume(query, context)
- elif action == "back":
- await query.edit_message_text(
- "📊 S2A 服务管理面板\n\n"
- "Shared Account 服务配置管理\n\n"
- "请选择功能:",
- parse_mode="HTML",
- reply_markup=self._get_s2a_main_keyboard()
- )
-
- async def _s2a_show_dashboard(self, query):
- """显示 S2A 仪表盘"""
- await query.edit_message_text("⏳ 正在获取仪表盘数据...")
-
- try:
- stats = s2a_get_dashboard_stats()
- if stats:
- text = format_dashboard_stats(stats)
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(text, parse_mode="HTML", reply_markup=reply_markup)
- else:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- "❌ 获取仪表盘数据失败\n请检查 S2A 配置和 API 连接",
- reply_markup=reply_markup
- )
- except Exception as e:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(f"❌ 错误: {e}", reply_markup=reply_markup)
-
- async def _s2a_show_stock(self, query):
- """显示库存信息"""
- await query.edit_message_text("⏳ 正在获取库存数据...")
-
- try:
- stats = s2a_get_dashboard_stats()
- if not stats:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text("❌ 获取库存信息失败", reply_markup=reply_markup)
- return
-
- text = self._format_stock_message(stats)
- keyboard = [
- [InlineKeyboardButton("🔄 刷新", callback_data="s2a:stock")],
- [InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")],
- ]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(text, parse_mode="HTML", reply_markup=reply_markup)
- except Exception as e:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(f"❌ 错误: {e}", reply_markup=reply_markup)
-
- async def _s2a_show_keys_usage(self, query, period: str = ""):
- """显示密钥用量"""
- if not period:
- # 显示时间选择菜单
- keyboard = [
- [
- InlineKeyboardButton("今日", callback_data="s2a:keys_usage:today"),
- InlineKeyboardButton("本周", callback_data="s2a:keys_usage:week"),
- ],
- [
- InlineKeyboardButton("本月", callback_data="s2a:keys_usage:month"),
- InlineKeyboardButton("全部", callback_data="s2a:keys_usage:all"),
- ],
- [InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")],
- ]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- "🔑 API 密钥用量查询\n\n选择时间范围:",
- parse_mode="HTML",
- reply_markup=reply_markup
- )
- return
-
- await query.edit_message_text("⏳ 正在获取用量数据...")
-
- try:
- keys_data = s2a_get_keys_with_usage(period)
- if keys_data:
- text = format_keys_usage(keys_data, period)
- keyboard = [
- [InlineKeyboardButton("🔄 刷新", callback_data=f"s2a:keys_usage:{period}")],
- [InlineKeyboardButton("◀️ 返回时间选择", callback_data="s2a:keys_usage")],
- [InlineKeyboardButton("◀️ 返回主菜单", callback_data="s2a:back")],
- ]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(text, parse_mode="HTML", reply_markup=reply_markup)
- else:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:keys_usage")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text("❌ 获取用量数据失败", reply_markup=reply_markup)
- except Exception as e:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:keys_usage")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(f"❌ 错误: {e}", reply_markup=reply_markup)
-
- async def _s2a_show_config(self, query):
- """显示 S2A 配置"""
- # 脱敏显示 API Key
- key_display = "未配置"
- if S2A_ADMIN_KEY:
- if len(S2A_ADMIN_KEY) > 10:
- key_display = f"{S2A_ADMIN_KEY[:4]}...{S2A_ADMIN_KEY[-4:]}"
- else:
- key_display = S2A_ADMIN_KEY[:4] + "..."
-
- groups_display = ", ".join(S2A_GROUP_NAMES) if S2A_GROUP_NAMES else "默认分组"
- group_ids_display = ", ".join(str(x) for x in S2A_GROUP_IDS) if S2A_GROUP_IDS else "无"
-
- lines = [
- "⚙️ S2A 服务配置",
- "",
- f"API 地址: {S2A_API_BASE or '未配置'}",
- f"CPA 地址: {CPA_API_BASE or '未配置'}",
- f"CRS 地址: {CRS_API_BASE or '未配置'}",
- f"Admin Key: {key_display}",
- "",
- f"并发数: {S2A_CONCURRENCY}",
- f"优先级: {S2A_PRIORITY}",
- f"分组名称: {groups_display}",
- f"分组 ID: {group_ids_display}",
- "",
- "💡 使用命令修改配置:",
- "/s2a_config concurrency 10",
- "/s2a_config priority 50",
- ]
-
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
-
- await query.edit_message_text(
- "\n".join(lines),
- parse_mode="HTML",
- reply_markup=reply_markup
- )
-
- async def _s2a_clean_errors(self, query, sub_action: str = ""):
- """清理错误账号"""
- if sub_action == "confirm":
- await query.edit_message_text("⏳ 正在清理错误账号...")
-
- try:
- # 获取错误账号
- error_accounts = s2a_get_error_accounts()
- if not error_accounts:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- "✅ 没有需要清理的错误账号",
- reply_markup=reply_markup
- )
- return
-
- # 批量删除
- deleted, failed = s2a_batch_delete_error_accounts(error_accounts)
-
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- f"✅ 清理完成\n\n"
- f"已删除: {deleted} 个\n"
- f"失败: {failed} 个",
- parse_mode="HTML",
- reply_markup=reply_markup
- )
- except Exception as e:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(f"❌ 清理失败: {e}", reply_markup=reply_markup)
- return
-
- # 显示确认菜单
- try:
- error_accounts = s2a_get_error_accounts()
- count = len(error_accounts) if error_accounts else 0
- except:
- count = 0
-
- if count == 0:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- "✅ 没有错误状态的账号需要清理",
- reply_markup=reply_markup
- )
- return
-
- keyboard = [
- [
- InlineKeyboardButton("✅ 确认清理", callback_data="s2a:clean_errors:confirm"),
- InlineKeyboardButton("❌ 取消", callback_data="s2a:back"),
- ],
- ]
- reply_markup = InlineKeyboardMarkup(keyboard)
-
- await query.edit_message_text(
- f"🧹 清理错误账号\n\n"
- f"发现 {count} 个错误状态账号\n\n"
- f"确认要清理这些账号吗?",
- parse_mode="HTML",
- reply_markup=reply_markup
- )
-
- async def _s2a_clean_teams(self, query, sub_action: str = ""):
- """清理已完成 Teams"""
- if sub_action == "confirm":
- await query.edit_message_text("⏳ 正在清理已完成 Teams...")
-
- try:
- completed_teams = get_completed_teams()
- if not completed_teams:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- "✅ 没有需要清理的已完成 Team",
- reply_markup=reply_markup
- )
- return
-
- # 批量删除
- removed_count = batch_remove_completed_teams()
-
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- f"✅ 清理完成\n\n"
- f"已清理: {removed_count} 个 Team",
- parse_mode="HTML",
- reply_markup=reply_markup
- )
- except Exception as e:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(f"❌ 清理失败: {e}", reply_markup=reply_markup)
- return
-
- # 显示确认菜单
- try:
- completed_teams = get_completed_teams()
- count = len(completed_teams) if completed_teams else 0
- except:
- count = 0
-
- if count == 0:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- "✅ 没有已完成的 Team 需要清理",
- reply_markup=reply_markup
- )
- return
-
- keyboard = [
- [
- InlineKeyboardButton("✅ 确认清理", callback_data="s2a:clean_teams:confirm"),
- InlineKeyboardButton("❌ 取消", callback_data="s2a:back"),
- ],
- ]
- reply_markup = InlineKeyboardMarkup(keyboard)
-
- await query.edit_message_text(
- f"🗑️ 清理已完成 Teams\n\n"
- f"发现 {count} 个已完成的 Team\n\n"
- f"确认要从 tracker 中清理这些记录吗?",
- parse_mode="HTML",
- reply_markup=reply_markup
- )
-
- async def _s2a_show_import(self, query):
- """显示导入说明"""
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
-
- await query.edit_message_text(
- "📤 导入账号到 team.json\n\n"
- "方式一: 使用命令\n"
- "/import <json内容>\n\n"
- "方式二: 发送 JSON 文件\n"
- "直接发送 .json 文件即可自动导入\n\n"
- "JSON 格式:\n"
- "[{\"email\":\"...\",\"password\":\"...\"}]",
- parse_mode="HTML",
- reply_markup=reply_markup
- )
-
- async def _s2a_test_connection(self, query):
- """测试 S2A API 连接"""
- await query.edit_message_text("⏳ 正在测试 API 连接...")
-
- import requests
-
- results = []
- apis = [
- ("S2A", S2A_API_BASE),
- ("CPA", CPA_API_BASE),
- ("CRS", CRS_API_BASE),
- ]
-
- for name, base_url in apis:
- if not base_url:
- results.append(f" {name}: ⚠️ 未配置")
- continue
-
- try:
- start = time.time()
- resp = requests.get(f"{base_url}/health", timeout=5)
- elapsed = (time.time() - start) * 1000
-
- if resp.status_code == 200:
- results.append(f" {name}: ✅ 正常 ({elapsed:.0f}ms)")
- else:
- results.append(f" {name}: ⚠️ 状态 {resp.status_code}")
- except requests.exceptions.ConnectionError:
- results.append(f" {name}: ❌ 连接失败")
- except requests.exceptions.Timeout:
- results.append(f" {name}: ❌ 超时")
- except Exception as e:
- results.append(f" {name}: ❌ {str(e)[:20]}")
-
- keyboard = [
- [InlineKeyboardButton("🔄 重新测试", callback_data="s2a:test")],
- [InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")],
- ]
- reply_markup = InlineKeyboardMarkup(keyboard)
-
- await query.edit_message_text(
- "🔄 API 连接测试\n\n" +
- "\n".join(results),
- parse_mode="HTML",
- reply_markup=reply_markup
- )
-
- async def _s2a_show_run_options(self, query):
- """显示运行选项"""
- # 获取 Team 列表
- team_count = len(TEAMS) if TEAMS else 0
-
- if team_count == 0:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- "⚠️ 没有可处理的 Team\n\n"
- "请先在 team.json 中添加账号",
- parse_mode="HTML",
- reply_markup=reply_markup
- )
- return
-
- # 显示前几个 Team 的快捷按钮
- team_buttons = []
- for i, team in enumerate(TEAMS[:6]):
- name = team.get("name", f"Team{i}")
- team_buttons.append(
- InlineKeyboardButton(f"{i}: {name[:8]}", callback_data=f"s2a:run_team:{i}")
- )
-
- # 每行2个按钮
- keyboard = []
- for i in range(0, len(team_buttons), 2):
- row = team_buttons[i:i+2]
- keyboard.append(row)
-
- keyboard.append([
- InlineKeyboardButton("🚀 处理全部", callback_data="s2a:run_all"),
- InlineKeyboardButton("🔄 继续未完成", callback_data="s2a:resume"),
- ])
- keyboard.append([InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")])
-
- reply_markup = InlineKeyboardMarkup(keyboard)
-
- await query.edit_message_text(
- f"🚀 开始处理任务\n\n"
- f"共有 {team_count} 个 Team\n\n"
- f"选择要处理的 Team:",
- parse_mode="HTML",
- reply_markup=reply_markup
- )
-
- async def _s2a_run_team(self, query, context, team_idx_str: str):
- """运行指定 Team"""
- try:
- team_idx = int(team_idx_str)
- if team_idx < 0 or team_idx >= len(TEAMS):
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:run")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- f"❌ 无效的 Team 序号: {team_idx}",
- reply_markup=reply_markup
- )
- return
-
- # 检查是否有任务在运行
- if self.current_task and not self.current_task.done():
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:run")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- f"⚠️ 当前有任务在运行: {self.current_team}\n\n"
- f"请先使用 /stop 停止当前任务",
- reply_markup=reply_markup
- )
- return
-
- team = TEAMS[team_idx]
- team_name = team.get("name", f"Team{team_idx}")
-
- await query.edit_message_text(f"🚀 开始处理 Team: {team_name}\n\n请查看日志了解进度")
-
- # 启动任务
- self.current_team = team_name
- self.current_task = asyncio.create_task(
- self._run_single_team_task(team_idx, query.message.chat_id)
- )
-
- except ValueError:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:run")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text("❌ 无效的参数", reply_markup=reply_markup)
-
- async def _s2a_run_all(self, query, context):
- """运行所有 Team"""
- if self.current_task and not self.current_task.done():
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:run")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- f"⚠️ 当前有任务在运行: {self.current_team}\n\n"
- f"请先使用 /stop 停止当前任务",
- reply_markup=reply_markup
- )
- return
-
- if not TEAMS:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text("⚠️ 没有可处理的 Team", reply_markup=reply_markup)
- return
-
- await query.edit_message_text(f"🚀 开始处理所有 {len(TEAMS)} 个 Team\n\n请查看日志了解进度")
-
- self.current_team = "全部"
- self.current_task = asyncio.create_task(
- self._run_all_teams_task(query.message.chat_id)
- )
-
- async def _s2a_resume(self, query, context):
- """继续处理未完成账号"""
- if self.current_task and not self.current_task.done():
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:run")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- f"⚠️ 当前有任务在运行: {self.current_team}\n\n"
- f"请先使用 /stop 停止当前任务",
- reply_markup=reply_markup
- )
- return
-
- # 检查未完成账号
- incomplete = get_all_incomplete_accounts()
- if not incomplete:
- keyboard = [[InlineKeyboardButton("◀️ 返回", callback_data="s2a:back")]]
- reply_markup = InlineKeyboardMarkup(keyboard)
- await query.edit_message_text(
- "✅ 没有未完成的账号\n\n所有账号都已处理完成",
- reply_markup=reply_markup
- )
- return
-
- await query.edit_message_text(f"🔄 继续处理 {len(incomplete)} 个未完成账号\n\n请查看日志了解进度")
-
- self.current_team = "恢复"
- self.current_task = asyncio.create_task(
- self._run_resume_task(query.message.chat_id)
- )
-
- # ==================== AutoGPTPlus 管理面板 ====================
-
@admin_only
async def cmd_autogptplus(self, update: Update, context: ContextTypes.DEFAULT_TYPE):
"""AutoGPTPlus 配置管理 - 交互式菜单"""