import asyncio import sys import os from datetime import datetime from typing import Dict, Any, List, Callable, Awaitable from loguru import logger # 导入功能模块 import upload_accounts import reset_extracted import reupload_all try: import retry_failed has_retry_failed = True except ImportError: has_retry_failed = False try: import reset_retrying has_reset_retrying = True except ImportError: has_reset_retrying = False try: import delete_db has_delete_db = True except ImportError: has_delete_db = False from core.config import Config from core.logger import setup_logger from core.database import DatabaseManager class MenuSystem: def __init__(self): self.config = Config.from_yaml() self.logger = setup_logger(self.config) self.db_manager = DatabaseManager(self.config) # 定义菜单项 self.menu_items = [ { "key": "1", "title": "重新上传所有账号", "description": "重置所有账号的提取状态并重新上传到API服务器(推荐)", "handler": self.reupload_all_menu }, { "key": "2", "title": "上传账号到API", "description": "将注册成功的账号上传到API服务器", "handler": self.upload_accounts_menu }, { "key": "3", "title": "重置账号提取状态", "description": "将账号的extracted字段重置为0,允许重新上传", "handler": self.reset_extracted_menu } ] # 添加可选功能 if has_retry_failed: self.menu_items.append({ "key": "4", "title": "重试失败账号", "description": "重新尝试注册失败的账号", "handler": self.retry_failed_menu }) if has_reset_retrying: self.menu_items.append({ "key": "5", "title": "重置正在重试的账号", "description": "将状态为retrying的账号重置为failed", "handler": self.reset_retrying_menu }) if has_delete_db: self.menu_items.append({ "key": "6", "title": "删除数据库记录", "description": "根据条件删除数据库中的记录", "handler": self.delete_db_menu }) # 添加退出选项 self.menu_items.append({ "key": "q", "title": "退出程序", "description": "退出菜单系统", "handler": self.exit_menu }) async def initialize(self): """初始化数据库连接""" await self.db_manager.initialize() async def cleanup(self): """清理资源""" await self.db_manager.cleanup() async def show_menu(self): """显示主菜单""" os.system('cls' if os.name == 'nt' else 'clear') print("\n===== Cursor 账号管理系统 =====\n") # 显示菜单项 for item in self.menu_items: print(f"{item['key']}. {item['title']} - {item['description']}") print("\n请选择功能 (输入对应数字或q退出):", end=" ") choice = input().strip().lower() # 查找选择的菜单项 selected_item = next((item for item in self.menu_items if item["key"] == choice), None) if selected_item: await selected_item["handler"]() else: print("\n无效的选择,请重试!") input("按Enter键继续...") await self.show_menu() async def upload_accounts_menu(self): """上传账号菜单""" os.system('cls' if os.name == 'nt' else 'clear') print("\n===== 上传账号到API =====\n") # 统计账号状态 resetter = reset_extracted.ExtractedResetter() await resetter.initialize() stats = await resetter.count_success_accounts() await resetter.cleanup() print(f"当前状态: 总成功账号: {stats['total']}, 已提取: {stats['extracted']}, 未提取: {stats['not_extracted']}") if stats['not_extracted'] == 0: print("\n没有待上传的账号。是否要重置所有账号的提取状态?(y/n)", end=" ") reset_choice = input().strip().lower() if reset_choice == 'y': await self.reset_all_extracted() print("\n已重置所有账号的提取状态,现在可以上传。") else: print("\n未重置状态,无法上传账号。") input("\n按Enter键返回主菜单...") return print("\n选项:") print("1. 预览待上传账号 (dry-run)") print("2. 上传所有账号") print("3. 上传指定批次数量") print("4. 修改批次大小 (默认: 100)") print("5. 禁用代理") print("b. 返回主菜单") choice = input("\n请选择操作: ").strip().lower() args = [] use_proxy = True batch_size = 100 max_batches = 0 if choice == "1": args = ["--dry-run"] elif choice == "2": args = [] # 使用默认设置上传所有 elif choice == "3": try: batches = int(input("请输入要处理的批次数量: ").strip()) if batches > 0: args = [f"--batches", f"{batches}"] max_batches = batches else: print("批次数量必须大于0") input("按Enter键继续...") await self.upload_accounts_menu() return except ValueError: print("无效的输入,必须是整数") input("按Enter键继续...") await self.upload_accounts_menu() return elif choice == "4": try: size = int(input("请输入批次大小: ").strip()) if size > 0: batch_size = size args = [f"--batch-size", f"{size}"] else: print("批次大小必须大于0") input("按Enter键继续...") await self.upload_accounts_menu() return except ValueError: print("无效的输入,必须是整数") input("按Enter键继续...") await self.upload_accounts_menu() return elif choice == "5": use_proxy = False args = ["--no-proxy"] elif choice == "b": await self.show_menu() return else: print("无效的选择,请重试!") input("按Enter键继续...") await self.upload_accounts_menu() return # 处理输入的批次大小和批次数量组合 if choice == "3" and choice == "4": args = [f"--batches", f"{max_batches}", f"--batch-size", f"{batch_size}"] # 添加代理设置 if not use_proxy and "--no-proxy" not in args: args.append("--no-proxy") print(f"\n准备执行命令: python upload_accounts.py {' '.join(args)}") print("按Enter键开始执行,或按Ctrl+C取消...") input() # 存储原始sys.argv并替换 original_argv = sys.argv.copy() sys.argv = [sys.argv[0]] + args try: # 执行上传账号功能 await upload_accounts.main() except Exception as e: self.logger.error(f"执行上传账号出错: {e}") finally: # 恢复sys.argv sys.argv = original_argv input("\n操作完成,按Enter键返回主菜单...") await self.show_menu() async def reset_extracted_menu(self): """重置提取状态菜单""" os.system('cls' if os.name == 'nt' else 'clear') print("\n===== 重置账号提取状态 =====\n") # 统计账号状态 resetter = reset_extracted.ExtractedResetter() await resetter.initialize() stats = await resetter.count_success_accounts() print(f"当前状态: 总成功账号: {stats['total']}, 已提取: {stats['extracted']}, 未提取: {stats['not_extracted']}") print("\n选项:") print("1. 预览账号状态 (dry-run)") print("2. 重置所有账号的提取状态") print("3. 重置指定模式的账号提取状态") print("b. 返回主菜单") choice = input("\n请选择操作: ").strip().lower() args = [] if choice == "1": args = ["--dry-run"] elif choice == "2": print("\n确定要重置所有账号的提取状态吗?这将允许已提取的账号再次上传。(y/n)", end=" ") confirm = input().strip().lower() if confirm != 'y': print("操作已取消") input("按Enter键继续...") await self.reset_extracted_menu() return elif choice == "3": pattern = input("请输入邮箱匹配模式 (例如: outlook.com): ").strip() if pattern: args = ["--pattern", pattern] else: print("模式不能为空") input("按Enter键继续...") await self.reset_extracted_menu() return elif choice == "b": await self.show_menu() return else: print("无效的选择,请重试!") input("按Enter键继续...") await self.reset_extracted_menu() return # 执行重置 print(f"\n准备执行命令: python reset_extracted.py {' '.join(args)}") print("按Enter键开始执行,或按Ctrl+C取消...") input() # 存储原始sys.argv并替换 original_argv = sys.argv.copy() sys.argv = [sys.argv[0]] + args try: # 执行重置操作 await reset_extracted.main() except Exception as e: self.logger.error(f"执行重置提取状态出错: {e}") finally: # 恢复sys.argv sys.argv = original_argv await resetter.cleanup() input("\n操作完成,按Enter键返回主菜单...") await self.show_menu() async def reupload_all_menu(self): """重新上传所有账号菜单""" os.system('cls' if os.name == 'nt' else 'clear') print("\n===== 重新上传所有账号 =====\n") print("此操作将:") print("1. 重置所有成功账号的提取状态") print("2. 重新上传所有账号到API服务器") print("\n警告: 此操作可能需要较长时间,请确保网络连接稳定。") print("是否继续? (y/n)", end=" ") choice = input().strip().lower() if choice != 'y': print("\n操作已取消") input("\n按Enter键返回主菜单...") await self.show_menu() return try: # 执行重新上传 await reupload_all.main() except Exception as e: self.logger.error(f"重新上传过程中出错: {e}") finally: input("\n操作完成,按Enter键返回主菜单...") await self.show_menu() async def retry_failed_menu(self): """重试失败账号菜单""" if not has_retry_failed: print("\n功能不可用: retry_failed.py 未找到") input("按Enter键返回主菜单...") await self.show_menu() return os.system('cls' if os.name == 'nt' else 'clear') print("\n===== 重试失败账号 =====\n") print("此功能将重新尝试注册失败的账号。") # 这里可以添加更多的统计信息显示 print("\n选项:") print("1. 执行账号重试") print("b. 返回主菜单") choice = input("\n请选择操作: ").strip().lower() if choice == "1": # 执行重试失败账号功能 print("\n准备重试失败账号...") print("按Enter键开始执行,或按Ctrl+C取消...") input() # 存储原始sys.argv original_argv = sys.argv.copy() sys.argv = [sys.argv[0]] try: # 执行重试功能 await retry_failed.main() except Exception as e: self.logger.error(f"执行重试失败账号出错: {e}") finally: # 恢复sys.argv sys.argv = original_argv elif choice == "b": await self.show_menu() return else: print("无效的选择,请重试!") input("按Enter键继续...") await self.retry_failed_menu() return input("\n操作完成,按Enter键返回主菜单...") await self.show_menu() async def reset_retrying_menu(self): """重置正在重试的账号菜单""" if not has_reset_retrying: print("\n功能不可用: reset_retrying.py 未找到") input("按Enter键返回主菜单...") await self.show_menu() return os.system('cls' if os.name == 'nt' else 'clear') print("\n===== 重置正在重试的账号 =====\n") print("此功能将状态为retrying的账号重置为failed。") # 这里可以添加更多的统计信息显示 print("\n选项:") print("1. 执行重置retrying状态") print("b. 返回主菜单") choice = input("\n请选择操作: ").strip().lower() if choice == "1": # 执行重置retrying状态功能 print("\n准备重置正在重试的账号...") print("按Enter键开始执行,或按Ctrl+C取消...") input() # 存储原始sys.argv original_argv = sys.argv.copy() sys.argv = [sys.argv[0]] try: # 执行重置功能 await reset_retrying.main() except Exception as e: self.logger.error(f"执行重置retrying状态出错: {e}") finally: # 恢复sys.argv sys.argv = original_argv elif choice == "b": await self.show_menu() return else: print("无效的选择,请重试!") input("按Enter键继续...") await self.reset_retrying_menu() return input("\n操作完成,按Enter键返回主菜单...") await self.show_menu() async def delete_db_menu(self): """删除数据库记录菜单""" if not has_delete_db: print("\n功能不可用: delete_db.py 未找到") input("按Enter键返回主菜单...") await self.show_menu() return os.system('cls' if os.name == 'nt' else 'clear') print("\n===== 删除数据库记录 =====\n") print("此功能将根据条件删除数据库中的记录。") print("警告: 删除操作不可恢复,请谨慎操作!") print("\n选项:") print("1. 交互式删除(按状态)") print("2. 删除所有记录") print("b. 返回主菜单") choice = input("\n请选择操作: ").strip().lower() if choice == "b": await self.show_menu() return elif choice == "2": print("\n警告: 此操作将删除数据库中的所有记录!") print("确定要继续吗?(y/n)", end=" ") confirm = input().strip().lower() if confirm != 'y': print("操作已取消") input("按Enter键继续...") await self.delete_db_menu() return # 执行全部删除 args = ["--all", "--confirm"] print(f"\n准备执行命令: python delete_db.py {' '.join(args)}") print("按Enter键开始执行,或按Ctrl+C取消...") input() # 存储原始sys.argv original_argv = sys.argv.copy() sys.argv = [sys.argv[0]] + args try: # 执行删除功能 await delete_db.main() except Exception as e: self.logger.error(f"执行删除数据库记录出错: {e}") finally: # 恢复sys.argv sys.argv = original_argv input("\n操作完成,按Enter键返回主菜单...") await self.show_menu() return elif choice == "1": # 执行交互式删除 args = ["--interactive"] print(f"\n准备执行命令: python delete_db.py {' '.join(args)}") print("按Enter键开始执行,或按Ctrl+C取消...") input() # 存储原始sys.argv original_argv = sys.argv.copy() sys.argv = [sys.argv[0]] + args try: # 执行删除功能 await delete_db.main() except Exception as e: self.logger.error(f"执行删除数据库记录出错: {e}") finally: # 恢复sys.argv sys.argv = original_argv input("\n操作完成,按Enter键返回主菜单...") await self.show_menu() return else: print("无效的选择,请重试!") input("按Enter键继续...") await self.delete_db_menu() return async def reset_all_extracted(self): """重置所有账号的提取状态""" resetter = reset_extracted.ExtractedResetter() await resetter.initialize() try: await resetter.reset_extracted() finally: await resetter.cleanup() async def exit_menu(self): """退出菜单""" print("\n感谢使用,再见!") sys.exit(0) async def main(): menu = MenuSystem() try: await menu.initialize() await menu.show_menu() except KeyboardInterrupt: print("\n程序被用户中断") except Exception as e: logger.error(f"程序执行出错: {e}") finally: await menu.cleanup() if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: print("\n程序被用户中断") except Exception as e: print(f"程序崩溃: {e}")