diff --git a/.gitignore b/.gitignore index a271cfe..94b11c4 100644 --- a/.gitignore +++ b/.gitignore @@ -52,3 +52,4 @@ temp/ *.pem *.key credentials.json +.history \ No newline at end of file diff --git a/api.py b/api.py index 95fb433..521fb47 100644 --- a/api.py +++ b/api.py @@ -1,6 +1,6 @@ from fastapi import FastAPI, HTTPException, status, UploadFile, Request from pydantic import BaseModel -from typing import Optional, List +from typing import Optional, List, Dict from sqlalchemy import select, func, delete, desc from pathlib import Path from database import get_session, AccountModel, AccountUsageRecordModel, init_db @@ -29,6 +29,12 @@ from fastapi.staticfiles import StaticFiles from dotenv import load_dotenv import json import time +import uuid + +# 从get_email_code.py导入验证码请求字典 +from get_email_code import EmailVerificationHandler +# 修改为直接访问get_email_code模块 +import get_email_code # 全局状态追踪 registration_status = { @@ -48,6 +54,9 @@ static_path.mkdir(exist_ok=True) # 确保目录存在 # 全局任务存储 background_tasks = {"registration_task": None} +# 添加用于存储等待验证码的邮箱信息的全局字典 +# pending_verification_codes = {} + # 添加lifespan管理器,在应用启动时初始化数据库 @asynccontextmanager @@ -168,9 +177,9 @@ async def run_registration(): info("注册失败") except SystemExit: # 捕获 SystemExit 异常,这是注册脚本正常退出的方式 - info("注册脚本正常退出") - if registration_status["last_status"] != "error": - registration_status["last_status"] = "completed" + info("注册脚本通过sys.exit退出") + registration_status["successful_runs"] += 1 + registration_status["last_status"] = "completed" except Exception as e: error(f"注册过程执行出错: {str(e)}") error(traceback.format_exc()) @@ -1423,7 +1432,141 @@ async def restart_service(): return {"success": False, "message": f"重启服务失败: {str(e)}"} +class CustomRegistrationRequest(BaseModel): + email: str + +@app.post("/registration/custom", tags=["Registration"]) +async def register_with_custom_email(request: CustomRegistrationRequest): + """使用自定义邮箱注册Cursor账号""" + global registration_status + + if registration_status["is_running"]: + return JSONResponse( + status_code=400, + content={ + "success": False, + "message": "当前有注册任务正在运行,请先停止当前任务" + } + ) + + count = await get_active_account_count() + if count >= MAX_ACCOUNTS: + return JSONResponse( + status_code=400, + content={ + "success": False, + "message": f"已达到最大账号数量 ({count}/{MAX_ACCOUNTS})" + } + ) + + # 验证邮箱格式 + if "@" not in request.email: + return JSONResponse( + status_code=400, + content={ + "success": False, + "message": "邮箱格式不正确,请提供正确的邮箱地址" + } + ) + + try: + info(f"开始使用自定义邮箱注册: {request.email}") + + # 调用注册函数,传入自定义邮箱 + try: + success = await asyncio.get_event_loop().run_in_executor( + None, lambda: register_account(custom_email=request.email) + ) + + if success: + return JSONResponse( + content={ + "success": True, + "message": f"使用邮箱 {request.email} 注册成功" + } + ) + else: + return JSONResponse( + status_code=500, + content={ + "success": False, + "message": f"使用邮箱 {request.email} 注册失败" + } + ) + except SystemExit: + # 处理注册函数通过sys.exit退出的情况 + info("注册函数通过sys.exit退出,可能已成功注册") + return JSONResponse( + content={ + "success": True, + "message": f"使用邮箱 {request.email} 注册过程完成,请检查账号列表" + } + ) + except Exception as e: + error(f"使用自定义邮箱注册过程出错: {str(e)}") + error(traceback.format_exc()) + return JSONResponse( + status_code=500, + content={ + "success": False, + "message": f"注册过程出错: {str(e)}" + } + ) + + +@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": + result.append({ + "id": email_id, + "email": email_info.get("email"), + "created_at": email_info.get("created_at") + }) + + return { + "success": True, + "data": result + } + +@app.post("/verification/submit") +async def submit_verification_code(code_data: Dict): + """提交验证码""" + email_id = code_data.get("id") + code = code_data.get("code") + + if not email_id or not code: + return { + "success": False, + "message": "缺少必要参数" + } + + if email_id not in get_email_code.pending_verification_codes: + return { + "success": False, + "message": "未找到对应的验证请求" + } + + # 更新状态和验证码 + get_email_code.pending_verification_codes[email_id]["status"] = "submitted" + get_email_code.pending_verification_codes[email_id]["code"] = code + + return { + "success": True, + "message": "验证码已提交" + } + + if __name__ == "__main__": + # 设置Web模式环境变量 + os.environ["CURSOR_AUTO_REGISTER_WEB"] = "true" + + # 启动服务器 + import uvicorn + uvicorn.run( "api:app", host=API_HOST, diff --git a/cursor_pro_keep_alive.py b/cursor_pro_keep_alive.py index 9107d40..72b73b8 100644 --- a/cursor_pro_keep_alive.py +++ b/cursor_pro_keep_alive.py @@ -11,7 +11,9 @@ from config import ( EMAIL_DOMAINS, REGISTRATION_MAX_RETRIES, EMAIL_TYPE, - EMAIL_CODE_TYPE + EMAIL_CODE_TYPE, + EMAIL_USERNAME, + EMAIL_DOMAIN ) @@ -182,7 +184,7 @@ def sign_up_account(browser, tab, account_info): if ( tab.ele("verify the user is human. Please try again.") or tab.ele("Can't verify the user is human. Please try again.") - or tab.ele("Can‘t verify the user is human. Please try again.") + or tab.ele("Can't verify the user is human. Please try again.") ): info("检测到turnstile验证失败,(IP问题、UA问题、域名问题)...正在重试...") return "EMAIL_USED" @@ -223,8 +225,8 @@ def sign_up_account(browser, tab, account_info): info("注册限制") return "SIGNUP_RESTRICTED" - # 创建邮件处理器 - email_handler = EmailVerificationHandler() + # 创建邮件处理器,传入自定义邮箱 + email_handler = EmailVerificationHandler(custom_email=account_info["email"]) i = 0 while i < 5: try: @@ -234,10 +236,26 @@ def sign_up_account(browser, tab, account_info): break if tab.ele("@data-index=0"): 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" + + # 切换到邮箱标签页获取验证码 code = email_handler.get_verification_code( source_email=account_info["email"] ) + + # 如果临时修改了EMAIL_CODE_TYPE,恢复原值 + if EMAIL_CODE_TYPE != local_email_code_type: + EMAIL_CODE_TYPE = local_email_code_type + if code is None: info("未获取到验证码...系统异常,正在退出....") return "EMAIL_GET_CODE_FAILED" @@ -329,9 +347,9 @@ class EmailGenerator: domain = random.choice(self.domains) return f"{random_str}@{domain}" - def get_account_info(self): - """获取账号信息,确保每次调用都生成新的邮箱和密码""" - self.email = self.generate_email() + def get_account_info(self, email=None): + """获取账号信息,确保每次调用都生成新的邮箱和密码,或使用提供的邮箱""" + self.email = email if email else self.generate_email() self.password = self.generate_random_password() return { "email": self.email, @@ -417,19 +435,29 @@ def cleanup_and_exit(browser_manager=None, exit_code=0): sys.exit(1) -def main(): +def main(custom_email=None): browser_manager = None max_retries = REGISTRATION_MAX_RETRIES # 从配置文件获取 current_retry = 0 + + # 先声明全局变量并保存原始值 + global EMAIL_CODE_TYPE + original_email_code_type = EMAIL_CODE_TYPE try: - email_handler = EmailVerificationHandler() + # 如果使用自定义邮箱,切换验证码获取模式为手动输入 + if custom_email: + info(f"使用自定义邮箱 {custom_email},切换为手动输入验证码模式") + EMAIL_CODE_TYPE = "INPUT" + + # 初始化邮箱验证处理器时传入自定义邮箱 + email_handler = EmailVerificationHandler(custom_email=custom_email) if email_handler.check(): info('邮箱服务连接正常,开始注册!') else: if EMAIL_CODE_TYPE == "API": error('邮箱服务连接失败,并且验证码为API获取,结束注册!') - return + return False else: info('邮箱服务连接失败,并且验证码为手动输入,等待输入验证码...') @@ -438,7 +466,8 @@ def main(): browser = browser_manager.init_browser() while current_retry < max_retries: try: - account_info = email_generator.get_account_info() + # 使用自定义邮箱或生成新邮箱 + account_info = email_generator.get_account_info(email=custom_email) info( f"初始化账号信息成功 => 邮箱: {account_info['email']}, 用户名: {account_info['first_name']}, 密码: {account_info['password']}" ) @@ -455,7 +484,10 @@ def main(): if token: email_generator._save_account_info(user, token, TOTAL_USAGE) info("注册流程完成") - cleanup_and_exit(browser_manager, 0) + # 关闭资源但不退出程序,只返回成功状态 + if browser_manager and hasattr(browser_manager, "browser"): + browser_manager.browser.quit() + return True else: info("获取Cursor会话Token失败") current_retry += 1 @@ -487,9 +519,19 @@ def main(): pass info(f"达到最大重试次数 {max_retries},注册失败") + return False except Exception as e: info(f"主程序错误: {str(e)}") info(f"错误详情: {traceback.format_exc()}") - cleanup_and_exit(browser_manager, 1) + return False finally: - cleanup_and_exit(browser_manager, 1) + # 恢复原始的EMAIL_CODE_TYPE + EMAIL_CODE_TYPE = original_email_code_type + + # 清理资源 + if browser_manager: + try: + if hasattr(browser_manager, "browser"): + browser_manager.browser.quit() + except Exception as e: + error(f"关闭浏览器时发生错误: {str(e)}") diff --git a/get_email_code.py b/get_email_code.py index 9122bbc..9c620f5 100644 --- a/get_email_code.py +++ b/get_email_code.py @@ -7,6 +7,9 @@ def warn(message): import time import re import requests +import os +import uuid +from datetime import datetime from config import ( EMAIL_USERNAME, EMAIL_DOMAIN, @@ -17,13 +20,22 @@ from config import ( EMAIL_PROXY_ADDRESS, EMAIL_PROXY_ENABLED, EMAIL_API, - EMAIL_CODE_TYPE + EMAIL_CODE_TYPE, + EMAIL_DOMAINS ) +# 声明全局变量用于存储验证码请求 +pending_verification_codes = {} class EmailVerificationHandler: - def __init__(self, username=None, domain=None, pin=None, use_proxy=False): + def __init__(self, username=None, domain=None, pin=None, use_proxy=False, custom_email=None): self.email = EMAIL_TYPE + self.custom_email = custom_email + + # 如果提供了自定义邮箱,则解析它 + if custom_email and '@' in custom_email: + username, domain = custom_email.split('@', 1) + self.username = username or EMAIL_USERNAME self.domain = domain or EMAIL_DOMAIN self.session = requests.Session() @@ -51,7 +63,14 @@ class EmailVerificationHandler: info(f"已启用代理: {EMAIL_PROXY_ADDRESS}") def check(self): - mail_list_url = f"https://tempmail.plus/api/mails?email={self.username}%40{self.domain}&limit=20&epin={self.pin}" + # 如果有自定义邮箱,优先使用它 + username = self.username + domain = self.domain + if self.custom_email and '@' in self.custom_email: + username, domain = self.custom_email.split('@', 1) + info(f"使用自定义邮箱进行连接检查: {username}@{domain}") + + mail_list_url = f"https://tempmail.plus/api/mails?email={username}%40{domain}&limit=20&epin={self.pin}" try: # 增加超时时间并添加错误重试 for retry in range(3): @@ -97,11 +116,22 @@ class EmailVerificationHandler: Returns: str: 验证码或None """ - # 如果邮箱验证码获取方式为输入,则直接返回输入的验证码 + # 记录当前的验证码获取模式和邮箱信息 + 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) + + # 如果是INPUT模式,始终直接进入手动输入 if EMAIL_CODE_TYPE == "INPUT": info("EMAIL_CODE_TYPE设为INPUT,跳过自动获取,直接手动输入") - return self.prompt_manual_code() + return self.prompt_manual_code(source_email) + # 以下是自动获取验证码的逻辑,只有在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}") @@ -110,7 +140,7 @@ class EmailVerificationHandler: if self.email not in ["tempemail", "zmail"]: error(f"不支持的邮箱类型: {self.email},支持的类型为: tempemail, zmail") warn("自动切换到手动输入模式") - return self.prompt_manual_code() + return self.prompt_manual_code(source_email) for attempt in range(max_retries): try: @@ -142,20 +172,77 @@ class EmailVerificationHandler: # 所有自动尝试都失败后,询问是否手动输入 response = input("自动获取验证码失败,是否手动输入? (y/n): ").lower() if response == 'y': - return self.prompt_manual_code() + return self.prompt_manual_code(source_email) return None - # 手动输入验证码 - def prompt_manual_code(self): - """手动输入验证码""" - info("自动获取验证码失败,开始手动输入验证码。") - code = input("输入6位数字验证码: ").strip() - return code + # 手动输入验证码 - 添加前端输入支持 + def prompt_manual_code(self, source_email=None): + """手动输入验证码,支持前端输入""" + email_display = source_email if source_email else f"{self.username}@{self.domain}" + info("=============手动输入验证码模式=============") + info(f"请查看邮箱 [{email_display}] 中的验证码") + + # 检查是否有CURSOR_AUTO_REGISTER_WEB环境变量,表示是否通过Web界面运行 + web_mode = os.environ.get("CURSOR_AUTO_REGISTER_WEB", "").lower() == "true" + + if web_mode: + info("检测到Web模式,使用前端验证码输入") + return self.prompt_manual_code_web(email_display) + else: + info("命令行模式,使用控制台输入验证码") + info("通常验证码为6位数字,在邮件正文中") + code = input("请输入收到的验证码: ").strip() + info(f"已输入验证码: {code}") + return code + + # 前端验证码输入方法 + def prompt_manual_code_web(self, source_email): + """在Web界面请求验证码输入""" + # 生成唯一ID + email_id = str(uuid.uuid4()) + + # 存储到等待字典中 + global pending_verification_codes + pending_verification_codes[email_id] = { + "email": source_email, + "status": "pending", + "created_at": datetime.now().isoformat(), + "code": None + } + + info(f"已创建验证码请求 ID: {email_id},等待前端输入验证码") + + # 循环等待验证码输入,最多等待180秒 + start_time = time.time() + while time.time() - start_time < 180: + # 检查是否已提交验证码 + if email_id in pending_verification_codes and pending_verification_codes[email_id]["status"] == "submitted": + code = pending_verification_codes[email_id]["code"] + info(f"前端已提交验证码: {code}") + # 删除已使用的记录 + pending_verification_codes.pop(email_id, None) + return code + + # 等待1秒再检查 + time.sleep(1) + + # 超时,从等待字典中移除 + pending_verification_codes.pop(email_id, None) + info("验证码输入超时") + return None def get_tempmail_email_code(self, source_email=None): info("开始获取邮件列表") - # 获取邮件列表 - mail_list_url = f"https://tempmail.plus/api/mails?email={self.username}%40{self.domain}&limit=20&epin={self.pin}" + + # 如果提供了source_email,且当前不是使用custom_email,则尝试解析它 + if source_email and source_email != f"{self.username}@{self.domain}" and '@' in source_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}") + try: # 增加错误重试和超时时间 for retry in range(3): @@ -188,8 +275,14 @@ class EmailVerificationHandler: if not first_id: return None, None info(f"开始获取邮件详情: {first_id}") - # 获取具体邮件内容 - mail_detail_url = f"https://tempmail.plus/api/mails/{first_id}?email={self.username}%40{self.domain}&epin={self.pin}" + + # 使用相同的用户名和域名获取邮件详情 + if source_email and source_email != f"{self.username}@{self.domain}" and '@' in source_email: + username, domain = source_email.split('@', 1) + 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: mail_detail_response = self.session.get( mail_detail_url, timeout=10 @@ -222,7 +315,7 @@ class EmailVerificationHandler: if code_match: # 清理邮件 - self._cleanup_mail(first_id) + self._cleanup_mail(first_id, source_email) return code_match.group(), first_id return None, None except requests.exceptions.Timeout: @@ -235,11 +328,18 @@ class EmailVerificationHandler: error(f"获取邮件列表发生错误: {str(e)}") return None, None - def _cleanup_mail(self, first_id): + def _cleanup_mail(self, first_id, source_email=None): + # 如果提供了source_email,优先使用它 + username = self.username + domain = self.domain + if source_email and '@' in source_email: + username, domain = source_email.split('@', 1) + info(f"使用自定义邮箱清理邮件: {username}@{domain}") + # 构造删除请求的URL和数据 delete_url = "https://tempmail.plus/api/mails/" payload = { - "email": f"{self.username}@{self.domain}", + "email": f"{username}@{domain}", "first_id": first_id, "epin": self.pin, } @@ -301,13 +401,24 @@ class EmailVerificationHandler: def get_zmail_email_code(self, source_email=None): info("开始获取邮件列表") # 获取邮件列表 - username = source_email.split("@")[0] + # 优先使用传入的source_email,其次是自定义邮箱,最后才是配置中的邮箱 + email_to_use = source_email if source_email else (self.custom_email if self.custom_email else f"{self.username}@{self.domain}") + + if '@' not in email_to_use: + error(f"邮箱格式错误: {email_to_use}") + return None, None + + username = email_to_use.split("@")[0] + info(f"使用邮箱获取验证码: {email_to_use}, 用户名: {username}") + mail_list_url = f"{EMAIL_API}/api/mailboxes/{username}/emails" - proxy = { + + if EMAIL_PROXY_ENABLED: + proxy = { "http": f"{EMAIL_PROXY_ADDRESS}", "https": f"{EMAIL_PROXY_ADDRESS}", } - self.session.proxies.update(proxy) + self.session.proxies.update(proxy) try: mail_list_response = self.session.get( mail_list_url, timeout=10000 diff --git a/index.html b/index.html index 8d37bd9..24b36da 100644 --- a/index.html +++ b/index.html @@ -24,6 +24,9 @@
+
加载中... @@ -170,6 +173,43 @@
+ +
+
+
+
+
自定义邮箱注册
+
+
+
+
+
+ +
+ + +
+ 使用自定义邮箱注册时,需要手动获取验证码 +
+
+
+ +
+
+
+
+
+
+
@@ -332,7 +372,8 @@ True=无界面运行浏览器,False=显示浏览器界面
- +
@@ -349,7 +390,8 @@
- Windows下浏览器可执行文件的完整路径(示例:C:\Users\Administrator\AppData\Local\Google\Chrome\Bin\chrome.exe) + Windows下浏览器可执行文件的完整路径(示例:C:\Users\Administrator\AppData\Local\Google\Chrome\Bin\chrome.exe)
@@ -357,7 +399,7 @@ disabled> 系统允许创建的最大账号数量
- +
@@ -549,6 +591,7 @@

确定要删除此账号吗?此操作不可恢复。

邮箱:

ID:

+
-