diff --git a/.env b/.env index 3375ca4..295daff 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -version=1.0.8 -VERSION=1.0.8 +version=1.0.9 +VERSION=1.0.9 diff --git a/README.md b/README.md index b24f51e..26c0175 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ [![Stars](https://img.shields.io/github/stars/yeongpin/cursor-free-vip?style=flat-square&logo=github)](https://github.com/yeongpin/cursor-free-vip/stargazers)

-

Support Latest 0.45 Version | 支持最新0.45版本

+

Support Latest 0.45.11 Version | 支持最新0.45.11版本

This is a tool to automatically register (except for Google verification code), support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration. @@ -38,6 +38,21 @@ This is a tool to automatically register (except for Google verification code), ## 🔄 更新日志
+v1.0.9 + +

+ free
+

+ +1. Fixed New 0.45.x Version Reset Machine | 修復新0.45版本重置機器 +2. Fix Locale Language | 修復多語言 +3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊 +4. Add Remake main.js | 重做main.js +
+
+ +Other Version Change Log +
v1.0.8 1. Fix New 0.45 Version Reset Machine | 修復新0.45版本重置機器 @@ -45,8 +60,6 @@ This is a tool to automatically register (except for Google verification code), 3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
-Other Version Change Log -
v1.0.7 - HotFix 1. Fix Reset Machine | 修復重置機器 diff --git a/browser.py b/browser.py index a2cb5f0..b613563 100644 --- a/browser.py +++ b/browser.py @@ -2,6 +2,7 @@ from DrissionPage import ChromiumOptions, ChromiumPage import sys import os import logging +import random class BrowserManager: @@ -36,17 +37,58 @@ class BrowserManager: except FileNotFoundError as e: logging.warning(f"警告: {e}") + # 设置更真实的用户代理 co.set_user_agent( - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.6723.92 Safari/537.36" + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" ) + + # 基本设置 co.set_pref("credentials_enable_service", False) + co.set_pref("profile.password_manager_enabled", False) + + # 禁用自动化标志 + co.set_pref("useAutomationExtension", False) + co.set_pref("excludeSwitches", ["enable-automation"]) + + # WebGL 和 GPU 设置 + co.set_pref("webgl.disabled", False) + co.set_pref("webgl.enable_webgl2", True) + + # 设置语言和地区 + co.set_pref("intl.accept_languages", "en-US,en") + + # 基本命令行参数 + co.set_argument("--disable-blink-features=AutomationControlled") co.set_argument("--hide-crash-restore-bubble") + co.set_argument("--no-first-run") + co.set_argument("--no-default-browser-check") + co.set_argument("--disable-popup-blocking") + + # 性能和稳定性参数 + co.set_argument("--disable-dev-shm-usage") + co.set_argument("--disable-gpu") + co.set_argument("--no-sandbox") + co.set_argument("--ignore-certificate-errors") + + # WebGL 相关参数 + co.set_argument("--use-gl=swiftshader") + co.set_argument("--enable-webgl") + + # 随机端口 co.auto_port() - # Mac 系统特殊处理 - if sys.platform == "darwin": - co.set_argument("--no-sandbox") + # 系统特定设置 + if sys.platform == "darwin": # macOS co.set_argument("--disable-gpu") + co.set_argument("--no-sandbox") + elif sys.platform == "win32": # Windows + co.set_argument("--disable-software-rasterizer") + + + # 设置窗口大小 + window_width = random.randint(1024, 1920) + window_height = random.randint(768, 1080) + co.set_argument(f"--window-size={window_width},{window_height}") return co diff --git a/cursor_register.py b/cursor_register.py index e7bfb9c..a039cc2 100644 --- a/cursor_register.py +++ b/cursor_register.py @@ -246,56 +246,87 @@ class CursorRegistration: def _handle_turnstile(self): """处理 Turnstile 验证""" - print(f"{Fore.YELLOW}{EMOJI['VERIFY']} {self.translator.get('register.handle_turnstile')}...{Style.RESET_ALL}") - - # 设置最大等待时间(秒) - max_wait_time = 10 # 增加等待时间 - start_time = time.time() - - while True: - try: - # 检查是否超时 - if time.time() - start_time > max_wait_time: - print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.no_turnstile')}...{Style.RESET_ALL}") - time.sleep(2) - break - + try: + print(f"{Fore.YELLOW}{EMOJI['VERIFY']} {self.translator.get('register.handle_turnstile')}...{Style.RESET_ALL}") + + max_retries = 2 + retry_interval = (1, 2) + retry_count = 0 + + while retry_count < max_retries: + retry_count += 1 + print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.turnstile_attempt', attempt=retry_count)}...{Style.RESET_ALL}") + try: - challengeCheck = ( - self.signup_tab.ele("@id=cf-turnstile", timeout=1) + # 尝试重置 turnstile + self.signup_tab.run_js("try { turnstile.reset() } catch(e) { }") + time.sleep(2) + + # 定位验证框元素 + challenge_check = ( + self.signup_tab.ele("@id=cf-turnstile", timeout=2) .child() .shadow_root.ele("tag:iframe") .ele("tag:body") .sr("tag:input") ) - if challengeCheck: - challengeCheck.click() - time.sleep(3) - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.turnstile_passed')}{Style.RESET_ALL}") + if challenge_check: + print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.turnstile_detected')}...{Style.RESET_ALL}") + + # 随机延时后点击验证 + time.sleep(random.uniform(1, 3)) + challenge_check.click() time.sleep(2) - break - except: - pass - - try: - if (self.signup_tab.ele("@name=password", timeout=0.5) or - self.signup_tab.ele("@name=email", timeout=0.5) or - self.signup_tab.ele("@data-index=0", timeout=0.5)): - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.turnstile_passed')}{Style.RESET_ALL}") - time.sleep(2) - break - except: - pass - - time.sleep(1) - - except Exception as e: - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.error', error=str(e))}{Style.RESET_ALL}") - time.sleep(2) - break - time.sleep(2) + # 检查验证结果 + if self._check_verification_success(): + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.turnstile_passed')}{Style.RESET_ALL}") + return True + + except Exception as e: + print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('register.turnstile_attempt_failed', error=str(e))}{Style.RESET_ALL}") + + # 检查是否已经验证成功 + if self._check_verification_success(): + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.turnstile_passed')}{Style.RESET_ALL}") + return True + + # 随机延时后继续下一次尝试 + time.sleep(random.uniform(*retry_interval)) + + # 超出最大重试次数 + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.turnstile_max_retries', max=max_retries)}{Style.RESET_ALL}") + return False + + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.turnstile_error', error=str(e))}{Style.RESET_ALL}") + return False + + def _check_verification_success(self): + """检查验证是否成功""" + try: + # 检查是否存在后续表单元素,这表示验证已通过 + if (self.signup_tab.ele("@name=password", timeout=0.5) or + self.signup_tab.ele("@name=email", timeout=0.5) or + self.signup_tab.ele("@data-index=0", timeout=0.5) or + self.signup_tab.ele("Account Settings", timeout=0.5)): + return True + + # 检查是否出现错误消息 + error_messages = [ + 'xpath://div[contains(text(), "Can\'t verify the user is human")]', + 'xpath://div[contains(text(), "Error: 600010")]', + 'xpath://div[contains(text(), "Please try again")]' + ] + + for error_xpath in error_messages: + if self.signup_tab.ele(error_xpath): + return False + + return False + except: + return False def _get_account_info(self): """获取账户信息和 Token""" diff --git a/images/pass_2025-02-08_21-48-36.png b/images/pass_2025-02-08_21-48-36.png new file mode 100644 index 0000000..fd3ed4d Binary files /dev/null and b/images/pass_2025-02-08_21-48-36.png differ diff --git a/locales/en.json b/locales/en.json index 614a091..ec73366 100644 --- a/locales/en.json +++ b/locales/en.json @@ -46,12 +46,28 @@ "sqlite_success": "SQLite Database Updated Successfully", "sqlite_error": "SQLite Database Update Failed: {error}", "press_enter": "Press Enter to Exit", + "unsupported_os": "Unsupported OS: {os}", + "linux_path_not_found": "Linux Path Not Found", "updating_system_ids": "Updating System IDs", "system_ids_updated": "System IDs Updated Successfully", - "system_ids_update_failed": "System IDs Update Failed: {error}" + "system_ids_update_failed": "System IDs Update Failed: {error}", + "windows_guid_updated": "Windows GUID Updated Successfully", + "windows_permission_denied": "Windows Permission Denied", + "windows_guid_update_failed": "Windows GUID Update Failed", + "macos_uuid_updated": "macOS UUID Updated Successfully", + "plutil_command_failed": "plutil Command Failed", + "start_patching": "Starting Patching getMachineId", + "macos_uuid_update_failed": "macOS UUID Update Failed", + "current_version": "Current Cursor Version: {version}", + "patch_completed": "Patching getMachineId Completed", + "patch_failed": "Patching getMachineId Failed: {error}", + "version_check_passed": "Cursor Version Check Passed", + "file_modified": "File Modified" }, "register": { "title": "Cursor Registration Tool", + + "start": "Starting Registration Process", "mailbox": "Successfully Entered Mailbox", "register_start": "Start Register", diff --git a/locales/zh_cn.json b/locales/zh_cn.json index 4d3ffbc..336fe39 100644 --- a/locales/zh_cn.json +++ b/locales/zh_cn.json @@ -48,7 +48,21 @@ "press_enter": "按回车键退出", "updating_system_ids": "更新系统ID", "system_ids_updated": "系统ID更新成功", - "system_ids_update_failed": "系统ID更新失败: {error}" + "system_ids_update_failed": "系统ID更新失败: {error}", + "unsupported_os": "不支持的操作系统: {os}", + "linux_path_not_found": "Linux路径未找到", + "windows_guid_updated": "Windows GUID更新成功", + "windows_permission_denied": "Windows权限拒绝", + "windows_guid_update_failed": "Windows GUID更新失败", + "macos_uuid_updated": "macOS UUID更新成功", + "plutil_command_failed": "plutil命令失败", + "macos_uuid_update_failed": "macOS UUID更新失败", + "start_patching": "开始修补getMachineId", + "current_version": "当前Cursor版本: {version}", + "patch_completed": "getMachineId修补完成", + "patch_failed": "getMachineId修补失败: {error}", + "version_check_passed": "Cursor版本检查通过", + "file_modified": "文件已修改" }, "register": { "title": "Cursor 注册工具", diff --git a/locales/zh_tw.json b/locales/zh_tw.json index 04ba35d..4d7c04e 100644 --- a/locales/zh_tw.json +++ b/locales/zh_tw.json @@ -48,8 +48,26 @@ "press_enter": "按回車鍵退出", "updating_system_ids": "更新系統ID", "system_ids_updated": "系統ID更新成功", - "system_ids_update_failed": "系統ID更新失敗: {error}" + "system_ids_update_failed": "系統ID更新失敗: {error}", + "unsupported_os": "不支持的操作系統: {os}", + "linux_path_not_found": "Linux路徑未找到", + "windows_guid_updated": "Windows GUID更新成功", + "windows_permission_denied": "Windows權限拒絕", + "windows_guid_update_failed": "Windows GUID更新失敗", + "macos_uuid_updated": "macOS UUID更新成功", + "plutil_command_failed": "plutil命令失敗", + "macos_uuid_update_failed": "macOS UUID更新失敗", + "start_patching": "開始修補getMachineId", + "current_version": "當前Cursor版本: {version}", + "patch_completed": "getMachineId修補完成", + "patch_failed": "getMachineId修補失敗: {error}", + "version_check_passed": "Cursor版本檢查通過", + "file_modified": "文件已修改" }, + + + + "register": { "title": "Cursor 註冊工具", "start": "開始註冊流程", diff --git a/reset_machine_manual.py b/reset_machine_manual.py index c5f0992..791ac31 100644 --- a/reset_machine_manual.py +++ b/reset_machine_manual.py @@ -5,19 +5,11 @@ import uuid import hashlib import shutil import sqlite3 -import logging +import platform +import re +import tempfile from colorama import Fore, Style, init - -# 设置日志记录 -logging.basicConfig( - level=logging.INFO, - format='%(asctime)s - %(levelname)s - %(message)s', - handlers=[ - logging.StreamHandler(), - logging.FileHandler('reset_machine.log', encoding='utf-8') - ] -) -logger = logging.getLogger(__name__) +from typing import Tuple # 初始化colorama init() @@ -32,6 +24,173 @@ EMOJI = { "RESET": "🔄", } +def get_cursor_paths(translator=None) -> Tuple[str, str]: + """根据不同操作系统获取 Cursor 相关路径""" + system = platform.system() + + paths_map = { + "Darwin": { + "base": "/Applications/Cursor.app/Contents/Resources/app", + "package": "package.json", + "main": "out/main.js", + }, + "Windows": { + "base": os.path.join( + os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app" + ), + "package": "package.json", + "main": "out/main.js", + }, + "Linux": { + "bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"], + "package": "package.json", + "main": "out/main.js", + }, + } + + if system not in paths_map: + raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}") + + if system == "Linux": + for base in paths_map["Linux"]["bases"]: + pkg_path = os.path.join(base, paths_map["Linux"]["package"]) + if os.path.exists(pkg_path): + return (pkg_path, os.path.join(base, paths_map["Linux"]["main"])) + raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径") + + base_path = paths_map[system]["base"] + return ( + os.path.join(base_path, paths_map[system]["package"]), + os.path.join(base_path, paths_map[system]["main"]), + ) + +def version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool: + """版本号检查""" + version_pattern = r"^\d+\.\d+\.\d+$" + try: + if not re.match(version_pattern, version): + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}") + return False + + def parse_version(ver: str) -> Tuple[int, ...]: + return tuple(map(int, ver.split("."))) + + current = parse_version(version) + + if min_version and current < parse_version(min_version): + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_too_low', version=version, min_version=min_version)}{Style.RESET_ALL}") + return False + + if max_version and current > parse_version(max_version): + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_too_high', version=version, max_version=max_version)}{Style.RESET_ALL}") + return False + + return True + + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_check_error', error=str(e))}{Style.RESET_ALL}") + return False + +def check_cursor_version(translator) -> bool: + """检查 Cursor 版本""" + try: + pkg_path, _ = get_cursor_paths(translator) + with open(pkg_path, "r", encoding="utf-8") as f: + version = json.load(f)["version"] + return version_check(version, min_version="0.45.0", translator=translator) + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}") + return False + +def modify_main_js(main_path: str, translator) -> bool: + """修改 main.js 文件""" + try: + original_stat = os.stat(main_path) + original_mode = original_stat.st_mode + original_uid = original_stat.st_uid + original_gid = original_stat.st_gid + + with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file: + with open(main_path, "r", encoding="utf-8") as main_file: + content = main_file.read() + + patterns = { + r"async getMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMachineId(){return \1}", + r"async getMacMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMacMachineId(){return \1}", + } + + for pattern, replacement in patterns.items(): + content = re.sub(pattern, replacement, content) + + tmp_file.write(content) + tmp_path = tmp_file.name + + shutil.copy2(main_path, main_path + ".old") + shutil.move(tmp_path, main_path) + + os.chmod(main_path, original_mode) + if os.name != "nt": + os.chown(main_path, original_uid, original_gid) + + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}") + return True + + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}") + if "tmp_path" in locals(): + os.unlink(tmp_path) + return False + +def patch_cursor_get_machine_id(translator) -> bool: + """修补 Cursor getMachineId 函数""" + try: + print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}") + + # 获取路径 + pkg_path, main_path = get_cursor_paths(translator) + + # 检查文件权限 + for file_path in [pkg_path, main_path]: + if not os.path.isfile(file_path): + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}") + return False + if not os.access(file_path, os.W_OK): + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}") + return False + + # 获取版本号 + try: + with open(pkg_path, "r", encoding="utf-8") as f: + version = json.load(f)["version"] + print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.current_version', version=version)}{Style.RESET_ALL}") + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}") + return False + + # 检查版本 + if not version_check(version, min_version="0.45.0", translator=translator): + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}") + return False + + print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}") + + # 备份文件 + backup_path = main_path + ".bak" + if not os.path.exists(backup_path): + shutil.copy2(main_path, backup_path) + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}") + + # 修改文件 + if not modify_main_js(main_path, translator): + return False + + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.patch_completed')}{Style.RESET_ALL}") + return True + + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.patch_failed', error=str(e))}{Style.RESET_ALL}") + return False + class MachineIDResetter: def __init__(self, translator=None): self.translator = translator @@ -135,7 +294,6 @@ class MachineIDResetter: return True except Exception as e: print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.system_ids_update_failed', error=str(e))}{Style.RESET_ALL}") - logger.error(f"System IDs update failed: {e}") return False def _update_windows_machine_guid(self): @@ -151,12 +309,12 @@ class MachineIDResetter: new_guid = str(uuid.uuid4()) winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid) winreg.CloseKey(key) - logger.info("Windows MachineGuid updated successfully") + print("Windows MachineGuid updated successfully") except PermissionError: - logger.error("Permission denied: Run as administrator to update Windows MachineGuid") + print("Permission denied: Run as administrator to update Windows MachineGuid") raise except Exception as e: - logger.error(f"Failed to update Windows MachineGuid: {e}") + print(f"Failed to update Windows MachineGuid: {e}") raise def _update_macos_platform_uuid(self, new_ids): @@ -168,11 +326,11 @@ class MachineIDResetter: cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"' result = os.system(cmd) if result == 0: - logger.info("macOS Platform UUID updated successfully") + print("macOS Platform UUID updated successfully") else: raise Exception("Failed to execute plutil command") except Exception as e: - logger.error(f"Failed to update macOS Platform UUID: {e}") + print(f"Failed to update macOS Platform UUID: {e}") raise def reset_machine_ids(self): @@ -215,6 +373,14 @@ class MachineIDResetter: # 更新系统ID self.update_system_ids(new_ids) + # 检查 Cursor 版本并执行相应的操作 + greater_than_0_45 = check_cursor_version(self.translator) + if greater_than_0_45: + print(f"{Fore.CYAN}{EMOJI['INFO']} 检测到 Cursor 版本 >= 0.45.0,正在修补 getMachineId...{Style.RESET_ALL}") + patch_cursor_get_machine_id(self.translator) + else: + print(f"{Fore.YELLOW}{EMOJI['INFO']} Cursor 版本 < 0.45.0,跳过 getMachineId 修补{Style.RESET_ALL}") + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.success')}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}{self.translator.get('reset.new_id')}:{Style.RESET_ALL}") for key, value in new_ids.items():