diff --git a/api_register.py b/api_register.py index c9b3b63..9ce8950 100644 --- a/api_register.py +++ b/api_register.py @@ -20,6 +20,20 @@ except ImportError: import requests +def _is_shutdown_requested(): + """检查是否收到停止请求""" + try: + import run + return run._shutdown_requested + except Exception: + return False + + +class ShutdownRequested(Exception): + """用户请求停止异常""" + pass + + def log_status(step, message): """日志输出""" timestamp = time.strftime("%H:%M:%S") @@ -330,12 +344,20 @@ def get_verification_code_api(target_email: str, mail_api_base: str, mail_api_to Returns: str: 验证码,失败返回空字符串 + + Raises: + ShutdownRequested: 用户请求停止时抛出 """ log_status("API监听", "正在监听邮件...") headers = {"Authorization": mail_api_token, "Content-Type": "application/json"} start_time = time.time() for i in range(max_retries): + # 检查停止请求 + if _is_shutdown_requested(): + log_status("停止", "[!] 检测到停止请求,中断邮件监听") + raise ShutdownRequested("用户请求停止") + elapsed = int(time.time() - start_time) try: url = f"{mail_api_base}/api/public/emailList" @@ -389,6 +411,9 @@ def api_register_flow( Returns: ChatGPTAPIRegister: 成功返回 reg 对象,失败返回 None + + Raises: + ShutdownRequested: 用户请求停止时抛出 """ def log_cb(msg): if progress_callback: @@ -396,57 +421,72 @@ def api_register_flow( else: log_progress(msg) + def check_shutdown(): + """检查停止请求""" + if _is_shutdown_requested(): + log_cb("[!] 检测到停止请求") + raise ShutdownRequested("用户请求停止") + reg = ChatGPTAPIRegister(proxy=proxy) try: + check_shutdown() log_status("API注册", "初始化会话...") if not reg.init_session(): log_cb("[X] 初始化失败") return None log_cb("[OK] 会话初始化成功") + check_shutdown() log_status("API注册", "获取授权 URL...") if not reg.get_authorize_url(email): log_cb("[X] 获取授权 URL 失败") return None log_cb("[OK] 授权 URL 获取成功") + check_shutdown() log_status("API注册", "开始授权流程...") if not reg.start_authorize(): log_cb("[X] 授权流程启动失败") return None log_cb("[OK] 授权流程已启动") + check_shutdown() log_status("API注册", "注册账户...") if not reg.register(email, password): log_cb("[X] 注册失败") return None log_cb("[OK] 账户注册成功") + check_shutdown() log_status("API注册", "发送验证邮件...") if not reg.send_verification_email(): log_cb("[X] 发送验证邮件失败") return None log_cb("[OK] 验证邮件已发送") + check_shutdown() # 获取验证码 otp_code = get_verification_code_api(email, mail_api_base, mail_api_token) if not otp_code: log_cb("[X] 未能获取验证码") return None + check_shutdown() log_status("API注册", f"验证 OTP: {otp_code}") if not reg.validate_otp(otp_code): log_cb("[X] OTP 验证失败") return None log_cb("[OK] OTP 验证成功") + check_shutdown() # 创建账户(带重试) log_status("API注册", "创建账户...") create_success = reg.create_account(real_name, birthdate) # 如果创建失败,重新获取验证码再试一次 if not create_success: + check_shutdown() log_cb("[!] 创建账户失败,尝试重新验证...") # 重新发送验证邮件 @@ -456,6 +496,7 @@ def api_register_flow( return None log_cb("[OK] 验证邮件已重新发送") + check_shutdown() # 重新获取验证码 time.sleep(2) # 等待新邮件 otp_code = get_verification_code_api(email, mail_api_base, mail_api_token) @@ -463,12 +504,14 @@ def api_register_flow( log_cb("[X] 未能获取新验证码") return None + check_shutdown() log_status("API注册", f"重新验证 OTP: {otp_code}") if not reg.validate_otp(otp_code): log_cb("[X] OTP 重新验证失败") return None log_cb("[OK] OTP 重新验证成功") + check_shutdown() # 再次尝试创建账户 log_status("API注册", "重新创建账户...") if not reg.create_account(real_name, birthdate): @@ -477,6 +520,7 @@ def api_register_flow( log_cb("[OK] 账户创建成功") + check_shutdown() # 验证 session 是否有效 token = reg.get_session_token() if token: @@ -486,6 +530,8 @@ def api_register_flow( return reg + except ShutdownRequested: + raise # 重新抛出停止请求异常 except Exception as e: log_status("错误", f"注册异常: {e}") return None diff --git a/auto_gpt_team.py b/auto_gpt_team.py index 09fb61c..f4eca8d 100644 --- a/auto_gpt_team.py +++ b/auto_gpt_team.py @@ -30,6 +30,7 @@ try: api_login_flow, is_api_mode_available, get_verification_code_api, + ShutdownRequested, ) API_MODE_AVAILABLE = is_api_mode_available() except ImportError: @@ -37,6 +38,7 @@ except ImportError: ChatGPTAPIRegister = None api_register_flow = None api_login_flow = None + ShutdownRequested = Exception # 回退到基础异常类 # ================= 配置加载 ================= try: @@ -2432,6 +2434,9 @@ def run_single_registration_api(progress_callback=None, step_callback=None, prox log_status("失败", "注册成功但支付/获取token失败") return {"success": False, "error": "支付流程失败", "account": email, "password": password} + except ShutdownRequested: + log_status("停止", "[!] 用户请求停止") + return {"success": False, "error": "用户停止", "stopped": True, "account": email, "password": password} except Exception as e: error_msg = str(e) # 只有连接断开才认为是停止请求 diff --git a/telegram_bot.py b/telegram_bot.py index 4925087..7fb7b50 100644 --- a/telegram_bot.py +++ b/telegram_bot.py @@ -2716,6 +2716,54 @@ class ProvisionerBot: except Exception as e: await update.message.reply_text(f"❌ 修改配置失败: {e}") + async def _test_mail_api_connection(self, mail_api_base: str, mail_api_token: str, domain: str) -> tuple[bool, str]: + """测试邮件 API 连接 + + Args: + mail_api_base: 邮件 API 地址 + mail_api_token: 邮件 API Token + domain: 测试用的邮箱域名 + + Returns: + tuple: (success, message) + """ + import requests + import random + import string + + try: + # 生成测试邮箱 + random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=8)) + test_email = f"test-{random_str}@{domain.lstrip('@')}" + + # 测试 API 连接 + url = f"{mail_api_base}/api/public/emailList" + headers = { + "Authorization": mail_api_token, + "Content-Type": "application/json" + } + payload = { + "toEmail": test_email, + "timeSort": "desc", + "size": 1 + } + + response = requests.post(url, headers=headers, json=payload, timeout=10) + data = response.json() + + if data.get("code") == 200: + return True, "邮件 API 连接正常" + else: + error_msg = data.get("message", "未知错误") + return False, f"API 响应异常: {error_msg}" + + except requests.exceptions.Timeout: + return False, "连接超时,无法连接到邮件 API 服务器" + except requests.exceptions.ConnectionError: + return False, "连接失败,请检查 mail_api_base 配置" + except Exception as e: + return False, f"测试失败: {e}" + @admin_only async def cmd_team_register(self, update: Update, context: ContextTypes.DEFAULT_TYPE): """开始 GPT Team 自动订阅注册""" @@ -2757,6 +2805,25 @@ class ProvisionerBot: parse_mode="HTML" ) return + + # 测试邮件 API 连接 + await update.message.reply_text("⏳ 正在检测邮件 API 连接...") + + import random + test_domain = random.choice(domains) + success, message = await self._test_mail_api_connection(MAIL_API_BASE, MAIL_API_TOKEN, test_domain) + + if not success: + await update.message.reply_text( + f"❌ 邮件 API 连接失败\n\n" + f"错误: {message}\n\n" + f"请检查配置后重试:\n" + f"• mail_api_base: {MAIL_API_BASE}\n" + f"• mail_api_token: {'已配置' if MAIL_API_TOKEN else '未配置'}", + parse_mode="HTML" + ) + return + except ImportError: await update.message.reply_text("❌ auto_gpt_team 模块未找到") return @@ -2943,6 +3010,8 @@ class ProvisionerBot: try: import run if run._shutdown_requested: + with step_lock: + current_step[0] = "用户请求停止..." break except: pass @@ -2953,6 +3022,13 @@ class ProvisionerBot: import functools def run_with_callback(): + # 在执行前再次检查停止请求 + try: + import run as run_module + if run_module._shutdown_requested: + return {"success": False, "error": "用户停止", "stopped": True} + except: + pass return run_single_registration_auto( progress_callback=None, step_callback=step_callback @@ -2971,6 +3047,8 @@ class ProvisionerBot: if result.get("stopped"): # 被 /stop 命令中断,不计入失败 log.info("注册被用户停止") + with step_lock: + current_step[0] = "已停止" break elif result.get("success"): success_count += 1 @@ -2985,12 +3063,35 @@ class ProvisionerBot: else: fail_count += 1 log.warning(f"注册失败: {result.get('error', '未知错误')}") + except asyncio.CancelledError: + log.info("注册任务被取消") + with step_lock: + current_step[0] = "已取消" + break except Exception as e: fail_count += 1 log.error(f"注册异常: {e}") # 清理浏览器进程 cleanup_chrome_processes() + + # 每次注册后检查停止请求 + try: + import run + if run._shutdown_requested: + with step_lock: + current_step[0] = "用户请求停止..." + break + except: + pass + + # 检查是否被停止 + stopped = False + try: + import run + stopped = run._shutdown_requested + except: + pass # 停止进度更新任务 progress_task.cancel() @@ -3001,8 +3102,15 @@ class ProvisionerBot: # 完成进度 progress_bar = '▰' * 20 + completed = success_count + fail_count + + if stopped: + status_text = f"🛑 注册已停止 {completed}/{count}" + else: + status_text = f"🎉 注册完成! {success_count}/{count}" + await progress_msg.edit_text( - f"🎉 注册完成! {success_count}/{count}\n" + f"{status_text}\n" f"{progress_bar}\n\n" f"✅ 成功: {success_count}\n" f"❌ 失败: {fail_count}",