From d155a2bcd7435f372a5a0f0b778277846fde4c37 Mon Sep 17 00:00:00 2001 From: real-jacket <1762982273@qq.com> Date: Tue, 1 Apr 2025 21:49:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E9=82=AE=E7=AE=B1=E6=B3=A8=E5=86=8C=E5=9C=BA=E6=99=AF?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8C=E4=BC=98=E5=8C=96=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E3=80=82=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=B8=85=E7=90=86=E5=BE=85=E5=A4=84=E7=90=86=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81=E8=AF=B7=E6=B1=82=E7=9A=84=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E8=BF=9B=E5=89=8D=E7=AB=AF=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=EF=BC=8C=E7=A1=AE=E4=BF=9D=E5=9C=A8=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=8E=B7=E5=8F=96=E9=AA=8C=E8=AF=81=E7=A0=81=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=97=B6=E8=BD=AC=E4=B8=BA=E6=89=8B=E5=8A=A8=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E6=A8=A1=E5=BC=8F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.py | 87 ++++++++++++++++++++-- cursor_pro_keep_alive.py | 52 +++++++++---- get_email_code.py | 129 +++++++++++++++++++++++++-------- index.html | 16 ++++ static/js/app.js | 153 ++++++++++++++++++++++++++++++++++----- 5 files changed, 369 insertions(+), 68 deletions(-) diff --git a/api.py b/api.py index 636e765..4500a83 100644 --- a/api.py +++ b/api.py @@ -30,6 +30,9 @@ from dotenv import load_dotenv import json import time import uuid +import aiosqlite +import sqlite3 +import logging # 从get_email_code.py导入验证码请求字典 from get_email_code import EmailVerificationHandler @@ -711,6 +714,12 @@ async def stop_registration(): background_tasks["registration_task"] = None registration_status["is_running"] = False registration_status["last_status"] = "manually stopped" + + # 清理所有待处理的验证码请求 + for email_id, request_info in list(get_email_code.pending_verification_codes.items()): + if request_info["status"] == "pending": + get_email_code.pending_verification_codes.pop(email_id, None) + info(f"已清理待处理的验证码请求: {email_id}") return {"success": True, "message": "Registration task stopped successfully"} except Exception as e: @@ -1475,23 +1484,48 @@ async def register_with_custom_email(request: CustomRegistrationRequest): # 调用注册函数,传入自定义邮箱 try: - success = await asyncio.get_event_loop().run_in_executor( + result = await asyncio.get_event_loop().run_in_executor( None, lambda: register_account(custom_email=request.email) ) - if success: + # 处理布尔值情况 - True表示成功,False表示失败 + if result is True: return JSONResponse( content={ "success": True, "message": f"使用邮箱 {request.email} 注册成功" } ) + elif result is False: + return JSONResponse( + status_code=500, + content={ + "success": False, + "message": f"使用邮箱 {request.email} 注册失败,请检查日志获取详细信息" + } + ) + # 字符串状态码处理 + elif result == "SUCCESS": + return JSONResponse( + content={ + "success": True, + "message": f"使用邮箱 {request.email} 注册成功" + } + ) + elif result == "EMAIL_VERIFICATION_FAILED": + return JSONResponse( + status_code=400, + content={ + "success": False, + "message": "验证码获取失败,请检查邮箱是否正确或尝试其他邮箱" + } + ) else: return JSONResponse( status_code=500, content={ "success": False, - "message": f"使用邮箱 {request.email} 注册失败" + "message": f"使用邮箱 {request.email} 注册失败: {result}" } ) except SystemExit: @@ -1518,15 +1552,30 @@ async def register_with_custom_email(request: CustomRegistrationRequest): @app.get("/verification/pending") async def check_pending_verification(): """检查是否有等待验证码输入的请求""" - # 返回所有等待验证的邮箱ID及邮箱地址 result = [] + for email_id, email_info in get_email_code.pending_verification_codes.items(): - if email_info.get("status") == "pending": + if email_info["status"] == "pending": + request_data = { + "id": email_id, + "email": email_info["email"], + "created_at": email_info["created_at"] + } + # 如果存在auto_failure标记,添加到结果中 + if "auto_failure" in email_info: + request_data["auto_failure"] = email_info["auto_failure"] + result.append(request_data) + elif email_info["status"] == "failed": + # 返回失败状态的请求,让前端知道验证失败 result.append({ "id": email_id, - "email": email_info.get("email"), - "created_at": email_info.get("created_at") + "email": email_info["email"], + "created_at": email_info["created_at"], + "status": "failed", + "message": email_info.get("message", "验证码获取失败") }) + # 返回后从字典中删除,避免重复通知 + get_email_code.pending_verification_codes.pop(email_id, None) return { "success": True, @@ -1560,6 +1609,30 @@ async def submit_verification_code(code_data: Dict): "message": "验证码已提交" } +@app.get("/verification/clear", tags=["Verification"]) +async def clear_verification_requests(): + """清理所有待处理的验证码请求""" + try: + count = 0 + for email_id, request_info in list(get_email_code.pending_verification_codes.items()): + if request_info["status"] == "pending": + get_email_code.pending_verification_codes.pop(email_id, None) + count += 1 + + info(f"已清理 {count} 个待处理的验证码请求") + + return { + "success": True, + "message": f"已清理 {count} 个待处理的验证码请求" + } + except Exception as e: + error(f"清理验证码请求失败: {str(e)}") + error(traceback.format_exc()) + return { + "success": False, + "message": f"清理验证码请求失败: {str(e)}" + } + if __name__ == "__main__": # 设置Web模式环境变量 diff --git a/cursor_pro_keep_alive.py b/cursor_pro_keep_alive.py index 72b73b8..62c693b 100644 --- a/cursor_pro_keep_alive.py +++ b/cursor_pro_keep_alive.py @@ -225,8 +225,13 @@ def sign_up_account(browser, tab, account_info): info("注册限制") return "SIGNUP_RESTRICTED" - # 创建邮件处理器,传入自定义邮箱 - email_handler = EmailVerificationHandler(custom_email=account_info["email"]) + # 判断是否为自定义邮箱注册场景 + is_custom_email_registration = account_info.get("is_custom_registration", False) + + # 创建邮件处理器,只在自定义邮箱注册场景下传入邮箱参数 + email_handler = EmailVerificationHandler( + custom_email=account_info["email"] if is_custom_email_registration else None + ) i = 0 while i < 5: try: @@ -241,15 +246,20 @@ def sign_up_account(browser, tab, account_info): global EMAIL_CODE_TYPE # 获取当前的EMAIL_CODE_TYPE值 local_email_code_type = EMAIL_CODE_TYPE - if account_info["email"] != f"{EMAIL_USERNAME}@{EMAIL_DOMAIN}": - info(f"检测到使用自定义邮箱 {account_info['email']},确保使用手动输入验证码") - # 使用自定义邮箱时确保使用手动输入 - if EMAIL_CODE_TYPE != "INPUT": - EMAIL_CODE_TYPE = "INPUT" - # 切换到邮箱标签页获取验证码 + if is_custom_email_registration: + info(f"检测到自定义邮箱注册场景,使用手动输入验证码模式") + # 自定义邮箱注册场景确保使用手动输入 + if EMAIL_CODE_TYPE != "INPUT": + # 临时切换为手动输入模式,在获取验证码完成后会恢复原值 + EMAIL_CODE_TYPE = "INPUT" + else: + info(f"检测到任务注册场景,使用配置的验证码获取方式: {EMAIL_CODE_TYPE}") + + # 获取验证码 - 对于自定义邮箱,EmailVerificationHandler会直接进入手动输入模式 + # 不会尝试从临时邮箱服务获取验证码 code = email_handler.get_verification_code( - source_email=account_info["email"] + source_email=account_info["email"] if is_custom_email_registration else None ) # 如果临时修改了EMAIL_CODE_TYPE,恢复原值 @@ -258,7 +268,14 @@ def sign_up_account(browser, tab, account_info): if code is None: info("未获取到验证码...系统异常,正在退出....") - return "EMAIL_GET_CODE_FAILED" + return "EMAIL_VERIFICATION_FAILED" + + # 在自动获取验证码失败时可能已转为手动输入模式 + if not is_custom_email_registration and EMAIL_CODE_TYPE != "INPUT": + info(f"成功获取到验证码: {code},继续注册流程") + else: + info(f"通过手动输入获取到验证码: {code},继续注册流程") + info(f"输入验证码: {code}") i = 0 for digit in code: @@ -447,15 +464,17 @@ def main(custom_email=None): try: # 如果使用自定义邮箱,切换验证码获取模式为手动输入 if custom_email: - info(f"使用自定义邮箱 {custom_email},切换为手动输入验证码模式") + info(f"使用自定义邮箱注册场景 {custom_email},切换为手动输入验证码模式") EMAIL_CODE_TYPE = "INPUT" + else: + info(f"使用任务注册场景,使用配置的验证码获取方式: {EMAIL_CODE_TYPE}") # 初始化邮箱验证处理器时传入自定义邮箱 email_handler = EmailVerificationHandler(custom_email=custom_email) if email_handler.check(): info('邮箱服务连接正常,开始注册!') else: - if EMAIL_CODE_TYPE == "API": + if EMAIL_CODE_TYPE == "API" and not custom_email: error('邮箱服务连接失败,并且验证码为API获取,结束注册!') return False else: @@ -468,8 +487,15 @@ def main(custom_email=None): try: # 使用自定义邮箱或生成新邮箱 account_info = email_generator.get_account_info(email=custom_email) + + # 标记是否为自定义邮箱注册场景 + if custom_email: + account_info["is_custom_registration"] = True + else: + account_info["is_custom_registration"] = False + info( - f"初始化账号信息成功 => 邮箱: {account_info['email']}, 用户名: {account_info['first_name']}, 密码: {account_info['password']}" + f"初始化账号信息成功 => 邮箱: {account_info['email']}, 用户名: {account_info['first_name']}, 密码: {account_info['password']}, 场景: {'自定义邮箱' if account_info['is_custom_registration'] else '任务注册'}" ) signup_tab = browser.new_tab(LOGIN_URL) diff --git a/get_email_code.py b/get_email_code.py index 9c620f5..2b52f17 100644 --- a/get_email_code.py +++ b/get_email_code.py @@ -63,6 +63,11 @@ class EmailVerificationHandler: info(f"已启用代理: {EMAIL_PROXY_ADDRESS}") def check(self): + # 如果是自定义邮箱场景,跳过邮箱连接检查,直接返回成功 + if self.custom_email is not None: + info(f"自定义邮箱注册场景 {self.custom_email},跳过临时邮箱连接检查") + return True + # 如果有自定义邮箱,优先使用它 username = self.username domain = self.domain @@ -119,19 +124,18 @@ class EmailVerificationHandler: # 记录当前的验证码获取模式和邮箱信息 info(f"验证码获取模式: {EMAIL_CODE_TYPE}, 使用邮箱: {source_email}") - # 首先检查是否是自定义邮箱(非系统配置的邮箱) - if source_email and '@' in source_email: - username, domain = source_email.split('@', 1) - if username != EMAIL_USERNAME or domain != EMAIL_DOMAIN: - info(f"检测到使用非系统配置邮箱: {source_email},强制使用手动输入模式") - return self.prompt_manual_code(source_email) + # 通过self.custom_email判断是否为自定义邮箱注册场景,而不是比较source_email + is_custom_email_scenario = self.custom_email is not None - # 如果是INPUT模式,始终直接进入手动输入 - if EMAIL_CODE_TYPE == "INPUT": - info("EMAIL_CODE_TYPE设为INPUT,跳过自动获取,直接手动输入") + # 如果是INPUT模式或者是自定义邮箱场景,直接进入手动输入 + if EMAIL_CODE_TYPE == "INPUT" or is_custom_email_scenario: + if is_custom_email_scenario: + info("检测到自定义邮箱注册场景,直接使用手动输入验证码模式") + else: + info("EMAIL_CODE_TYPE设为INPUT,跳过自动获取,直接手动输入") return self.prompt_manual_code(source_email) - # 以下是自动获取验证码的逻辑,只有在EMAIL_CODE_TYPE不是INPUT且不是自定义邮箱时才会执行 + # 以下是自动获取验证码的逻辑,只有在EMAIL_CODE_TYPE不是INPUT且不是自定义邮箱场景时才会执行 max_retries = max_retries or EMAIL_VERIFICATION_RETRIES wait_time = wait_time or EMAIL_VERIFICATION_WAIT info(f"开始获取邮箱验证码=>最大重试次数:{max_retries}, 等待时间:{wait_time}") @@ -169,7 +173,25 @@ class EmailVerificationHandler: else: error(f"已达到最大重试次数({max_retries}),获取验证码失败") - # 所有自动尝试都失败后,询问是否手动输入 + # Web模式下,更新等待验证码请求状态为失败 + web_mode = os.environ.get("CURSOR_AUTO_REGISTER_WEB", "").lower() == "true" + if web_mode and source_email: + # 查找该邮箱的待处理验证码请求 + global pending_verification_codes + for email_id, request_info in list(pending_verification_codes.items()): + if request_info.get("email") == source_email and request_info.get("status") == "pending": + # 更新状态为失败 + pending_verification_codes[email_id]["status"] = "failed" + pending_verification_codes[email_id]["message"] = f"验证码获取失败,已尝试{max_retries}次" + info(f"已更新验证码请求 {email_id} 状态为失败") + break + + # 在Web模式下,自动获取失败后转为前端手动输入,而不直接返回失败 + if web_mode: + info("自动获取验证码失败,转为前端手动输入模式") + return self.prompt_manual_code(source_email) + + # 命令行模式下询问手动输入 response = input("自动获取验证码失败,是否手动输入? (y/n): ").lower() if response == 'y': return self.prompt_manual_code(source_email) @@ -201,16 +223,23 @@ class EmailVerificationHandler: # 生成唯一ID email_id = str(uuid.uuid4()) + # 判断是否是自动获取失败后转为手动输入 + is_auto_failure = EMAIL_CODE_TYPE != "INPUT" + # 存储到等待字典中 global pending_verification_codes pending_verification_codes[email_id] = { "email": source_email, "status": "pending", "created_at": datetime.now().isoformat(), - "code": None + "code": None, + "auto_failure": is_auto_failure # 标记是否是自动获取失败后的手动输入 } - info(f"已创建验证码请求 ID: {email_id},等待前端输入验证码") + if is_auto_failure: + info(f"已创建验证码请求 ID: {email_id},这是在自动获取失败后转为手动输入") + else: + info(f"已创建验证码请求 ID: {email_id},等待前端输入验证码") # 循环等待验证码输入,最多等待180秒 start_time = time.time() @@ -232,14 +261,25 @@ class EmailVerificationHandler: return None def get_tempmail_email_code(self, source_email=None): + # 如果是自定义邮箱场景,直接返回None,不尝试通过临时邮箱API获取验证码 + if self.custom_email is not None: + info("自定义邮箱注册场景不应通过临时邮箱API获取验证码,跳过此步骤") + return None, None + info("开始获取邮件列表") - # 如果提供了source_email,且当前不是使用custom_email,则尝试解析它 - if source_email and source_email != f"{self.username}@{self.domain}" and '@' in source_email: + # 确保在任务注册场景(非自定义邮箱)下使用配置邮箱 + # 仅当source_email存在且与配置邮箱不同时才使用source_email(自定义邮箱场景) + using_custom_email = (source_email is not None and + '@' in source_email and + source_email != f"{self.username}@{self.domain}") + + if using_custom_email: username, domain = source_email.split('@', 1) mail_list_url = f"https://tempmail.plus/api/mails?email={username}%40{domain}&limit=20&epin={self.pin}" info(f"使用自定义邮箱获取验证码: {source_email}") else: + # 任务注册场景,使用配置邮箱 mail_list_url = f"https://tempmail.plus/api/mails?email={self.username}%40{self.domain}&limit=20&epin={self.pin}" info(f"使用配置邮箱获取验证码: {self.username}@{self.domain}") @@ -276,11 +316,12 @@ class EmailVerificationHandler: return None, None info(f"开始获取邮件详情: {first_id}") - # 使用相同的用户名和域名获取邮件详情 - if source_email and source_email != f"{self.username}@{self.domain}" and '@' in source_email: - username, domain = source_email.split('@', 1) + # 使用与邮件列表相同的邮箱账号获取邮件详情 + if using_custom_email: + # 自定义邮箱场景 mail_detail_url = f"https://tempmail.plus/api/mails/{first_id}?email={username}%40{domain}&epin={self.pin}" else: + # 任务注册场景,使用配置邮箱 mail_detail_url = f"https://tempmail.plus/api/mails/{first_id}?email={self.username}%40{self.domain}&epin={self.pin}" try: @@ -305,17 +346,20 @@ class EmailVerificationHandler: # 从邮件文本中提取6位数字验证码 mail_text = mail_detail_data.get("text", "") - # 如果提供了source_email,确保邮件内容中包含该邮箱地址 - if source_email and source_email.lower() not in mail_text.lower(): + # 在自定义邮箱场景下检查邮件内容包含该邮箱,任务注册场景不需要检查 + if using_custom_email and source_email and source_email.lower() not in mail_text.lower(): error(f"邮件内容不包含指定的邮箱地址: {source_email}") else: - info(f"邮件内容包含指定的邮箱地址: {source_email}") + if using_custom_email: + info(f"邮件内容包含指定的邮箱地址: {source_email}") + else: + info(f"使用配置邮箱获取验证码,不需要检查邮件内容") code_match = re.search(r"(? + +
+ +
+ +
+ 验证码提示: 在任务注册过程中,如果自动获取验证码失败,系统将自动转为前端手动输入模式,请留意弹窗提示并及时输入验证码。 +
+
+
+