├── emails.txt ├── data └── emails.txt ├── src ├── __init__.py ├── ui │ ├── __init__.py │ └── logo.py ├── auth │ ├── __init__.py │ ├── cursor_auth_manager.py │ ├── reset_machine.py │ └── patch_cursor_get_machine_id.py ├── core │ ├── __init__.py │ ├── go_cursor_help.py │ ├── exit_cursor.py │ └── cursor_pro_keep_alive.py ├── turnstilePatch │ ├── readme.txt │ ├── script.js │ └── manifest.json ├── utils │ ├── __init__.py │ ├── browser_utils.py │ ├── config.py │ ├── logger.py │ ├── get_email_code.py │ └── language.py ├── icloud │ ├── __init__.py │ ├── deleteEmail.py │ ├── generateEmail.py │ └── hidemyemail.py └── main.py ├── assets └── img │ └── preview.png ├── requirements.txt ├── .gitignore ├── logger.py ├── config.py ├── .env.example ├── generate_email.py ├── test ├── test_delete_email.py └── test_email.py ├── README-zh.md ├── README.md ├── .github └── workflows │ └── build.yml └── LICENSE.md /emails.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/emails.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/ui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/auth/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/turnstilePatch/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /assets/img/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ryan0204/cursor-auto-icloud/HEAD/assets/img/preview.png -------------------------------------------------------------------------------- /src/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Import common utilities for easier access 2 | from src.utils.language import LanguageManager, Language, _, getTranslation 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | DrissionPage==4.1.0.9 2 | colorama==0.4.6 3 | python-dotenv>=1.0.0 4 | pyinstaller 5 | requests 6 | aiohttp==3.9.4 7 | click==8.1.7 8 | certifi==2024.2.2 -------------------------------------------------------------------------------- /src/icloud/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | iCloud module for generating Hide My Email addresses. 3 | """ 4 | 5 | from src.icloud.generateEmail import generateIcloudEmail 6 | from src.icloud.hidemyemail import HideMyEmail 7 | 8 | __all__ = ["generateIcloudEmail", "HideMyEmail"] -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # PyInstaller 2 | build/ 3 | dist/ 4 | *.spec 5 | !CursorKeepAlive.mac.spec 6 | !CursorKeepAlive.win.spec 7 | 8 | # Python 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | **/__pycache__/ 13 | 14 | 15 | # Logs 16 | *.log 17 | 18 | # IDE 19 | .vscode/ 20 | .idea/ 21 | 22 | # Mac 23 | .DS_Store 24 | 25 | venv/ 26 | 27 | node_modules/ 28 | 29 | .env 30 | 31 | screenshots/ 32 | 33 | accounts.csv -------------------------------------------------------------------------------- /logger.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file is kept for backward compatibility. 3 | """ 4 | 5 | import os 6 | import sys 7 | 8 | # Add the current directory to the path so we can import the src package 9 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 10 | 11 | # Import from the new location 12 | from src.utils.logger import * 13 | 14 | # No need to initialize anything here, as the src/utils/logger.py already initializes itself -------------------------------------------------------------------------------- /src/turnstilePatch/script.js: -------------------------------------------------------------------------------- 1 | function getRandomInt(min, max) { 2 | return Math.floor(Math.random() * (max - min + 1)) + min; 3 | } 4 | 5 | // old method wouldn't work on 4k screens 6 | 7 | let screenX = getRandomInt(800, 1200); 8 | let screenY = getRandomInt(400, 600); 9 | 10 | Object.defineProperty(MouseEvent.prototype, 'screenX', { value: screenX }); 11 | 12 | Object.defineProperty(MouseEvent.prototype, 'screenY', { value: screenY }); -------------------------------------------------------------------------------- /src/turnstilePatch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "Turnstile Patcher", 4 | "version": "2.1", 5 | "content_scripts": [ 6 | { 7 | "js": [ 8 | "./script.js" 9 | ], 10 | "matches": [ 11 | "" 12 | ], 13 | "run_at": "document_start", 14 | "all_frames": true, 15 | "world": "MAIN" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file is kept for backward compatibility. 3 | The actual implementation has moved to src/utils/config.py. 4 | """ 5 | 6 | import os 7 | import sys 8 | 9 | # Add the current directory to the path so we can import the src package 10 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 11 | 12 | # Import from the new location 13 | from src.utils.config import Config 14 | 15 | # Export the class for backward compatibility 16 | __all__ = ['Config'] 17 | -------------------------------------------------------------------------------- /src/ui/logo.py: -------------------------------------------------------------------------------- 1 | CURSOR_LOGO = """ 2 | ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗ 3 | ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗ 4 | ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝ 5 | ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗ 6 | ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║ 7 | ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝ 8 | """ 9 | 10 | 11 | def print_logo(): 12 | print(CURSOR_LOGO) 13 | 14 | 15 | if __name__ == "__main__": 16 | print_logo() 17 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | BROWSER_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 2 | 3 | # zh or en 4 | LANGUAGE=zh 5 | 6 | # 无头模式 默认开启 7 | # BROWSER_HEADLESS='True' 8 | 9 | # 使用其他浏览器(如Edge) 10 | # BROWSER_PATH='C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe' 11 | 12 | 13 | ICLOUD_USER= 14 | ICLOUD_APP_PASSWORD= 15 | ICLOUD_COOKIES= 16 | 17 | 18 | # Custom paths for data files (optional) 19 | # EMAIL_FILE_PATH=C:/path/to/your/emails.txt 20 | # CSV_FILE_PATH=C:/path/to/your/accounts.csv 21 | 22 | -------------------------------------------------------------------------------- /generate_email.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Wrapper for iCloud Email Generator 4 | This provides an easy way to import and use the iCloud Hide My Email generator. 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | # Add the current directory to the path 11 | sys.path.append(os.path.dirname(os.path.abspath(__file__))) 12 | 13 | # Import from the new location 14 | try: 15 | from src.icloud.generateEmail import generateIcloudEmail 16 | except ImportError: 17 | # If that fails, try relative imports 18 | try: 19 | from src.icloud.generateEmail import generateIcloudEmail 20 | except ImportError: 21 | raise ImportError("Failed to import iCloud email generator. Make sure all dependencies are installed.") 22 | 23 | # Export the function for backward compatibility 24 | __all__ = ['generateIcloudEmail'] 25 | 26 | # If run directly, generate emails 27 | if __name__ == "__main__": 28 | import sys 29 | 30 | count = 5 # Default count 31 | if len(sys.argv) > 1: 32 | try: 33 | count = int(sys.argv[1]) 34 | except ValueError: 35 | print(f"Invalid count: {sys.argv[1]}") 36 | sys.exit(1) 37 | 38 | print(f"Generating {count} iCloud Hide My Email addresses...") 39 | emails = generateIcloudEmail(count) 40 | 41 | if emails: 42 | print(f"Successfully generated {len(emails)} email addresses:") 43 | for email in emails: 44 | print(email) 45 | else: 46 | print("Failed to generate any email addresses.") -------------------------------------------------------------------------------- /src/core/go_cursor_help.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import os 3 | import subprocess 4 | import sys 5 | 6 | # Add parent directory to path to import language module 7 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | if parent_dir not in sys.path: 9 | sys.path.append(parent_dir) 10 | 11 | try: 12 | from src.utils.logger import logging 13 | from src.utils.language import getTranslation, _ 14 | except ImportError: 15 | from logger import logging 16 | try: 17 | from utils.language import getTranslation, _ 18 | except ImportError: 19 | # Fallback if language module is not available 20 | def getTranslation(key, *args): 21 | return key 22 | 23 | def go_cursor_help(): 24 | system = platform.system() 25 | logging.info(getTranslation("current_operating_system").format(system)) 26 | 27 | base_url = "https://aizaozao.com/accelerate.php/https://raw.githubusercontent.com/yuaotian/go-cursor-help/refs/heads/master/scripts/run" 28 | 29 | if system == "Darwin": # macOS 30 | cmd = f'curl -fsSL {base_url}/cursor_mac_id_modifier.sh | sudo bash' 31 | logging.info(getTranslation("executing_macos_command")) 32 | os.system(cmd) 33 | elif system == "Linux": 34 | cmd = f'curl -fsSL {base_url}/cursor_linux_id_modifier.sh | sudo bash' 35 | logging.info(getTranslation("executing_linux_command")) 36 | os.system(cmd) 37 | elif system == "Windows": 38 | cmd = f'irm {base_url}/cursor_win_id_modifier.ps1 | iex' 39 | logging.info(getTranslation("executing_windows_command")) 40 | # 在Windows上使用PowerShell执行命令 41 | subprocess.run(["powershell", "-Command", cmd], shell=True) 42 | else: 43 | logging.error(getTranslation("unsupported_os").format(system)) 44 | return False 45 | 46 | return True 47 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Cursor Pro Keep Alive - Main Entry Point 4 | This script serves as the main entry point for the Cursor Pro Keep Alive application. 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | # Add the parent directory to the path to import the local packages 11 | parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 12 | if parent_dir not in sys.path: 13 | sys.path.append(parent_dir) 14 | 15 | # Handle frozen environment (PyInstaller) 16 | if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): 17 | # Running in a PyInstaller bundle 18 | bundle_dir = sys._MEIPASS 19 | # Add the bundle directory to path 20 | if bundle_dir not in sys.path: 21 | sys.path.append(bundle_dir) 22 | # Add the src directory in the bundle 23 | src_dir = os.path.join(bundle_dir, 'src') 24 | if os.path.exists(src_dir) and src_dir not in sys.path: 25 | sys.path.append(src_dir) 26 | 27 | # Import logger first to ensure it's initialized before other modules 28 | try: 29 | from src.utils.logger import logging 30 | from src.core.cursor_pro_keep_alive import main as cursor_keep_alive_main 31 | from src.ui.logo import print_logo 32 | from src.utils.language import getTranslation 33 | except ImportError: 34 | # If direct import fails, try relative import 35 | from utils.logger import logging 36 | from core.cursor_pro_keep_alive import main as cursor_keep_alive_main 37 | from ui.logo import print_logo 38 | from utils.language import getTranslation 39 | 40 | def main(): 41 | """Main entry point for the application.""" 42 | print_logo() 43 | logging.info(getTranslation("application_starting")) 44 | try: 45 | cursor_keep_alive_main() 46 | except Exception as e: 47 | logging.error(getTranslation("application_error").format(str(e))) 48 | import traceback 49 | logging.error(traceback.format_exc()) 50 | return 1 51 | return 0 52 | 53 | if __name__ == "__main__": 54 | sys.exit(main()) -------------------------------------------------------------------------------- /src/utils/browser_utils.py: -------------------------------------------------------------------------------- 1 | from DrissionPage import ChromiumOptions, Chromium 2 | import sys 3 | import os 4 | import logging 5 | from dotenv import load_dotenv 6 | 7 | load_dotenv() 8 | 9 | 10 | class BrowserManager: 11 | def __init__(self): 12 | self.browser = None 13 | 14 | def init_browser(self, user_agent=None): 15 | """初始化浏览器""" 16 | co = self._get_browser_options(user_agent) 17 | self.browser = Chromium(co) 18 | return self.browser 19 | 20 | def _get_browser_options(self, user_agent=None): 21 | """获取浏览器配置""" 22 | co = ChromiumOptions() 23 | try: 24 | extension_path = self._get_extension_path("turnstilePatch") 25 | co.add_extension(extension_path) 26 | except FileNotFoundError as e: 27 | logging.warning(f"警告: {e}") 28 | 29 | browser_path = os.getenv("BROWSER_PATH") 30 | if browser_path: 31 | co.set_paths(browser_path=browser_path) 32 | 33 | co.set_pref("credentials_enable_service", False) 34 | co.set_argument("--hide-crash-restore-bubble") 35 | proxy = os.getenv("BROWSER_PROXY") 36 | if proxy: 37 | co.set_proxy(proxy) 38 | 39 | co.auto_port() 40 | if user_agent: 41 | co.set_user_agent(user_agent) 42 | 43 | co.headless( 44 | os.getenv("BROWSER_HEADLESS", "True").lower() == "true" 45 | ) # 生产环境使用无头模式 46 | 47 | # Mac 系统特殊处理 48 | if sys.platform == "darwin": 49 | co.set_argument("--no-sandbox") 50 | co.set_argument("--disable-gpu") 51 | 52 | return co 53 | 54 | def _get_extension_path(self, exname='turnstilePatch'): 55 | """获取插件路径""" 56 | # First try to locate the extension in the src folder 57 | root_dir = os.getcwd() 58 | extension_path = os.path.join(root_dir, "src", exname) 59 | 60 | # If not found in src, try the root directory 61 | if not os.path.exists(extension_path): 62 | extension_path = os.path.join(root_dir, exname) 63 | 64 | # For PyInstaller bundles 65 | if hasattr(sys, "_MEIPASS"): 66 | extension_path = os.path.join(sys._MEIPASS, "src", exname) 67 | if not os.path.exists(extension_path): 68 | extension_path = os.path.join(sys._MEIPASS, exname) 69 | 70 | if not os.path.exists(extension_path): 71 | raise FileNotFoundError(f"插件不存在: {extension_path}") 72 | 73 | return extension_path 74 | 75 | def quit(self): 76 | """关闭浏览器""" 77 | if self.browser: 78 | try: 79 | self.browser.quit() 80 | except: 81 | pass 82 | -------------------------------------------------------------------------------- /test/test_delete_email.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test script for the iCloud Email Deletion feature. 3 | This script allows you to test deleting a specific iCloud Hide My Email address. 4 | """ 5 | 6 | import os 7 | import sys 8 | import asyncio 9 | import argparse 10 | from typing import List, Optional, Union, Tuple 11 | 12 | ICLOUD_COOKIES=None 13 | # Add parent directory to path to import required modules 14 | parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 15 | 16 | 17 | if parent_dir not in sys.path: 18 | sys.path.append(parent_dir) 19 | 20 | try: 21 | from src.utils.logger import logging 22 | from src.icloud.deleteEmail import deleteIcloudEmail 23 | from src.utils.language import getTranslation, _ 24 | except ImportError: 25 | print("Failed to import required modules. Make sure you're running from the project root.") 26 | sys.exit(1) 27 | 28 | def setup_logging(): 29 | """Configure detailed logging for the test script""" 30 | import logging as std_logging 31 | std_logging.basicConfig( 32 | level=std_logging.DEBUG, 33 | format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' 34 | ) 35 | 36 | def check_icloud_cookies(): 37 | """Check if iCloud cookies are configured""" 38 | cookies = ICLOUD_COOKIES; 39 | if not cookies.strip(): 40 | print("⚠️ " + getTranslation("icloud_cookies_not_configured")) 41 | return False 42 | else: 43 | cookie_length = len(cookies.strip()) 44 | print(f"✅ iCloud cookies found: {cookie_length} characters") 45 | return True 46 | 47 | def main(): 48 | """Main test function""" 49 | print("\n🧪 iCloud Hide My Email Deletion Test\n") 50 | 51 | # Setup logging 52 | setup_logging() 53 | 54 | # Check if cookies are configured 55 | if not check_icloud_cookies(): 56 | sys.exit(1) 57 | 58 | # Get email to delete from command line or user input 59 | parser = argparse.ArgumentParser(description='Test iCloud Hide My Email deletion.') 60 | parser.add_argument('email', nargs='?', help='Email address to delete') 61 | args = parser.parse_args() 62 | 63 | email_to_delete = args.email 64 | 65 | if not email_to_delete: 66 | # Get email from user input 67 | email_to_delete = input("Enter the email address to delete: ").strip() 68 | 69 | if not email_to_delete: 70 | print("No email specified. Exiting.") 71 | sys.exit(1) 72 | 73 | # Confirm before deletion 74 | print(f"\nYou're about to delete the email: {email_to_delete}") 75 | confirmation = input("Are you sure you want to proceed? (y/N): ").strip().lower() 76 | 77 | if confirmation != 'y': 78 | print("Operation cancelled by user.") 79 | sys.exit(0) 80 | 81 | print(f"\nDeleting email: {email_to_delete}") 82 | results = deleteIcloudEmail(email_to_delete) 83 | 84 | 85 | sys.exit(0 if results[0][1] else 1) 86 | 87 | if __name__ == "__main__": 88 | main() 89 | -------------------------------------------------------------------------------- /src/core/exit_cursor.py: -------------------------------------------------------------------------------- 1 | import psutil 2 | import time 3 | import os 4 | import sys 5 | 6 | # Add parent directory to path to import language module 7 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | if parent_dir not in sys.path: 9 | sys.path.append(parent_dir) 10 | 11 | try: 12 | from src.utils.logger import logging 13 | from src.utils.language import getTranslation, _ 14 | except ImportError: 15 | from logger import logging 16 | try: 17 | from utils.language import getTranslation, _ 18 | except ImportError: 19 | # Fallback if language module is not available 20 | def getTranslation(key, *args): 21 | if args: 22 | return key.format(*args) 23 | return key 24 | 25 | def ExitCursor(timeout=5): 26 | """ 27 | 温和地关闭 Cursor 进程 28 | 29 | Args: 30 | timeout (int): 等待进程自然终止的超时时间(秒) 31 | Returns: 32 | bool: 是否成功关闭所有进程 33 | """ 34 | try: 35 | logging.info(getTranslation("starting_cursor_exit")) 36 | cursor_processes = [] 37 | # 收集所有 Cursor 进程 38 | for proc in psutil.process_iter(['pid', 'name']): 39 | try: 40 | if proc.info['name'].lower() in ['cursor.exe', 'cursor']: 41 | cursor_processes.append(proc) 42 | except (psutil.NoSuchProcess, psutil.AccessDenied): 43 | continue 44 | 45 | if not cursor_processes: 46 | logging.info(getTranslation("no_cursor_processes_found")) 47 | return True 48 | 49 | # 温和地请求进程终止 50 | for proc in cursor_processes: 51 | try: 52 | if proc.is_running(): 53 | proc.terminate() # 发送终止信号 54 | except (psutil.NoSuchProcess, psutil.AccessDenied): 55 | continue 56 | 57 | # 等待进程自然终止 58 | start_time = time.time() 59 | while time.time() - start_time < timeout: 60 | still_running = [] 61 | for proc in cursor_processes: 62 | try: 63 | if proc.is_running(): 64 | still_running.append(proc) 65 | except (psutil.NoSuchProcess, psutil.AccessDenied): 66 | continue 67 | 68 | if not still_running: 69 | logging.info(getTranslation("all_cursor_processes_closed")) 70 | return True 71 | 72 | # 等待一小段时间再检查 73 | time.sleep(0.5) 74 | 75 | # 如果超时后仍有进程在运行 76 | if still_running: 77 | process_list = ", ".join([str(p.pid) for p in still_running]) 78 | logging.warning(getTranslation("processes_not_closed_in_time").format(process_list)) 79 | return False 80 | 81 | return True 82 | 83 | except Exception as e: 84 | logging.error(getTranslation("error_closing_cursor").format(str(e))) 85 | return False 86 | 87 | if __name__ == "__main__": 88 | ExitCursor() 89 | -------------------------------------------------------------------------------- /src/auth/cursor_auth_manager.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import os 3 | import sys 4 | 5 | # Add parent directory to path to import language module 6 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 7 | if parent_dir not in sys.path: 8 | sys.path.append(parent_dir) 9 | 10 | try: 11 | from src.utils.language import getTranslation, _ 12 | except ImportError: 13 | from utils.language import getTranslation, _ 14 | 15 | 16 | class CursorAuthManager: 17 | """Cursor认证信息管理器""" 18 | 19 | def __init__(self): 20 | # 判断操作系统 21 | if sys.platform == "win32": # Windows 22 | appdata = os.getenv("APPDATA") 23 | if appdata is None: 24 | raise EnvironmentError(getTranslation("appdata_not_set")) 25 | self.db_path = os.path.join( 26 | appdata, "Cursor", "User", "globalStorage", "state.vscdb" 27 | ) 28 | elif sys.platform == "darwin": # macOS 29 | self.db_path = os.path.abspath(os.path.expanduser( 30 | "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb" 31 | )) 32 | elif sys.platform == "linux" : # Linux 和其他类Unix系统 33 | self.db_path = os.path.abspath(os.path.expanduser( 34 | "~/.config/Cursor/User/globalStorage/state.vscdb" 35 | )) 36 | else: 37 | raise NotImplementedError(getTranslation("unsupported_os").format(sys.platform)) 38 | 39 | def update_auth(self, email=None, access_token=None, refresh_token=None): 40 | """ 41 | 更新Cursor的认证信息 42 | :param email: 新的邮箱地址 43 | :param access_token: 新的访问令牌 44 | :param refresh_token: 新的刷新令牌 45 | :return: bool 是否成功更新 46 | """ 47 | updates = [] 48 | # 登录状态 49 | updates.append(("cursorAuth/cachedSignUpType", "Auth_0")) 50 | 51 | if email is not None: 52 | updates.append(("cursorAuth/cachedEmail", email)) 53 | if access_token is not None: 54 | updates.append(("cursorAuth/accessToken", refresh_token)) 55 | if refresh_token is not None: 56 | updates.append(("cursorAuth/refreshToken", refresh_token)) 57 | 58 | if not updates: 59 | print(getTranslation("no_values_to_update")) 60 | return False 61 | 62 | conn = None 63 | try: 64 | conn = sqlite3.connect(self.db_path) 65 | cursor = conn.cursor() 66 | 67 | for key, value in updates: 68 | 69 | # 如果没有更新任何行,说明key不存在,执行插入 70 | # 检查 accessToken 是否存在 71 | check_query = f"SELECT COUNT(*) FROM itemTable WHERE key = ?" 72 | cursor.execute(check_query, (key,)) 73 | if cursor.fetchone()[0] == 0: 74 | insert_query = "INSERT INTO itemTable (key, value) VALUES (?, ?)" 75 | cursor.execute(insert_query, (key, value)) 76 | else: 77 | update_query = "UPDATE itemTable SET value = ? WHERE key = ?" 78 | cursor.execute(update_query, (value, key)) 79 | 80 | if cursor.rowcount > 0: 81 | print(getTranslation("value_updated_success").format(key.split('/')[-1])) 82 | else: 83 | print(getTranslation("value_not_found_or_unchanged").format(key.split('/')[-1])) 84 | 85 | conn.commit() 86 | return True 87 | 88 | except sqlite3.Error as e: 89 | print(getTranslation("database_error").format(str(e))) 90 | return False 91 | except Exception as e: 92 | print(getTranslation("general_error").format(str(e))) 93 | return False 94 | finally: 95 | if conn: 96 | conn.close() 97 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # Cursor Pro (iCloud) 自动化工具 - 免费试用重置工具 2 | 3 |
4 | 5 | [![Release](https://img.shields.io/github/v/release/ryan0204/cursor-auto-icloud?style=flat-square&logo=github&color=blue)](https://github.com/ryan0204/cursor-auto-icloud/releases/latest) 6 | [![Stars](https://img.shields.io/github/stars/ryan0204/cursor-auto-icloud?style=flat-square&logo=github)](https://github.com/ryan0204/cursor-auto-icloud/stargazers) 7 | 8 | 9 | ## ⭐️ 在 GitHub 上给我们 Star — 这对我们是很大的鼓励! 10 | 11 | [🌏 English README](README.md) 12 | 13 | Cursor Logo 14 | 15 | Tool Preview 16 | 17 |
18 | 19 | ## 目录 20 | 21 | - [准备工作](#准备工作) 22 | - [下载](#下载) 23 | - [设置](#设置) 24 | - [运行工具](#运行工具) 25 | - [免责声明](#免责声明) 26 | - [致谢](#致谢) 27 | - [贡献](#贡献) 28 | - [许可证](#许可证) 29 | 30 | ## 准备工作 31 | 32 | 在使用此工具之前,您应该准备以下内容: 33 | 34 | - 一个拥有 **iCloud Plus** 的苹果账号 (邮箱后缀为 @icloud.com) 35 | 36 | ## 下载 37 | 38 | 1. 从 GitHub Releases 下载最新版本 39 | 2. 根据你的系统选择对应的版本: 40 | 41 | > Windows:直接下载 CursorKeepAlive.exe 42 | > Mac(Intel):选择 x64 版本 43 | > Mac(M系列):选择 ARM64(aarch64) 版本 44 | 45 | ### Mac 用户额外步骤 46 | 47 | > 打开终端,进入应用所在目录 48 | > 执行以下命令使文件可执行: 49 | > ```chmod +x ./CursorKeepAlive``` 50 | 51 | 按照下文设置,然后运行 52 | 53 | ## 设置 54 | 55 | ### 设置环境变量 56 | 57 | > Mac 用户:如果您无法重命名文件,可以使用 `touch .env` 在同一目录中创建该文件。 58 | 59 | 1. 下载 [`.env.example`](https://github.com/Ryan0204/cursor-auto-icloud/blob/main/.env.example) 文件并将其重命名为 `.env` 60 | 2. 填写 `.env` 文件 61 | 62 | ```env 63 | ICLOUD_USER=您的苹果ID(!!! 不包括 @icloud.com) 64 | ICLOUD_APP_PASSWORD=您的苹果ID应用专用密码(解释如下) 65 | ICLOUD_COOKIES=您的iCloud cookies(解释如下) 66 | ``` 67 | 68 | ### 获取 iCloud cookie 字符串 69 | 70 | 1. 下载 [Cookie-Editor](https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) Chrome 扩展 71 | 2. 在浏览器中到 [iCloud 设置](https://www.icloud.com/settings/) 并登录 72 | 3. 点击 Cookie-Editor 扩展并以 `Header String` 格式导出 cookies 73 | 4. 将导出的 cookies 粘贴到名为 `.env` 的文件中作为 `ICLOUD_COOKIES` 74 | 75 | Cookie 示例: 76 | 77 | ``` 78 | X_APPLE_WEB_KB-V5590FJFX4ZYGBSJEZRZBTFB9UU="xxxxxx";X-APPLE-DS-WEB-SESSION-TOKEN="xxxxxx";X-APPLE-UNIQUE-CLIENT-ID="xxxxxx";X-APPLE-WEB-ID=28672BD9012631BA3CBAE022A1DBAEE2D0AFD358;X-APPLE-WEBAUTH-HSA-TRUST="xxxxxx";X-APPLE-WEBAUTH-LOGIN="xxxxxx";X-APPLE-WEBAUTH-PCS-Cloudkit="xxxxxx";X-APPLE-WEBAUTH-PCS-Documents="xxxxxx";X-APPLE-WEBAUTH-PCS-Mail="xxxxxx";X-APPLE-WEBAUTH-PCS-News="xxxxxx";X-APPLE-WEBAUTH-PCS-Notes="xxxxxx";X-APPLE-WEBAUTH-PCS-Photos="xxxxxx";X-APPLE-WEBAUTH-PCS-Safari="xxxxxx";X-APPLE-WEBAUTH-PCS-Sharing="xxxxxx";X-APPLE-WEBAUTH-TOKEN="xxxxxx";X-APPLE-WEBAUTH-USER="xxxxxx";X-APPLE-WEBAUTH-VALIDATE="xxxxxx"; 79 | ``` 80 | 81 | ### 获取 Apple ID 应用专用密码 82 | 83 | 1. 在 [account.apple.com](https://account.apple.com) 登录您的 Apple 账户 84 | 2. 在登录和安全部分,选择应用专用密码 85 | 3. 选择生成应用专用密码,然后按照屏幕上的步骤操作 86 | 4. 复制生成的密码并将其粘贴到名为 `.env` 的文件中作为 `ICLOUD_APP_PASSWORD` 87 | 88 | ## 运行工具 89 | 90 | ### Windows 用户 91 | 92 | 双击可执行文件运行工具。 93 | 94 | ### Mac 用户 95 | 96 | 1. 打开终端 97 | 2. 导航到可执行文件所在的目录 98 | 3. `./CursorKeepAlive` 99 | 100 | ### 请按 `4` 开始自动化流程 101 | 102 | ## 免责声明 103 | 104 | 本项目仅为教育目的而创建。作者不对以下情况承担任何责任或义务: 105 | 106 | - 对代码或相关材料的任何滥用 107 | - 使用本项目产生的任何损害或法律后果 108 | - 所提供内容的准确性、完整性或实用性 109 | 110 | 使用本项目,即表示您同意风险自负。本项目不适用于生产环境,且不提供任何保证或担保。 111 | 如果您有任何法律或道德顾虑,请不要使用此存储库。 112 | 113 | ## 致谢 114 | 115 | 如果没有这些出色项目的帮助,本项目將无法完成: 116 | 117 | - [cursor-auto-free](https://github.com/chengazhen/cursor-auto-free) 118 | - [go-cursor-help](https://github.com/yuaotian/go-cursor-help) 119 | - [hidemyemail-generator](https://github.com/rtunazzz/hidemyemail-generator) 120 | 121 | ## 贡献 122 | 123 | 如果您想为本项目做出贡献,请随时提交拉取请求。 124 | 125 | ## 许可证 126 | 127 | 本产品根据专有许可证分发。您可以在以下链接查看完整的许可协议:[CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/)。 128 | -------------------------------------------------------------------------------- /test/test_email.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import traceback 4 | from pathlib import Path 5 | from dotenv import load_dotenv 6 | 7 | # Properly add parent directory to path for imports 8 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 9 | 10 | # Now we can import modules using absolute imports 11 | from src.utils.get_email_code import EmailVerificationHandler 12 | 13 | # Define a function to find the .env file since we can't import it yet 14 | def get_env_directory(): 15 | """ 16 | Get the directory where the .env file is located. 17 | Returns the directory path if .env exists, otherwise returns the current directory. 18 | """ 19 | # Check common locations for .env file 20 | possible_env_paths = [ 21 | ".env", # Current directory 22 | os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), ".env"), # Project root 23 | os.path.join(os.path.dirname(sys.executable), ".env"), # Next to executable 24 | ] 25 | 26 | for env_path in possible_env_paths: 27 | if os.path.exists(env_path): 28 | return os.path.dirname(os.path.abspath(env_path)) 29 | 30 | # If .env is not found, return current directory 31 | return os.path.abspath(".") 32 | 33 | 34 | def test_icloud_imap(): 35 | """Test the iCloud IMAP email verification functionality""" 36 | print("\n=== Testing iCloud IMAP Mode ===") 37 | icloud_user = os.getenv('ICLOUD_USER', '') 38 | icloud_pass = os.getenv('ICLOUD_APP_PASSWORD', '') 39 | 40 | if not icloud_user or not icloud_pass: 41 | print("Error: iCloud credentials not configured in .env file") 42 | return False 43 | 44 | print(f"iCloud user: {icloud_user}") 45 | 46 | try: 47 | handler = EmailVerificationHandler(icloud_user) 48 | code = handler.get_verification_code() 49 | if code: 50 | print(f"Successfully obtained verification code: {code}") 51 | return code 52 | else: 53 | print("Failed to get verification code") 54 | return False 55 | except Exception as e: 56 | print(f"Error during verification: {str(e)}") 57 | traceback.print_exc() 58 | return False 59 | 60 | 61 | def print_config(): 62 | """Print the current configuration""" 63 | print("\n=== Current Environment Configuration ===") 64 | 65 | # Check if .env is loaded 66 | env_dir = get_env_directory() 67 | env_path = os.path.join(env_dir, ".env") 68 | print(f"Using .env file: {env_path}") 69 | 70 | # Check if iCloud IMAP is configured 71 | icloud_user = os.getenv('ICLOUD_USER') 72 | icloud_pass = os.getenv('ICLOUD_APP_PASSWORD') 73 | 74 | print(f"ICLOUD_USER: {icloud_user or 'Not configured'}") 75 | print(f"ICLOUD_APP_PASSWORD: {'[Configured]' if icloud_pass else '[Not configured]'}") 76 | print(f"ICLOUD_FOLDER: {os.getenv('ICLOUD_FOLDER', 'INBOX')}") 77 | 78 | # Check if emails.txt and accounts.csv are in the right place 79 | emails_path = os.path.join(env_dir, "emails.txt") 80 | accounts_path = os.path.join(env_dir, "accounts.csv") 81 | 82 | print(f"\nData files:") 83 | print(f"emails.txt: {'Exists' if os.path.exists(emails_path) else 'Not found'} at {emails_path}") 84 | print(f"accounts.csv: {'Exists' if os.path.exists(accounts_path) else 'Not found'} at {accounts_path}") 85 | 86 | 87 | def main(): 88 | # Load environment variables 89 | load_dotenv() 90 | 91 | # Print initial configuration 92 | print_config() 93 | 94 | try: 95 | # Check if iCloud IMAP is configured 96 | icloud_user = os.getenv('ICLOUD_USER') 97 | icloud_pass = os.getenv('ICLOUD_APP_PASSWORD') 98 | 99 | if icloud_user and icloud_pass: 100 | result = test_icloud_imap() 101 | print(f"Test result: {'Successful' if result else 'Failed'}") 102 | else: 103 | print("iCloud IMAP is not configured. Please set ICLOUD_USER and ICLOUD_APP_PASSWORD in your .env file.") 104 | except Exception as e: 105 | print(f"Error during testing: {str(e)}") 106 | traceback.print_exc() 107 | 108 | 109 | if __name__ == "__main__": 110 | main() -------------------------------------------------------------------------------- /src/utils/config.py: -------------------------------------------------------------------------------- 1 | from dotenv import load_dotenv 2 | import os 3 | import sys 4 | import json 5 | 6 | # Add parent directory to path to import language module 7 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 8 | if parent_dir not in sys.path: 9 | sys.path.append(parent_dir) 10 | 11 | try: 12 | from src.utils.logger import logging 13 | from src.utils.language import getTranslation, _ 14 | except ImportError: 15 | from utils.logger import logging 16 | try: 17 | from utils.language import getTranslation, _ 18 | except ImportError: 19 | # Fallback if language module is not available 20 | def getTranslation(key, *args): 21 | if args: 22 | return key.format(*args) 23 | return key 24 | 25 | 26 | class Config: 27 | def __init__(self): 28 | # 获取应用程序的根目录路径 29 | if getattr(sys, "frozen", False): 30 | # 如果是打包后的可执行文件 31 | application_path = os.path.dirname(sys.executable) 32 | else: 33 | # 如果是开发环境 34 | # Look for .env in the project root, not in src/utils 35 | application_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 36 | 37 | # 指定 .env 文件的路径 38 | dotenv_path = os.path.join(application_path, ".env") 39 | 40 | if not os.path.exists(dotenv_path): 41 | raise FileNotFoundError(getTranslation("env_file_not_exist").format(dotenv_path)) 42 | 43 | # 加载 .env 文件 44 | load_dotenv(dotenv_path) 45 | 46 | self.icloud_user = os.getenv('ICLOUD_USER', '').strip() 47 | if '@icloud.com' in self.icloud_user: 48 | self.icloud_user = self.icloud_user.replace('@icloud.com', '') 49 | self.icloud_pass = os.getenv('ICLOUD_APP_PASSWORD', '').strip() 50 | 51 | # 检查配置 52 | self.check_config() 53 | 54 | 55 | def get_icloud_imap(self): 56 | """获取 iCloud IMAP 配置 57 | 58 | Returns: 59 | dict or False: iCloud IMAP 配置信息,若未配置则返回 False 60 | """ 61 | # 检查必要的 iCloud IMAP 配置是否存在 62 | icloud_user = os.getenv('ICLOUD_USER', '').strip() 63 | 64 | if '@icloud.com' in icloud_user: 65 | icloud_user = icloud_user.replace('@icloud.com', '') 66 | 67 | icloud_pass = os.getenv('ICLOUD_APP_PASSWORD', '').strip() 68 | 69 | if not icloud_user or not icloud_pass: 70 | return False 71 | 72 | return { 73 | "imap_server": "imap.mail.me.com", # iCloud Mail 固定服务器 74 | "imap_port": 993, # iCloud Mail 固定端口 75 | "imap_user": icloud_user, # 用户名通常是邮箱前缀 76 | "imap_pass": icloud_pass, # 应用专用密码 77 | "imap_dir": os.getenv('ICLOUD_FOLDER', 'INBOX').strip(), 78 | } 79 | 80 | def check_config(self): 81 | """检查配置项是否有效 82 | 83 | 检查规则: 84 | 1. Validate if icloud user and pass is not null 85 | """ 86 | 87 | required_configs = { 88 | "icloud_user": getTranslation("icloud_email"), 89 | "icloud_pass": getTranslation("icloud_app_password"), 90 | } 91 | 92 | # 检查基础配置 93 | for key, name in required_configs.items(): 94 | if not self.check_is_valid(getattr(self, key)): 95 | raise ValueError(getTranslation("config_not_set").format(name=name, key=key.upper())) 96 | 97 | 98 | 99 | def check_is_valid(self, value): 100 | """检查配置项是否有效 101 | 102 | Args: 103 | value: 配置项的值 104 | 105 | Returns: 106 | bool: 配置项是否有效 107 | """ 108 | return isinstance(value, str) and len(str(value).strip()) > 0 109 | 110 | def print_config(self): 111 | logging.info(getTranslation("icloud_email_info").format(self.icloud_user)) 112 | logging.info(getTranslation("icloud_password_info").format(self.icloud_pass)) 113 | 114 | 115 | # 使用示例 116 | if __name__ == "__main__": 117 | try: 118 | config = Config() 119 | print(getTranslation("env_loaded_success")) 120 | config.print_config() 121 | except ValueError as e: 122 | print(getTranslation("error_message").format(str(e))) 123 | -------------------------------------------------------------------------------- /src/utils/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import sys 4 | from datetime import datetime 5 | 6 | # Import translation utilities 7 | try: 8 | from src.utils.language import getTranslation, _ 9 | except ImportError: 10 | try: 11 | from utils.language import getTranslation, _ 12 | except ImportError: 13 | # Fallback if language module is not available 14 | def getTranslation(key, *args): 15 | if args: 16 | return key.format(*args) 17 | return key 18 | 19 | # Global variable to track if logger has been initialized 20 | _logger_initialized = False 21 | 22 | def initialize_logger(): 23 | """Initialize logger if not already initialized""" 24 | global _logger_initialized 25 | 26 | if _logger_initialized: 27 | return 28 | 29 | # Configure logging - determine the log directory based on whether we're in a frozen executable 30 | if getattr(sys, 'frozen', False): 31 | # If we're running in a PyInstaller bundle, use the directory of the executable 32 | application_path = os.path.dirname(sys.executable) 33 | log_dir = os.path.join(application_path, "logs") 34 | else: 35 | # If we're in development environment, use the existing path 36 | log_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "logs") 37 | 38 | if not os.path.exists(log_dir): 39 | os.makedirs(log_dir) 40 | 41 | class PrefixFormatter(logging.Formatter): 42 | """自定义格式化器,为 DEBUG 级别日志添加开源项目前缀""" 43 | 44 | def format(self, record): 45 | if record.levelno == logging.DEBUG: # 只给 DEBUG 级别添加前缀 46 | record.msg = getTranslation("debug_prefix_format").format(record.msg) 47 | return super().format(record) 48 | 49 | # Clear any existing handlers 50 | root_logger = logging.getLogger() 51 | if root_logger.handlers: 52 | for handler in root_logger.handlers[:]: 53 | root_logger.removeHandler(handler) 54 | 55 | # Configure basic logging 56 | logging.basicConfig( 57 | level=logging.DEBUG, 58 | format="%(asctime)s - %(levelname)s - %(message)s", 59 | handlers=[ 60 | logging.FileHandler( 61 | os.path.join(log_dir, f"{datetime.now().strftime('%Y-%m-%d')}.log"), 62 | encoding="utf-8", 63 | ), 64 | ], 65 | ) 66 | 67 | # 为文件处理器设置自定义格式化器 68 | for handler in logging.getLogger().handlers: 69 | if isinstance(handler, logging.FileHandler): 70 | handler.setFormatter( 71 | PrefixFormatter("%(asctime)s - %(levelname)s - %(message)s") 72 | ) 73 | 74 | # 创建控制台处理器 75 | console_handler = logging.StreamHandler() 76 | console_handler.setLevel(logging.INFO) 77 | console_handler.setFormatter(PrefixFormatter("%(message)s")) 78 | 79 | # 将控制台处理器添加到日志记录器 80 | logging.getLogger().addHandler(console_handler) 81 | 82 | # 打印日志目录所在路径 83 | logging.info(getTranslation("logger_initialized").format(os.path.abspath(log_dir))) 84 | 85 | _logger_initialized = True 86 | 87 | # Initialize logger when module is imported 88 | initialize_logger() 89 | 90 | # Rest of the original code 91 | def main_task(): 92 | """ 93 | Main task execution function. Simulates a workflow and handles errors. 94 | """ 95 | try: 96 | logging.info(getTranslation("main_task_starting")) 97 | 98 | # Simulated task and error condition 99 | if some_condition(): 100 | raise ValueError(getTranslation("simulated_error")) 101 | 102 | logging.info(getTranslation("main_task_completed")) 103 | 104 | except ValueError as ve: 105 | logging.error(getTranslation("value_error_occurred").format(ve), exc_info=True) 106 | except Exception as e: 107 | logging.error(getTranslation("unexpected_error_occurred").format(e), exc_info=True) 108 | finally: 109 | logging.info(getTranslation("task_execution_finished")) 110 | 111 | 112 | def some_condition(): 113 | """ 114 | Simulates an error condition. Returns True to trigger an error. 115 | Replace this logic with actual task conditions. 116 | """ 117 | return True 118 | 119 | 120 | if __name__ == "__main__": 121 | # Application workflow 122 | logging.info(getTranslation("application_started")) 123 | main_task() 124 | logging.info(getTranslation("application_exited")) 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!WARNING] 2 | > This project no longer works. Please support **Cursor** by purchasing [Cursor Pro](https://cursor.com/pricing) 3 | 4 | 5 | # Cursor Pro (iCloud) Automation Tool - Free Trial Reset Tool 6 | 7 |
8 | 9 | [![Release](https://img.shields.io/github/v/release/ryan0204/cursor-auto-icloud?style=flat-square&logo=github&color=blue)](https://github.com/ryan0204/cursor-auto-icloud/releases/latest) 10 | [![Stars](https://img.shields.io/github/stars/ryan0204/cursor-auto-icloud?style=flat-square&logo=github)](https://github.com/ryan0204/cursor-auto-icloud/stargazers) 11 | 12 | 13 | ## ⭐️ Star us on GitHub — it motivates us a lot! 14 | 15 | [🌏 中文 README](README-zh.md) 16 | 17 | Cursor Logo 18 | 19 | Tool Preview 20 | 21 |
22 | 23 | ## Table of Contents 24 | 25 | - [Prepare](#prepare) 26 | - [Download](#download) 27 | - [Setting up](#setting-up) 28 | - [Running the tool](#running-the-tool) 29 | - [Disclaimer](#disclaimer) 30 | - [Credits](#credits) 31 | - [Contributing](#contributing) 32 | - [License](#license) 33 | 34 | ## Prepare 35 | 36 | You should have following items before using this tool: 37 | 38 | - An apple account (with @icloud.com as suffix) with **iCloud Plus** 39 | 40 | ## Download 41 | 42 | 1. Download the latest version from GitHub Releases 43 | 2. Choose the version according to your system: 44 | 45 | > Windows: Download CursorKeepAlive.exe directly 46 | > Mac (Intel): Choose x64 version 47 | > Mac (M series): Choose ARM64(aarch64) version 48 | 49 | ### Additional Steps for Mac Users 50 | 51 | > Open Terminal, navigate to the application directory 52 | > Execute the following command to make the file executable: 53 | > ```chmod +x ./CursorKeepAlive``` 54 | 55 | Follow the setup instructions below, then run the tool. 56 | 57 | ## Setting up 58 | 59 | ### Setting up environment variables 60 | 61 | > Mac User: If you are not able to rename the file, you can use `touch .env` to create the file in the same directory as executable file. 62 | 63 | 1. Download [`.env.example`](https://github.com/Ryan0204/cursor-auto-icloud/blob/main/.env.example) file and rename it to `.env` 64 | 2. Fill in the `.env` file 65 | 66 | ```env 67 | ICLOUD_USER=your_apple_id (!!! without @icloud.com) 68 | ICLOUD_APP_PASSWORD=your_apple_id_app_specific_password (explained below) 69 | ICLOUD_COOKIES=your_icloud_cookies (explained below) 70 | ``` 71 | 72 | ### Getting iCloud cookie string 73 | 74 | 1. Download [Cookie-Editor](https://chromewebstore.google.com/detail/cookie-editor/hlkenndednhfkekhgcdicdfddnkalmdm) Chrome extension 75 | 2. Navigate to [iCloud settings](https://www.icloud.com/settings/) in your browser and log in 76 | 3. Click on the Cookie-Editor extension and export cookies with `Header String` format 77 | 4. Paste the exported cookies into a file named `.env` as `ICLOUD_COOKIES` 78 | 79 | Example Cookie: 80 | 81 | ``` 82 | X_APPLE_WEB_KB-V5590FJFX4ZYGBSJEZRZBTFB9UU=“xxxxxx”;X-APPLE-DS-WEB-SESSION-TOKEN=“xxxxxx”;X-APPLE-UNIQUE-CLIENT-ID=“xxxxxx”;X-APPLE-WEB-ID=28672BD9012631BA3CBAE022A1DBAEE2D0AFD358;X-APPLE-WEBAUTH-HSA-TRUST=“xxxxxx”;X-APPLE-WEBAUTH-LOGIN=“xxxxxx”;X-APPLE-WEBAUTH-PCS-Cloudkit=“xxxxxx”;X-APPLE-WEBAUTH-PCS-Documents=“xxxxxx”;X-APPLE-WEBAUTH-PCS-Mail=“xxxxxx”;X-APPLE-WEBAUTH-PCS-News=“xxxxxx”;X-APPLE-WEBAUTH-PCS-Notes=“xxxxxx”;X-APPLE-WEBAUTH-PCS-Photos=“xxxxxx”;X-APPLE-WEBAUTH-PCS-Safari=“xxxxxx”;X-APPLE-WEBAUTH-PCS-Sharing=“xxxxxx”;X-APPLE-WEBAUTH-TOKEN=“xxxxxx”;X-APPLE-WEBAUTH-USER=“xxxxxx”;X-APPLE-WEBAUTH-VALIDATE=“xxxxxx”; 83 | ``` 84 | 85 | ### Getting Apple ID App Specific Password 86 | 87 | 1. Sign in to your Apple Account on [account.apple.com](https://account.apple.com) 88 | 2. In the Sign-In and Security section, select App-Specific Passwords. 89 | 3. Select Generate an app-specific password, then follow the steps on your screen. 90 | 4. Copy the generated password and paste it into a file named `.env` as `ICLOUD_APP_PASSWORD` 91 | 92 | ## Running the tool 93 | 94 | ### Windows User 95 | 96 | Double-click the executable file to run the tool. 97 | 98 | ### Mac User 99 | 100 | 1. Open Terminal 101 | 2. Navigate to the directory where the executable file is located 102 | 3. `./CursorKeepAlive` 103 | 104 | ### Please press `4` to start the automation 105 | 106 | ## Disclaimer 107 | 108 | This project is created solely for educational purposes. The author(s) do not assume any responsibility or liability for: 109 | 110 | - Any misuse of the code or related materials. 111 | - Any damages or legal implications arising from the use of this project. 112 | - The accuracy, completeness, or usefulness of the provided content. 113 | 114 | By using this project, you agree that you are doing so at your own risk. This project is not intended for use in production environments, and no warranties or guarantees are provided. 115 | If you have any legal or ethical concerns, please refrain from using this repository. 116 | 117 | ## Credits 118 | 119 | This project can't be done without the help of these amazing projects: 120 | 121 | - [cursor-auto-free](https://github.com/chengazhen/cursor-auto-free) 122 | - [go-cursor-help](https://github.com/yuaotian/go-cursor-help) 123 | - [hidemyemail-generator](https://github.com/rtunazzz/hidemyemail-generator) 124 | 125 | ## Contributing 126 | 127 | If you want to contribute to this project, please feel free to open a pull request. 128 | 129 | ## License 130 | 131 | This product is distributed under a proprietary license. You can review the full license agreement at the following link: [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/). 132 | -------------------------------------------------------------------------------- /src/auth/reset_machine.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import json 4 | import uuid 5 | import hashlib 6 | import shutil 7 | from colorama import Fore, Style, init 8 | 9 | # Add parent directory to path to import language module 10 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 11 | if parent_dir not in sys.path: 12 | sys.path.append(parent_dir) 13 | 14 | try: 15 | from src.utils.language import getTranslation, _ 16 | except ImportError: 17 | from utils.language import getTranslation, _ 18 | 19 | # 初始化colorama 20 | init() 21 | 22 | # 定义emoji和颜色常量 23 | EMOJI = { 24 | "FILE": "📄", 25 | "BACKUP": "💾", 26 | "SUCCESS": "✅", 27 | "ERROR": "❌", 28 | "INFO": "ℹ️", 29 | "RESET": "🔄", 30 | } 31 | 32 | 33 | class MachineIDResetter: 34 | def __init__(self): 35 | # 判断操作系统 36 | if sys.platform == "win32": # Windows 37 | appdata = os.getenv("APPDATA") 38 | if appdata is None: 39 | raise EnvironmentError(getTranslation("appdata_not_set")) 40 | self.db_path = os.path.join( 41 | appdata, "Cursor", "User", "globalStorage", "storage.json" 42 | ) 43 | elif sys.platform == "darwin": # macOS 44 | self.db_path = os.path.abspath( 45 | os.path.expanduser( 46 | "~/Library/Application Support/Cursor/User/globalStorage/storage.json" 47 | ) 48 | ) 49 | elif sys.platform == "linux": # Linux 和其他类Unix系统 50 | self.db_path = os.path.abspath( 51 | os.path.expanduser("~/.config/Cursor/User/globalStorage/storage.json") 52 | ) 53 | else: 54 | raise NotImplementedError(getTranslation("unsupported_os").format(sys.platform)) 55 | 56 | def generate_new_ids(self): 57 | """生成新的机器ID""" 58 | # 生成新的UUID 59 | dev_device_id = str(uuid.uuid4()) 60 | 61 | # 生成新的machineId (64个字符的十六进制) 62 | machine_id = hashlib.sha256(os.urandom(32)).hexdigest() 63 | 64 | # 生成新的macMachineId (128个字符的十六进制) 65 | mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest() 66 | 67 | # 生成新的sqmId 68 | sqm_id = "{" + str(uuid.uuid4()).upper() + "}" 69 | 70 | return { 71 | "telemetry.devDeviceId": dev_device_id, 72 | "telemetry.macMachineId": mac_machine_id, 73 | "telemetry.machineId": machine_id, 74 | "telemetry.sqmId": sqm_id, 75 | } 76 | 77 | def reset_machine_ids(self): 78 | """重置机器ID并备份原文件""" 79 | try: 80 | print(f"{Fore.CYAN}{EMOJI['INFO']} {getTranslation('checking_config_file')}...{Style.RESET_ALL}") 81 | 82 | # 检查文件是否存在 83 | if not os.path.exists(self.db_path): 84 | print( 85 | f"{Fore.RED}{EMOJI['ERROR']} {getTranslation('config_file_not_exist')}: {self.db_path}{Style.RESET_ALL}" 86 | ) 87 | return False 88 | 89 | # 检查文件权限 90 | if not os.access(self.db_path, os.R_OK | os.W_OK): 91 | print( 92 | f"{Fore.RED}{EMOJI['ERROR']} {getTranslation('config_file_no_permission')}{Style.RESET_ALL}" 93 | ) 94 | print( 95 | f"{Fore.RED}{EMOJI['ERROR']} {getTranslation('go_cursor_help_warning')} {self.db_path} {Style.RESET_ALL}" 96 | ) 97 | return False 98 | 99 | # 读取现有配置 100 | print(f"{Fore.CYAN}{EMOJI['FILE']} {getTranslation('reading_current_config')}...{Style.RESET_ALL}") 101 | with open(self.db_path, "r", encoding="utf-8") as f: 102 | config = json.load(f) 103 | 104 | # 生成新的ID 105 | print(f"{Fore.CYAN}{EMOJI['RESET']} {getTranslation('generating_new_machine_ids')}...{Style.RESET_ALL}") 106 | new_ids = self.generate_new_ids() 107 | 108 | # 更新配置 109 | config.update(new_ids) 110 | 111 | # 保存新配置 112 | print(f"{Fore.CYAN}{EMOJI['FILE']} {getTranslation('saving_new_config')}...{Style.RESET_ALL}") 113 | with open(self.db_path, "w", encoding="utf-8") as f: 114 | json.dump(config, f, indent=4) 115 | 116 | print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {getTranslation('machine_id_reset_success')}{Style.RESET_ALL}") 117 | print(f"\n{Fore.CYAN}{getTranslation('new_machine_ids')}:{Style.RESET_ALL}") 118 | for key, value in new_ids.items(): 119 | print(f"{EMOJI['INFO']} {key}: {Fore.GREEN}{value}{Style.RESET_ALL}") 120 | 121 | return True 122 | 123 | except PermissionError as e: 124 | print(f"{Fore.RED}{EMOJI['ERROR']} {getTranslation('permission_error')}: {str(e)}{Style.RESET_ALL}") 125 | print( 126 | f"{Fore.YELLOW}{EMOJI['INFO']} {getTranslation('run_as_admin_suggestion')}{Style.RESET_ALL}" 127 | ) 128 | return False 129 | except Exception as e: 130 | print(f"{Fore.RED}{EMOJI['ERROR']} {getTranslation('reset_process_error')}: {str(e)}{Style.RESET_ALL}") 131 | 132 | return False 133 | 134 | 135 | if __name__ == "__main__": 136 | print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") 137 | print(f"{Fore.CYAN}{EMOJI['RESET']} {getTranslation('cursor_machine_id_reset_tool')}{Style.RESET_ALL}") 138 | print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}") 139 | 140 | resetter = MachineIDResetter() 141 | resetter.reset_machine_ids() 142 | 143 | print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") 144 | input(f"{EMOJI['INFO']} {getTranslation('press_enter_exit')}...") 145 | -------------------------------------------------------------------------------- /src/icloud/deleteEmail.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | iCloud Email Deletion Utility 4 | This module deletes Hide My Email addresses from iCloud accounts. 5 | """ 6 | 7 | import os 8 | import sys 9 | import asyncio 10 | from typing import List, Optional, Union, Tuple 11 | 12 | # Add parent directory to path to import language module 13 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 14 | if parent_dir not in sys.path: 15 | sys.path.append(parent_dir) 16 | 17 | try: 18 | from src.utils.logger import logging 19 | from src.utils.config import Config 20 | from src.icloud.hidemyemail import HideMyEmail 21 | from src.utils.language import getTranslation, _ 22 | except ImportError: 23 | from utils.logger import logging 24 | from utils.config import Config 25 | from icloud.hidemyemail import HideMyEmail 26 | from utils.language import getTranslation, _ 27 | 28 | async def _delete_email(cookies: str, email: str) -> Tuple[bool, str]: 29 | """ 30 | Delete a single Hide My Email address from iCloud 31 | 32 | Args: 33 | cookies: iCloud cookies for authentication 34 | email: Email address to delete 35 | 36 | Returns: 37 | Tuple[bool, str]: (Success status, Message) 38 | """ 39 | try: 40 | logging.info(getTranslation("deleting_email").format(email)) 41 | 42 | # Use with statement for proper resource management since the class requires it 43 | async with HideMyEmail(label="Cursor-Auto-iCloud", cookies=cookies) as hide_my_email: 44 | # Get the list of existing emails 45 | email_list_response = await hide_my_email.list_email() 46 | if not email_list_response or not email_list_response.get("success", False): 47 | return False, getTranslation("no_emails_found") 48 | 49 | # Extract emails from the response - emails are in result.hmeEmails 50 | email_list = email_list_response.get("result", {}).get("hmeEmails", []) 51 | 52 | # Find the email in the list 53 | found_email = None 54 | for e in email_list: 55 | if e.get('hme') == email: 56 | found_email = e 57 | break 58 | 59 | if not found_email: 60 | return False, getTranslation("email_not_found").format(email) 61 | 62 | # First deactivate the email using the anonymousId 63 | anonymous_id = found_email.get('anonymousId') 64 | if not anonymous_id: 65 | return False, getTranslation("email_missing_id").format(email) 66 | 67 | deactivate_result = await hide_my_email.deactivate_email(anonymous_id) 68 | if not deactivate_result or not deactivate_result.get("success", False): 69 | reason = deactivate_result.get("reason", getTranslation("unknown_error")) if deactivate_result else getTranslation("unknown_error") 70 | return False, getTranslation("email_deactivation_failed").format(reason) 71 | 72 | # Then delete the email using the anonymousId 73 | delete_result = await hide_my_email.delete_email(anonymous_id, email) 74 | 75 | if delete_result and delete_result.get("success", False): 76 | return True, getTranslation("email_deleted_success").format(email) 77 | else: 78 | reason = delete_result.get("reason", getTranslation("unknown_error")) if delete_result else getTranslation("unknown_error") 79 | return False, getTranslation("email_deletion_failed").format(reason) 80 | 81 | except asyncio.TimeoutError: 82 | return False, getTranslation("delete_email_timeout") 83 | except Exception as e: 84 | logging.error(f"Exception details: {str(e)}") 85 | import traceback 86 | logging.error(traceback.format_exc()) 87 | return False, getTranslation("delete_email_failed").format(str(e)) 88 | 89 | def deleteIcloudEmail(email: Union[str, List[str]]) -> List[Tuple[str, bool, str]]: 90 | """ 91 | Delete iCloud Hide My Email address(es) 92 | 93 | Args: 94 | email: Single email address or list of email addresses to delete 95 | 96 | Returns: 97 | List[Tuple[str, bool, str]]: List of (email, success, message) tuples 98 | """ 99 | # Ensure email is a list 100 | emails = [email] if isinstance(email, str) else email 101 | 102 | if not emails: 103 | logging.error(getTranslation("no_emails_to_delete")) 104 | return [] 105 | 106 | # Get iCloud cookies from config 107 | try: 108 | # Get cookies from .env file 109 | cookies = os.getenv('ICLOUD_COOKIES', '').strip() 110 | if not cookies: 111 | logging.error(getTranslation("icloud_cookies_not_configured")) 112 | return [(e, False, getTranslation("icloud_cookies_not_configured")) for e in emails] 113 | 114 | # Process each email 115 | results = [] 116 | for e in emails: 117 | success, message = asyncio.run(_delete_email(cookies, e)) 118 | results.append((e, success, message)) 119 | if success: 120 | logging.info(message) 121 | else: 122 | logging.error(message) 123 | 124 | return results 125 | 126 | except Exception as e: 127 | logging.error(getTranslation("email_deletion_error").format(str(e))) 128 | import traceback 129 | logging.error(traceback.format_exc()) 130 | return [(e, False, str(e)) for e in emails] 131 | 132 | if __name__ == "__main__": 133 | # If run directly, delete specified emails 134 | if len(sys.argv) > 1: 135 | emails_to_delete = sys.argv[1:] 136 | print(getTranslation("deleting_emails").format(len(emails_to_delete))) 137 | results = deleteIcloudEmail(emails_to_delete) 138 | 139 | for email, success, message in results: 140 | status = getTranslation("success") if success else getTranslation("failed") 141 | print(f"{email}: {status} - {message}") 142 | else: 143 | print(getTranslation("no_emails_specified")) -------------------------------------------------------------------------------- /src/icloud/generateEmail.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | iCloud Email Generator 4 | This module generates Hide My Email addresses for iCloud accounts. 5 | """ 6 | 7 | import os 8 | import sys 9 | import asyncio 10 | from typing import List, Optional 11 | 12 | # Add parent directory to path to import language module 13 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 14 | if parent_dir not in sys.path: 15 | sys.path.append(parent_dir) 16 | 17 | try: 18 | from src.utils.logger import logging 19 | from src.utils.config import Config 20 | from src.icloud.hidemyemail import HideMyEmail 21 | from src.utils.language import getTranslation, _ 22 | except ImportError: 23 | from utils.logger import logging 24 | from utils.config import Config 25 | from icloud.hidemyemail import HideMyEmail 26 | from utils.language import getTranslation, _ 27 | 28 | async def _generate_single_email(cookies: str, label: str = "Cursor-Auto-iCloud") -> Optional[str]: 29 | """ 30 | Generate a single iCloud Hide My Email address 31 | 32 | Args: 33 | cookies: iCloud cookies for authentication 34 | label: Label for the email 35 | 36 | Returns: 37 | str: The generated email address or None if failed 38 | """ 39 | try: 40 | async with HideMyEmail(label, cookies) as hme: 41 | # Generate email 42 | gen_result = await hme.generate_email() 43 | 44 | # Debug print the result 45 | logging.debug(f"API Response: {gen_result}") 46 | 47 | if not gen_result.get("success", False): 48 | logging.error(getTranslation("generate_email_failed").format(gen_result.get('reason', getTranslation("unknown_error")))) 49 | return None 50 | 51 | # Correctly access the email address from the nested structure 52 | email = gen_result.get("result", {}).get("hme") 53 | if not email: 54 | logging.error(getTranslation("generate_email_failed_no_address")) 55 | return None 56 | 57 | # Reserve email 58 | reserve_result = await hme.reserve_email(email) 59 | if not reserve_result.get("success", False): 60 | logging.error(getTranslation("reserve_email_failed").format(reserve_result.get('reason', getTranslation("unknown_error")))) 61 | return None 62 | 63 | logging.info(getTranslation("email_generated_success").format(email)) 64 | return email 65 | except Exception as e: 66 | logging.error(getTranslation("generate_email_error").format(str(e))) 67 | return None 68 | 69 | async def _generate_multiple_emails(count: int, cookies: str, label: str = "Cursor-Auto-iCloud") -> List[str]: 70 | """ 71 | Generate multiple iCloud Hide My Email addresses 72 | 73 | Args: 74 | count: Number of emails to generate 75 | cookies: iCloud cookies for authentication 76 | label: Label for the emails 77 | 78 | Returns: 79 | List[str]: List of generated email addresses 80 | """ 81 | tasks = [] 82 | for _ in range(count): 83 | tasks.append(_generate_single_email(cookies, label)) 84 | 85 | results = await asyncio.gather(*tasks) 86 | # Filter out None values 87 | return [email for email in results if email] 88 | 89 | def generateIcloudEmail(count: int = 1, save_to_file: bool = True) -> List[str]: 90 | """ 91 | Generate a specified number of iCloud Hide My Email addresses 92 | 93 | Args: 94 | count: Number of emails to generate 95 | save_to_file: Whether to save emails to data/emails.txt 96 | 97 | Returns: 98 | List[str]: List of generated email addresses 99 | """ 100 | # Get iCloud cookies from config 101 | try: 102 | # Get cookies from .env file 103 | cookies = os.getenv('ICLOUD_COOKIES', '').strip() 104 | if not cookies: 105 | logging.error(getTranslation("icloud_cookies_not_configured")) 106 | return [] 107 | 108 | # Generate emails 109 | logging.info(getTranslation("start_generating_emails").format(count)) 110 | emails = asyncio.run(_generate_multiple_emails(count, cookies)) 111 | 112 | if not emails: 113 | logging.error(getTranslation("no_emails_generated")) 114 | return [] 115 | 116 | logging.info(getTranslation("emails_generated_success").format(len(emails))) 117 | 118 | # Save to file if requested 119 | if save_to_file: 120 | data_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "data") 121 | if not os.path.exists(data_dir): 122 | os.makedirs(data_dir) 123 | 124 | emails_file = os.path.join(data_dir, "emails.txt") 125 | 126 | # If file exists, read existing emails 127 | existing_emails = [] 128 | if os.path.exists(emails_file): 129 | with open(emails_file, "r") as f: 130 | existing_emails = [line.strip() for line in f.readlines() if line.strip()] 131 | 132 | # Add new emails 133 | all_emails = existing_emails + emails 134 | 135 | # Write back to file 136 | with open(emails_file, "w") as f: 137 | f.write("\n".join(all_emails)) 138 | 139 | logging.info(getTranslation("emails_saved_to_file").format(emails_file)) 140 | 141 | return emails 142 | 143 | except Exception as e: 144 | logging.error(getTranslation("generate_email_error").format(str(e))) 145 | import traceback 146 | logging.error(traceback.format_exc()) 147 | return [] 148 | 149 | if __name__ == "__main__": 150 | # If run directly, generate 5 emails 151 | count = 5 152 | if len(sys.argv) > 1: 153 | try: 154 | count = int(sys.argv[1]) 155 | except ValueError: 156 | logging.error(getTranslation("invalid_count_parameter").format(sys.argv[1])) 157 | sys.exit(1) 158 | 159 | emails = generateIcloudEmail(count) 160 | if emails: 161 | print(getTranslation("emails_generated_success").format(len(emails))) 162 | for email in emails: 163 | print(email) 164 | else: 165 | print(getTranslation("no_emails_generated")) 166 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Executables 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" # Trigger on tags like v1.0.0 7 | workflow_dispatch: # Allow manual triggering of the workflow 8 | 9 | jobs: 10 | build-windows: 11 | runs-on: windows-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Set up Python 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: "3.9" 20 | 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install pyinstaller 25 | pip install -r requirements.txt 26 | 27 | - name: Build Windows executable 28 | run: | 29 | # Set PowerShell to use UTF-8 encoding 30 | [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 31 | $env:PYTHONIOENCODING = "utf-8" 32 | python build.py --platform windows 33 | 34 | # Check if the output directory exists and has files 35 | if (Test-Path "dist/windows") { 36 | Write-Host "Windows output directory exists" 37 | Get-ChildItem "dist/windows" -Recurse | ForEach-Object { 38 | Write-Host " - $($_.FullName) (Size: $($_.Length) bytes)" 39 | } 40 | 41 | # Check if directory is empty 42 | if (!(Get-ChildItem "dist/windows")) { 43 | Write-Host "Warning: Output directory is empty, creating placeholder file" 44 | "Placeholder file to prevent artifact upload failure" | Out-File -FilePath "dist/windows/placeholder.txt" 45 | } 46 | } else { 47 | Write-Host "Warning: Output directory not found, creating it with a placeholder file" 48 | New-Item -ItemType Directory -Path "dist/windows" -Force 49 | "Placeholder file to prevent artifact upload failure" | Out-File -FilePath "dist/windows/placeholder.txt" 50 | } 51 | 52 | - name: Upload Windows artifact 53 | uses: actions/upload-artifact@v4 54 | with: 55 | name: CursorKeepAlive-Windows 56 | path: dist/windows/ 57 | if-no-files-found: warn 58 | 59 | build-macos-arm64: 60 | runs-on: macos-latest 61 | 62 | steps: 63 | - uses: actions/checkout@v2 64 | 65 | - name: Set up Python 66 | uses: actions/setup-python@v2 67 | with: 68 | python-version: "3.9" 69 | 70 | - name: Install dependencies 71 | run: | 72 | python -m pip install --upgrade pip 73 | pip install pyinstaller 74 | pip install -r requirements.txt 75 | 76 | - name: Build MacOS ARM executable 77 | run: | 78 | python build.py --platform mac_m1 --arch arm64 79 | 80 | # Check if the output directory exists and has files 81 | if [ -d "dist/mac_m1" ]; then 82 | echo "MacOS ARM output directory exists" 83 | find "dist/mac_m1" -type f -exec ls -la {} \; 84 | 85 | # Check if directory is empty 86 | if [ ! "$(ls -A dist/mac_m1)" ]; then 87 | echo "Warning: Output directory is empty, creating placeholder file" 88 | echo "Placeholder file to prevent artifact upload failure" > "dist/mac_m1/placeholder.txt" 89 | fi 90 | else 91 | echo "Warning: Output directory not found, creating it with a placeholder file" 92 | mkdir -p "dist/mac_m1" 93 | echo "Placeholder file to prevent artifact upload failure" > "dist/mac_m1/placeholder.txt" 94 | fi 95 | 96 | - name: Upload MacOS ARM artifact 97 | uses: actions/upload-artifact@v4 98 | with: 99 | name: CursorKeepAlive-MacOS-ARM64 100 | path: dist/mac_m1/ 101 | if-no-files-found: warn 102 | 103 | build-linux: 104 | runs-on: ubuntu-latest 105 | 106 | steps: 107 | - uses: actions/checkout@v2 108 | 109 | - name: Set up Python 110 | uses: actions/setup-python@v2 111 | with: 112 | python-version: "3.9" 113 | 114 | - name: Install dependencies 115 | run: | 116 | python -m pip install --upgrade pip 117 | pip install pyinstaller 118 | pip install -r requirements.txt 119 | 120 | - name: Build Linux executable 121 | run: | 122 | python build.py --platform linux 123 | 124 | # Check if the output directory exists and has files 125 | if [ -d "dist/linux" ]; then 126 | echo "Linux output directory exists" 127 | find "dist/linux" -type f -exec ls -la {} \; 128 | 129 | # Check if directory is empty 130 | if [ ! "$(ls -A dist/linux)" ]; then 131 | echo "Warning: Output directory is empty, creating placeholder file" 132 | echo "Placeholder file to prevent artifact upload failure" > "dist/linux/placeholder.txt" 133 | fi 134 | else 135 | echo "Warning: Output directory not found, creating it with a placeholder file" 136 | mkdir -p "dist/linux" 137 | echo "Placeholder file to prevent artifact upload failure" > "dist/linux/placeholder.txt" 138 | fi 139 | 140 | - name: Upload Linux artifact 141 | uses: actions/upload-artifact@v4 142 | with: 143 | name: CursorKeepAlive-Linux 144 | path: dist/linux/ 145 | if-no-files-found: warn 146 | 147 | build-macos-intel: 148 | runs-on: macos-latest 149 | 150 | steps: 151 | - uses: actions/checkout@v2 152 | 153 | - name: Set up Python 154 | uses: actions/setup-python@v2 155 | with: 156 | python-version: "3.9" 157 | 158 | - name: Install dependencies 159 | run: | 160 | python -m pip install --upgrade pip 161 | pip install pyinstaller 162 | pip install -r requirements.txt 163 | 164 | - name: Build MacOS Intel executable 165 | run: | 166 | python build.py --platform mac_intel 167 | 168 | # Check if the output directory exists and has files 169 | if [ -d "dist/mac_intel" ]; then 170 | echo "MacOS Intel output directory exists" 171 | find "dist/mac_intel" -type f -exec ls -la {} \; 172 | 173 | # Check if directory is empty 174 | if [ ! "$(ls -A dist/mac_intel)" ]; then 175 | echo "Warning: Output directory is empty, creating placeholder file" 176 | echo "Placeholder file to prevent artifact upload failure" > "dist/mac_intel/placeholder.txt" 177 | fi 178 | else 179 | echo "Warning: Output directory not found, creating it with a placeholder file" 180 | mkdir -p "dist/mac_intel" 181 | echo "Placeholder file to prevent artifact upload failure" > "dist/mac_intel/placeholder.txt" 182 | fi 183 | 184 | - name: Upload MacOS Intel artifact 185 | uses: actions/upload-artifact@v4 186 | with: 187 | name: CursorKeepAlive-MacOS-Intel 188 | path: dist/mac_intel/ 189 | if-no-files-found: warn 190 | -------------------------------------------------------------------------------- /src/icloud/hidemyemail.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | import ssl 4 | import certifi 5 | import os 6 | import sys 7 | 8 | # Add parent directory to path to import language module 9 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 10 | if parent_dir not in sys.path: 11 | sys.path.append(parent_dir) 12 | 13 | try: 14 | from src.utils.logger import logging 15 | from src.utils.language import getTranslation, _ 16 | except ImportError: 17 | from utils.logger import logging 18 | from utils.language import getTranslation, _ 19 | 20 | 21 | class HideMyEmail: 22 | base_url_v1 = "https://p68-maildomainws.icloud.com/v1/hme" 23 | base_url_v2 = "https://p68-maildomainws.icloud.com/v2/hme" 24 | params = { 25 | "clientBuildNumber": "2413Project28", 26 | "clientMasteringNumber": "2413B20", 27 | "clientId": "", 28 | "dsid": "", # Directory Services Identifier (DSID) is a method of identifying AppleID accounts 29 | } 30 | 31 | def __init__(self, label: str = "Cursor-Auto-iCloud", cookies: str = ""): 32 | """Initializes the HideMyEmail class. 33 | 34 | Args: 35 | label (str) Label that will be set for all emails generated, defaults to `Cursor-Auto-iCloud` 36 | cookies (str) Cookie string to be used with requests. Required for authorization. 37 | """ 38 | self.label = label 39 | 40 | # Cookie string to be used with requests. Required for authorization. 41 | self.cookies = cookies 42 | 43 | async def __aenter__(self): 44 | connector = aiohttp.TCPConnector(ssl_context=ssl.create_default_context(cafile=certifi.where())) 45 | self.s = aiohttp.ClientSession( 46 | headers={ 47 | "Connection": "keep-alive", 48 | "Pragma": "no-cache", 49 | "Cache-Control": "no-cache", 50 | "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", 51 | "Content-Type": "text/plain", 52 | "Accept": "*/*", 53 | "Sec-GPC": "1", 54 | "Origin": "https://www.icloud.com", 55 | "Sec-Fetch-Site": "same-site", 56 | "Sec-Fetch-Mode": "cors", 57 | "Sec-Fetch-Dest": "empty", 58 | "Referer": "https://www.icloud.com/", 59 | "Accept-Language": "en-US,en-GB;q=0.9,en;q=0.8,cs;q=0.7", 60 | "Cookie": self.__cookies.strip(), 61 | }, 62 | timeout=aiohttp.ClientTimeout(total=10), 63 | connector=connector, 64 | ) 65 | 66 | return self 67 | 68 | async def __aexit__(self, exc_t, exc_v, exc_tb): 69 | await self.s.close() 70 | 71 | @property 72 | def cookies(self) -> str: 73 | return self.__cookies 74 | 75 | @cookies.setter 76 | def cookies(self, cookies: str): 77 | # remove new lines/whitespace for security reasons 78 | self.__cookies = cookies.strip() 79 | 80 | async def generate_email(self) -> dict: 81 | """Generates an email""" 82 | try: 83 | logging.debug(getTranslation("generating_icloud_hidden_email")) 84 | async with self.s.post( 85 | f"{self.base_url_v1}/generate", params=self.params, json={"langCode": "en-us"} 86 | ) as resp: 87 | res = await resp.json() 88 | return res 89 | except asyncio.TimeoutError: 90 | logging.error(getTranslation("generate_email_timeout")) 91 | return {"error": 1, "reason": getTranslation("request_timeout")} 92 | except Exception as e: 93 | logging.error(getTranslation("generate_email_failed").format(str(e))) 94 | return {"error": 1, "reason": str(e)} 95 | 96 | async def reserve_email(self, email: str) -> dict: 97 | """Reserves an email and registers it for forwarding""" 98 | try: 99 | logging.debug(getTranslation("reserving_email").format(email)) 100 | payload = { 101 | "hme": email, 102 | "label": self.label, 103 | "note": "Cursor-Auto-iCloud", 104 | } 105 | async with self.s.post( 106 | f"{self.base_url_v1}/reserve", params=self.params, json=payload 107 | ) as resp: 108 | res = await resp.json() 109 | return res 110 | except asyncio.TimeoutError: 111 | logging.error(getTranslation("reserve_email_timeout")) 112 | return {"error": 1, "reason": getTranslation("request_timeout")} 113 | except Exception as e: 114 | logging.error(getTranslation("reserve_email_failed").format(str(e))) 115 | return {"error": 1, "reason": str(e)} 116 | 117 | async def list_email(self) -> dict: 118 | """List all HME""" 119 | logging.info(getTranslation("getting_email_list")) 120 | try: 121 | async with self.s.get(f"{self.base_url_v2}/list", params=self.params) as resp: 122 | res = await resp.json() 123 | return res 124 | except asyncio.TimeoutError: 125 | logging.error(getTranslation("list_email_timeout")) 126 | return {"error": 1, "reason": getTranslation("request_timeout")} 127 | except Exception as e: 128 | logging.error(getTranslation("list_email_failed").format(str(e))) 129 | return {"error": 1, "reason": str(e)} 130 | 131 | async def deactivate_email(self, anonymous_id: str) -> dict: 132 | """Deactivates an email using its anonymousId""" 133 | logging.info(getTranslation("deactivating_email").format(anonymous_id)) 134 | try: 135 | async with self.s.post(f"{self.base_url_v1}/deactivate", params=self.params, json={"anonymousId": anonymous_id}) as resp: 136 | res = await resp.json() 137 | return res 138 | except asyncio.TimeoutError: 139 | logging.error(getTranslation("deactivate_email_timeout")) 140 | return {"error": 1, "reason": getTranslation("request_timeout")} 141 | except Exception as e: 142 | logging.error(getTranslation("deactivate_email_failed").format(str(e))) 143 | return {"error": 1, "reason": str(e)} 144 | 145 | async def delete_email(self, anonymous_id: str, email: str = None) -> dict: 146 | """Deletes an email using its anonymousId 147 | 148 | Args: 149 | anonymous_id: The anonymousId of the email to delete 150 | email: Optional email address (for logging purposes only) 151 | """ 152 | log_identifier = email if email else anonymous_id 153 | logging.info(getTranslation("deleting_email").format(log_identifier)) 154 | try: 155 | payload = {"anonymousId": anonymous_id} 156 | async with self.s.post(f"{self.base_url_v1}/delete", params=self.params, json=payload) as resp: 157 | res = await resp.json() 158 | print(res) 159 | return res 160 | except asyncio.TimeoutError: 161 | logging.error(getTranslation("delete_email_timeout")) 162 | return {"error": 1, "reason": getTranslation("request_timeout")} 163 | except Exception as e: 164 | print(e) 165 | logging.error(getTranslation("delete_email_failed").format(str(e))) 166 | return {"error": 1, "reason": str(e)} 167 | -------------------------------------------------------------------------------- /src/auth/patch_cursor_get_machine_id.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import json 5 | import logging 6 | import os 7 | import platform 8 | import re 9 | import shutil 10 | import sys 11 | import tempfile 12 | from typing import Tuple 13 | 14 | # Add parent directory to path to import language module 15 | parent_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 16 | if parent_dir not in sys.path: 17 | sys.path.append(parent_dir) 18 | 19 | try: 20 | from src.utils.language import getTranslation, _ 21 | except ImportError: 22 | try: 23 | from utils.language import getTranslation, _ 24 | except ImportError: 25 | # Fallback if language module is not available 26 | def getTranslation(key, *args): 27 | if args: 28 | return key.format(*args) 29 | return key 30 | 31 | 32 | # 配置日志 33 | def setup_logging() -> logging.Logger: 34 | """配置并返回logger实例""" 35 | logger = logging.getLogger(__name__) 36 | logger.setLevel(logging.INFO) 37 | handler = logging.StreamHandler() 38 | formatter = logging.Formatter( 39 | "%(asctime)s - %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S" 40 | ) 41 | handler.setFormatter(formatter) 42 | logger.addHandler(handler) 43 | return logger 44 | 45 | 46 | logger = setup_logging() 47 | 48 | 49 | def get_cursor_paths() -> Tuple[str, str]: 50 | """ 51 | 根据不同操作系统获取 Cursor 相关路径 52 | 53 | Returns: 54 | Tuple[str, str]: (package.json路径, main.js路径)的元组 55 | 56 | Raises: 57 | OSError: 当找不到有效路径或系统不支持时抛出 58 | """ 59 | system = platform.system() 60 | 61 | paths_map = { 62 | "Darwin": { 63 | "base": "/Applications/Cursor.app/Contents/Resources/app", 64 | "package": "package.json", 65 | "main": "out/main.js", 66 | }, 67 | "Windows": { 68 | "base": os.path.join( 69 | os.getenv("USERAPPPATH") or os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app") 70 | ), 71 | "package": "package.json", 72 | "main": "out/main.js", 73 | }, 74 | "Linux": { 75 | "bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"], 76 | "package": "package.json", 77 | "main": "out/main.js", 78 | }, 79 | } 80 | 81 | if system not in paths_map: 82 | raise OSError(getTranslation("unsupported_os").format(system)) 83 | 84 | if system == "Linux": 85 | for base in paths_map["Linux"]["bases"]: 86 | pkg_path = os.path.join(base, paths_map["Linux"]["package"]) 87 | if os.path.exists(pkg_path): 88 | return (pkg_path, os.path.join(base, paths_map["Linux"]["main"])) 89 | raise OSError(getTranslation("cursor_path_not_found_linux")) 90 | 91 | base_path = paths_map[system]["base"] 92 | # 判断Windows是否存在这个文件夹,如果不存在,提示需要创建软连接后重试 93 | if system == "Windows": 94 | if not os.path.exists(base_path): 95 | logging.info(getTranslation("cursor_path_not_default")) 96 | logging.info(getTranslation("create_symlink_command")) 97 | logging.info(getTranslation("example_command")) 98 | logging.info(getTranslation("example_command_path")) 99 | input(getTranslation("press_enter_exit")) 100 | return ( 101 | os.path.join(base_path, paths_map[system]["package"]), 102 | os.path.join(base_path, paths_map[system]["main"]), 103 | ) 104 | 105 | 106 | def check_system_requirements(pkg_path: str, main_path: str) -> bool: 107 | """ 108 | 检查系统要求 109 | 110 | Args: 111 | pkg_path: package.json 文件路径 112 | main_path: main.js 文件路径 113 | 114 | Returns: 115 | bool: 检查是否通过 116 | """ 117 | for file_path in [pkg_path, main_path]: 118 | if not os.path.isfile(file_path): 119 | logger.error(getTranslation("file_not_exist").format(file_path)) 120 | return False 121 | 122 | if not os.access(file_path, os.W_OK): 123 | logger.error(getTranslation("file_no_write_permission").format(file_path)) 124 | return False 125 | 126 | return True 127 | 128 | 129 | def version_check(version: str, min_version: str = "", max_version: str = "") -> bool: 130 | """ 131 | 版本号检查 132 | 133 | Args: 134 | version: 当前版本号 135 | min_version: 最小版本号要求 136 | max_version: 最大版本号要求 137 | 138 | Returns: 139 | bool: 版本号是否符合要求 140 | """ 141 | version_pattern = r"^\d+\.\d+\.\d+$" 142 | try: 143 | if not re.match(version_pattern, version): 144 | logger.error(getTranslation("invalid_version_format").format(version)) 145 | return False 146 | 147 | def parse_version(ver: str) -> Tuple[int, ...]: 148 | return tuple(map(int, ver.split("."))) 149 | 150 | current = parse_version(version) 151 | 152 | if min_version and current < parse_version(min_version): 153 | logger.error(getTranslation("version_below_minimum").format(version, min_version)) 154 | return False 155 | 156 | if max_version and current > parse_version(max_version): 157 | logger.error(getTranslation("version_above_maximum").format(version, max_version)) 158 | return False 159 | 160 | return True 161 | 162 | except Exception as e: 163 | logger.error(getTranslation("version_check_failed").format(str(e))) 164 | return False 165 | 166 | 167 | def modify_main_js(main_path: str) -> bool: 168 | """ 169 | 修改 main.js 文件 170 | 171 | Args: 172 | main_path: main.js 文件路径 173 | 174 | Returns: 175 | bool: 修改是否成功 176 | """ 177 | try: 178 | # 获取原始文件的权限和所有者信息 179 | original_stat = os.stat(main_path) 180 | original_mode = original_stat.st_mode 181 | original_uid = original_stat.st_uid 182 | original_gid = original_stat.st_gid 183 | 184 | with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file: 185 | with open(main_path, "r", encoding="utf-8") as main_file: 186 | content = main_file.read() 187 | 188 | # 执行替换 189 | patterns = { 190 | r"async getMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMachineId(){return \1}", 191 | r"async getMacMachineId\(\)\{return [^??]+\?\?([^}]+)\}": r"async getMacMachineId(){return \1}", 192 | } 193 | 194 | for pattern, replacement in patterns.items(): 195 | content = re.sub(pattern, replacement, content) 196 | 197 | tmp_file.write(content) 198 | tmp_path = tmp_file.name 199 | 200 | # 使用 shutil.copy2 保留文件权限 201 | shutil.copy2(main_path, main_path + ".old") 202 | shutil.move(tmp_path, main_path) 203 | 204 | # 恢复原始文件的权限和所有者 205 | os.chmod(main_path, original_mode) 206 | if os.name != "nt": # 在非Windows系统上设置所有者 207 | os.chown(main_path, original_uid, original_gid) 208 | 209 | logger.info(getTranslation("file_modified_success")) 210 | return True 211 | 212 | except Exception as e: 213 | logger.error(getTranslation("file_modification_error").format(str(e))) 214 | if "tmp_path" in locals(): 215 | os.unlink(tmp_path) 216 | return False 217 | 218 | 219 | def backup_files(pkg_path: str, main_path: str) -> bool: 220 | """ 221 | 备份原始文件 222 | 223 | Args: 224 | pkg_path: package.json 文件路径(未使用) 225 | main_path: main.js 文件路径 226 | 227 | Returns: 228 | bool: 备份是否成功 229 | """ 230 | try: 231 | # 只备份 main.js 232 | if os.path.exists(main_path): 233 | backup_main = f"{main_path}.bak" 234 | shutil.copy2(main_path, backup_main) 235 | logger.info(getTranslation("mainjs_backup_created").format(backup_main)) 236 | 237 | return True 238 | except Exception as e: 239 | logger.error(getTranslation("backup_failed").format(str(e))) 240 | return False 241 | 242 | 243 | def restore_backup_files(pkg_path: str, main_path: str) -> bool: 244 | """ 245 | 恢复备份文件 246 | 247 | Args: 248 | pkg_path: package.json 文件路径(未使用) 249 | main_path: main.js 文件路径 250 | 251 | Returns: 252 | bool: 恢复是否成功 253 | """ 254 | try: 255 | # 只恢复 main.js 256 | backup_main = f"{main_path}.bak" 257 | if os.path.exists(backup_main): 258 | shutil.copy2(backup_main, main_path) 259 | logger.info(getTranslation("mainjs_restored")) 260 | return True 261 | 262 | logger.error(getTranslation("backup_not_found")) 263 | return False 264 | except Exception as e: 265 | logger.error(getTranslation("restore_backup_failed").format(str(e))) 266 | return False 267 | 268 | 269 | def patch_cursor_get_machine_id(restore_mode=False) -> None: 270 | """ 271 | 主函数 272 | 273 | Args: 274 | restore_mode: 是否为恢复模式 275 | """ 276 | logger.info(getTranslation("script_execution_started")) 277 | 278 | try: 279 | # 获取路径 280 | pkg_path, main_path = get_cursor_paths() 281 | 282 | # 检查系统要求 283 | if not check_system_requirements(pkg_path, main_path): 284 | sys.exit(1) 285 | 286 | if restore_mode: 287 | # 恢复备份 288 | if restore_backup_files(pkg_path, main_path): 289 | logger.info(getTranslation("backup_restore_complete")) 290 | else: 291 | logger.error(getTranslation("backup_restore_failed")) 292 | return 293 | 294 | # 获取版本号 295 | try: 296 | with open(pkg_path, "r", encoding="utf-8") as f: 297 | version = json.load(f)["version"] 298 | logger.info(getTranslation("current_cursor_version").format(version)) 299 | except Exception as e: 300 | logger.error(getTranslation("reading_version_failed").format(str(e))) 301 | sys.exit(1) 302 | 303 | # 检查版本 304 | if not version_check(version, min_version="0.45.0"): 305 | logger.error(getTranslation("version_not_supported")) 306 | sys.exit(1) 307 | 308 | logger.info(getTranslation("version_check_passed")) 309 | 310 | # 备份文件 311 | if not backup_files(pkg_path, main_path): 312 | logger.error(getTranslation("backup_failed_abort")) 313 | sys.exit(1) 314 | 315 | # 修改文件 316 | if not modify_main_js(main_path): 317 | sys.exit(1) 318 | 319 | logger.info(getTranslation("script_execution_complete")) 320 | 321 | except Exception as e: 322 | logger.error(getTranslation("execution_error").format(str(e))) 323 | sys.exit(1) 324 | 325 | 326 | if __name__ == "__main__": 327 | patch_cursor_get_machine_id() 328 | -------------------------------------------------------------------------------- /src/utils/get_email_code.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import logging 3 | import time 4 | import re 5 | from config import Config 6 | import requests 7 | import email 8 | import imaplib 9 | import poplib 10 | from email.parser import Parser 11 | import json 12 | import socket 13 | 14 | # Import translation functions 15 | from src.utils.language import getTranslation, _ 16 | 17 | 18 | class EmailVerificationHandler: 19 | def __init__(self,account): 20 | self.session = requests.Session() 21 | self.account = account 22 | 23 | def get_verification_code(self, max_retries=5, retry_interval=60): 24 | """ 25 | 获取验证码,带有重试机制。 26 | 27 | Args: 28 | max_retries: 最大重试次数。 29 | retry_interval: 重试间隔时间(秒)。 30 | 31 | Returns: 32 | 验证码 (字符串或 None)。 33 | """ 34 | 35 | for attempt in range(max_retries): 36 | try: 37 | logging.info(getTranslation("verification_code_attempt").format(attempt + 1, max_retries)) 38 | 39 | verify_code = self._get_latest_mail_code() 40 | if attempt < max_retries - 1 and not verify_code: # 除了最后一次尝试,都等待 41 | logging.warning(getTranslation("verification_code_not_found_retry").format(retry_interval)) 42 | time.sleep(retry_interval) 43 | else: 44 | return verify_code 45 | 46 | except Exception as e: 47 | logging.error(getTranslation("verification_code_fetch_failed").format(e)) # 记录更一般的异常 48 | if attempt < max_retries - 1: 49 | logging.error(getTranslation("error_will_retry").format(retry_interval)) 50 | time.sleep(retry_interval) 51 | else: 52 | raise Exception(getTranslation("max_retries_reached_with_error").format(e)) from e 53 | 54 | raise Exception(getTranslation("verification_code_not_found_after_attempts").format(max_retries)) 55 | 56 | # 手动输入验证码 57 | def _get_latest_mail_code(self): 58 | """ 59 | 获取最新邮件中的验证码: 60 | 1. iCloud IMAP 61 | 62 | Returns: 63 | str or tuple: 验证码或 (验证码, 邮件ID) 元组 64 | """ 65 | # 首先尝试 iCloud IMAP 66 | icloud_imap = Config().get_icloud_imap() 67 | if icloud_imap: 68 | logging.info(getTranslation("using_icloud_imap")) 69 | # First check inbox 70 | verify_code = self._get_mail_code_by_icloud_imap(icloud_imap) 71 | if verify_code: 72 | return verify_code 73 | 74 | # If no code found in inbox, check spam/junk folders 75 | logging.info(getTranslation("checking_spam_folders")) 76 | verify_code = self._check_spam_folders(icloud_imap) 77 | if verify_code: 78 | return verify_code 79 | 80 | return None 81 | 82 | def _check_spam_folders(self, icloud_config): 83 | """Check spam and junk folders for verification code 84 | 85 | Args: 86 | icloud_config: iCloud IMAP configuration 87 | 88 | Returns: 89 | str or None: verification code if found 90 | """ 91 | # Common spam/junk folder names in email services 92 | spam_folders = ['Junk', 'Spam', 'Bulk Mail', 'Junk E-mail'] 93 | 94 | # Get list of available folders first 95 | try: 96 | mail = imaplib.IMAP4_SSL(icloud_config['imap_server'], icloud_config['imap_port']) 97 | mail.login(icloud_config['imap_user'], icloud_config['imap_pass']) 98 | 99 | status, folder_list = mail.list() 100 | mail.logout() 101 | 102 | if status != 'OK': 103 | logging.error(getTranslation("icloud_folder_list_failed")) 104 | return None 105 | 106 | # Parse folder names from the response 107 | available_folders = [] 108 | for folder_info in folder_list: 109 | if isinstance(folder_info, bytes): 110 | parts = folder_info.decode('utf-8', errors='ignore').split('"') 111 | if len(parts) > 1: 112 | folder_name = parts[-2] 113 | available_folders.append(folder_name) 114 | 115 | # Filter spam folders that actually exist 116 | valid_spam_folders = [folder for folder in spam_folders if folder in available_folders] 117 | 118 | # If no valid spam folders found, try the default list 119 | if not valid_spam_folders: 120 | valid_spam_folders = spam_folders 121 | 122 | logging.info(f"Available spam folders: {valid_spam_folders}") 123 | 124 | except Exception as e: 125 | logging.error(getTranslation("icloud_folder_list_failed") + f": {str(e)}") 126 | # Continue with the default list if we can't get available folders 127 | valid_spam_folders = spam_folders 128 | 129 | # Check each potential spam folder 130 | for folder in valid_spam_folders: 131 | try: 132 | # Create a new connection for each folder to avoid state issues 133 | mail = None 134 | try: 135 | mail = imaplib.IMAP4_SSL(icloud_config['imap_server'], icloud_config['imap_port']) 136 | mail.login(icloud_config['imap_user'], icloud_config['imap_pass']) 137 | mail.select(folder) 138 | except Exception as e: 139 | logging.error(getTranslation("error_checking_folder").format(folder, f"Failed to select folder: {e}")) 140 | if mail: 141 | try: 142 | mail.logout() 143 | except: 144 | pass 145 | continue # Try next folder 146 | 147 | logging.info(getTranslation("checking_folder").format(folder)) 148 | 149 | try: 150 | status, messages = mail.search(None, 'ALL') 151 | if status != 'OK': 152 | logging.error(f"Search failed in folder {folder}: {status}") 153 | mail.logout() 154 | continue # Try next folder 155 | 156 | # Handle different message types (bytes or string) 157 | if isinstance(messages[0], bytes): 158 | mail_ids = messages[0].split() 159 | elif isinstance(messages[0], str): 160 | mail_ids = messages[0].encode('utf-8').split() 161 | else: 162 | mail_ids = [] 163 | 164 | if not mail_ids: 165 | logging.info(f"No emails in folder {folder}") 166 | mail.logout() 167 | continue # No emails in this folder 168 | 169 | # Check the latest 10 emails in this folder 170 | for i in range(min(10, len(mail_ids))): 171 | mail_id = mail_ids[len(mail_ids) - 1 - i] 172 | try: 173 | status, msg_data = mail.fetch(mail_id, '(BODY[])') 174 | except (EOFError, ConnectionError, socket.error) as e: 175 | logging.error(getTranslation("icloud_imap_fetch_failed").format(e)) 176 | break # Connection issue, try next folder 177 | 178 | if status != 'OK' or not msg_data or not msg_data[0]: 179 | logging.error(getTranslation("icloud_imap_fetch_status_failed").format(status)) 180 | continue 181 | 182 | # Safety check for data structure 183 | if not isinstance(msg_data[0], tuple) or len(msg_data[0]) < 2: 184 | logging.error(f"Unexpected message data structure: {msg_data}") 185 | continue 186 | 187 | raw_email = msg_data[0][1] 188 | if not raw_email: 189 | continue 190 | 191 | email_message = email.message_from_bytes(raw_email) 192 | sender = email_message.get('from', '') 193 | recipient = email_message.get('to', '') 194 | 195 | if self.account not in recipient: 196 | continue 197 | if 'no-reply_at_cursor_sh' not in sender: 198 | continue 199 | 200 | body = self._extract_imap_body(email_message) 201 | if body: 202 | # Look for 6-digit verification code 203 | code_match = re.search(r"(? 0: 243 | time.sleep(3) 244 | if retry >= 20: 245 | raise Exception(getTranslation("verification_code_timeout")) 246 | 247 | try: 248 | # 连接到 iCloud IMAP 服务器 249 | mail = imaplib.IMAP4_SSL(icloud_config['imap_server'], icloud_config['imap_port']) 250 | 251 | mail.login(icloud_config['imap_user'], icloud_config['imap_pass']) 252 | mail.select(icloud_config['imap_dir'] or 'INBOX') 253 | 254 | # 获取最近的邮件 255 | status, messages = mail.search(None, 'ALL') 256 | if status != 'OK': 257 | logging.error(getTranslation("icloud_email_list_failed").format(status)) 258 | return None 259 | 260 | mail_ids = messages[0].split() 261 | print(mail_ids) 262 | if not mail_ids: 263 | # 没有获取到邮件 264 | logging.info(getTranslation("no_emails_in_icloud")) 265 | return self._get_mail_code_by_icloud_imap(icloud_config, retry=retry + 1) 266 | 267 | # 检查最新的10封邮件 268 | for i in range(min(10, len(mail_ids))): 269 | mail_id = mail_ids[len(mail_ids) - 1 - i] 270 | try: 271 | status, msg_data = mail.fetch(mail_id, '(BODY[])') 272 | except (EOFError, ConnectionError, socket.error) as e: 273 | logging.error(getTranslation("icloud_imap_fetch_failed").format(e)) 274 | mail.logout() 275 | return None 276 | if status != 'OK': 277 | logging.error(getTranslation("icloud_imap_fetch_status_failed").format(status)) 278 | continue 279 | raw_email = msg_data[0][1] 280 | 281 | email_message = email.message_from_bytes(raw_email) 282 | sender = email_message.get('from', '') 283 | recipient = email_message.get('to', '') 284 | 285 | if self.account not in recipient: 286 | continue 287 | if 'no-reply_at_cursor_sh' not in sender: 288 | continue 289 | 290 | 291 | body = self._extract_imap_body(email_message) 292 | if body: 293 | # 查找 6 位数验证码 294 | code_match = re.search(r"(? *Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible.* 4 | > 5 | > ### Using Creative Commons Public Licenses 6 | > 7 | > Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. 8 | > 9 | > * __Considerations for licensors:__ Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. [More considerations for licensors](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors). 10 | > 11 | > * __Considerations for the public:__ By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. [More considerations for the public](http://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees). 12 | 13 | ## Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License 14 | 15 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. 16 | 17 | ### Section 1 – Definitions. 18 | 19 | a. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. 20 | 21 | b. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. 22 | 23 | e. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. 24 | 25 | f. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. 26 | 27 | h. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License. 28 | 29 | i. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. 30 | 31 | h. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License. 32 | 33 | i. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange. 34 | 35 | j. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. 36 | 37 | k. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. 38 | 39 | l. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. 40 | 41 | ### Section 2 – Scope. 42 | 43 | a. ___License grant.___ 44 | 45 | 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: 46 | 47 | A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and 48 | 49 | B. produce and reproduce, but not Share, Adapted Material for NonCommercial purposes only. 50 | 51 | 2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 52 | 53 | 3. __Term.__ The term of this Public License is specified in Section 6(a). 54 | 55 | 4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 56 | 57 | 5. __Downstream recipients.__ 58 | 59 | A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. 60 | 61 | B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 62 | 63 | 6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). 64 | 65 | b. ___Other rights.___ 66 | 67 | 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 68 | 69 | 2. Patent and trademark rights are not licensed under this Public License. 70 | 71 | 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes. 72 | 73 | ### Section 3 – License Conditions. 74 | 75 | Your exercise of the Licensed Rights is expressly made subject to the following conditions. 76 | 77 | a. ___Attribution.___ 78 | 79 | 1. If You Share the Licensed Material, You must: 80 | 81 | A. retain the following if it is supplied by the Licensor with the Licensed Material: 82 | 83 | i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); 84 | 85 | ii. a copyright notice; 86 | 87 | iii. a notice that refers to this Public License; 88 | 89 | iv. a notice that refers to the disclaimer of warranties; 90 | 91 | v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; 92 | 93 | B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and 94 | 95 | C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 96 | 97 | For the avoidance of doubt, You do not have permission under this Public License to Share Adapted Material. 98 | 99 | 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 100 | 101 | 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 102 | 103 | ### Section 4 – Sui Generis Database Rights. 104 | 105 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: 106 | 107 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only and provided You do not Share Adapted Material; 108 | 109 | b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and 110 | 111 | c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. 112 | 113 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. 114 | 115 | ### Section 5 – Disclaimer of Warranties and Limitation of Liability. 116 | 117 | a. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__ 118 | 119 | b. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__ 120 | 121 | c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. 122 | 123 | ### Section 6 – Term and Termination. 124 | 125 | a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. 126 | 127 | b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 128 | 129 | 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 130 | 131 | 2. upon express reinstatement by the Licensor. 132 | 133 | For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. 134 | 135 | c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. 136 | 137 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 138 | 139 | ### Section 7 – Other Terms and Conditions. 140 | 141 | a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. 142 | 143 | b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. 144 | 145 | ### Section 8 – Interpretation. 146 | 147 | a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. 148 | 149 | b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. 150 | 151 | c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. 152 | 153 | d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. 154 | 155 | > Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at [creativecommons.org/policies](http://creativecommons.org/policies), Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. 156 | > 157 | > Creative Commons may be contacted at [creativecommons.org](http://creativecommons.org). 158 | -------------------------------------------------------------------------------- /src/core/cursor_pro_keep_alive.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import json 4 | import sys 5 | import csv 6 | import uuid 7 | import secrets 8 | import hashlib 9 | import base64 10 | from pathlib import Path 11 | import dotenv 12 | import requests 13 | 14 | from enum import Enum 15 | from typing import Optional, Tuple 16 | 17 | from src.core.exit_cursor import ExitCursor 18 | import src.core.go_cursor_help as go_cursor_help 19 | import src.auth.patch_cursor_get_machine_id as patch_cursor_get_machine_id 20 | from src.auth.reset_machine import MachineIDResetter 21 | 22 | from src.icloud.generateEmail import generateIcloudEmail 23 | from src.icloud.deleteEmail import deleteIcloudEmail 24 | 25 | os.environ["PYTHONVERBOSE"] = "0" 26 | os.environ["PYINSTALLER_VERBOSE"] = "0" 27 | 28 | import time 29 | import random 30 | from src.auth.cursor_auth_manager import CursorAuthManager 31 | import os 32 | from src.utils.logger import logging 33 | from src.utils.browser_utils import BrowserManager 34 | from src.utils.get_email_code import EmailVerificationHandler 35 | from src.ui.logo import print_logo 36 | from src.utils.config import Config 37 | from src.utils.language import LanguageManager, Language, _, getTranslation 38 | from datetime import datetime 39 | 40 | # 定义 EMOJI 字典 41 | EMOJI = {"ERROR": "❌", "WARNING": "⚠️", "INFO": "ℹ️"} 42 | 43 | 44 | class VerificationStatus(Enum): 45 | """验证状态枚举""" 46 | 47 | PASSWORD_PAGE = "@name=password" 48 | CAPTCHA_PAGE = "@data-index=0" 49 | ACCOUNT_SETTINGS = "Account Settings" 50 | 51 | 52 | class TurnstileError(Exception): 53 | """Turnstile 验证相关异常""" 54 | 55 | pass 56 | 57 | 58 | def save_screenshot(tab, stage: str, timestamp: bool = True) -> None: 59 | """ 60 | 保存页面截图 61 | 62 | Args: 63 | tab: 浏览器标签页对象 64 | stage: 截图阶段标识 65 | timestamp: 是否添加时间戳 66 | """ 67 | try: 68 | # 创建 screenshots 目录 69 | screenshot_dir = "screenshots" 70 | if not os.path.exists(screenshot_dir): 71 | os.makedirs(screenshot_dir) 72 | 73 | # 生成文件名 74 | if timestamp: 75 | filename = f"turnstile_{stage}_{int(time.time())}.png" 76 | else: 77 | filename = f"turnstile_{stage}.png" 78 | 79 | filepath = os.path.join(screenshot_dir, filename) 80 | 81 | # 保存截图 82 | tab.get_screenshot(filepath) 83 | logging.debug(getTranslation("screenshot_saved").format(filepath)) 84 | except Exception as e: 85 | logging.warning(getTranslation("screenshot_save_failed").format(str(e))) 86 | 87 | 88 | def check_verification_success(tab) -> Optional[VerificationStatus]: 89 | """ 90 | 检查验证是否成功 91 | 92 | Returns: 93 | VerificationStatus: 验证成功时返回对应状态,失败返回 None 94 | """ 95 | for status in VerificationStatus: 96 | if tab.ele(status.value): 97 | logging.info(getTranslation("verification_success_page").format(status.name)) 98 | return status 99 | return None 100 | 101 | 102 | def handle_turnstile(tab, max_retries: int = 2, retry_interval: tuple = (1, 2)) -> bool: 103 | """ 104 | 处理 Turnstile 验证 105 | 106 | Args: 107 | tab: 浏览器标签页对象 108 | max_retries: 最大重试次数 109 | retry_interval: 重试间隔时间范围(最小值, 最大值) 110 | 111 | Returns: 112 | bool: 验证是否成功 113 | 114 | Raises: 115 | TurnstileError: 验证过程中出现异常 116 | """ 117 | logging.info(getTranslation("detecting_turnstile")) 118 | save_screenshot(tab, "start") 119 | 120 | retry_count = 0 121 | 122 | try: 123 | while retry_count < max_retries: 124 | retry_count += 1 125 | logging.debug(getTranslation("verification_attempt").format(retry_count)) 126 | 127 | try: 128 | # 定位验证框元素 129 | challenge_check = ( 130 | tab.ele("@id=cf-turnstile", timeout=2) 131 | .child() 132 | .shadow_root.ele("tag:iframe") 133 | .ele("tag:body") 134 | .sr("tag:input") 135 | ) 136 | 137 | if challenge_check: 138 | logging.info(getTranslation("turnstile_detected")) 139 | # 随机延时后点击验证 140 | time.sleep(random.uniform(1, 3)) 141 | challenge_check.click() 142 | time.sleep(2) 143 | 144 | # 保存验证后的截图 145 | save_screenshot(tab, "clicked") 146 | 147 | # 检查验证结果 148 | if check_verification_success(tab): 149 | logging.info(getTranslation("turnstile_passed")) 150 | save_screenshot(tab, "success") 151 | return True 152 | 153 | except Exception as e: 154 | logging.debug(getTranslation("attempt_failed").format(str(e))) 155 | 156 | # 检查是否已经验证成功 157 | if check_verification_success(tab): 158 | return True 159 | 160 | # 随机延时后继续下一次尝试 161 | time.sleep(random.uniform(*retry_interval)) 162 | 163 | # 超出最大重试次数 164 | logging.error(getTranslation("verification_max_retries_reached").format(max_retries)) 165 | logging.error(getTranslation("visit_project_for_info")) 166 | save_screenshot(tab, "failed") 167 | return False 168 | 169 | except Exception as e: 170 | error_msg = getTranslation("turnstile_exception").format(str(e)) 171 | logging.error(error_msg) 172 | save_screenshot(tab, "error") 173 | raise TurnstileError(error_msg) 174 | 175 | 176 | 177 | def get_cursor_session_token(tab, max_attempts: int = 3, retry_interval: int = 2) -> Optional[Tuple[str, str]]: 178 | """ 179 | 获取Cursor会话token 180 | 181 | Args: 182 | tab: 浏览器标签页对象 183 | max_attempts: 最大尝试次数 184 | retry_interval: 重试间隔(秒) 185 | 186 | Returns: 187 | Tuple[str, str] | None: 成功返回(userId, accessToken)元组,失败返回None 188 | """ 189 | logging.info(getTranslation("start_getting_session_token")) 190 | 191 | # 首先尝试使用UUID深度登录方式 192 | logging.info(getTranslation("try_deep_login")) 193 | 194 | def _generate_pkce_pair(): 195 | """生成PKCE验证对""" 196 | code_verifier = secrets.token_urlsafe(43) 197 | code_challenge_digest = hashlib.sha256(code_verifier.encode('utf-8')).digest() 198 | code_challenge = base64.urlsafe_b64encode(code_challenge_digest).decode('utf-8').rstrip('=') 199 | return code_verifier, code_challenge 200 | 201 | attempts = 0 202 | while attempts < max_attempts: 203 | try: 204 | verifier, challenge = _generate_pkce_pair() 205 | id = uuid.uuid4() 206 | client_login_url = f"https://www.cursor.com/cn/loginDeepControl?challenge={challenge}&uuid={id}&mode=login" 207 | 208 | logging.info(getTranslation("visiting_deep_login_url").format(client_login_url)) 209 | tab.get(client_login_url) 210 | save_screenshot(tab, f"deeplogin_attempt_{attempts}") 211 | 212 | if tab.ele("xpath=//span[contains(text(), 'Yes, Log In')]", timeout=5): 213 | logging.info(getTranslation("clicking_confirm_login")) 214 | tab.ele("xpath=//span[contains(text(), 'Yes, Log In')]").click() 215 | time.sleep(1.5) 216 | 217 | auth_poll_url = f"https://api2.cursor.sh/auth/poll?uuid={id}&verifier={verifier}" 218 | headers = { 219 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Cursor/0.48.6 Chrome/132.0.6834.210 Electron/34.3.4 Safari/537.36", 220 | "Accept": "*/*" 221 | } 222 | 223 | logging.info(getTranslation("polling_auth_status").format(auth_poll_url)) 224 | response = requests.get(auth_poll_url, headers=headers, timeout=5) 225 | 226 | if response.status_code == 200: 227 | data = response.json() 228 | accessToken = data.get("accessToken", None) 229 | authId = data.get("authId", "") 230 | 231 | if accessToken: 232 | userId = "" 233 | if len(authId.split("|")) > 1: 234 | userId = authId.split("|")[1] 235 | 236 | logging.info(getTranslation("token_userid_success")) 237 | return userId, accessToken 238 | else: 239 | logging.error(getTranslation("api_request_failed").format(response.status_code)) 240 | else: 241 | logging.warning(getTranslation("login_confirm_button_not_found")) 242 | 243 | attempts += 1 244 | if attempts < max_attempts: 245 | wait_time = retry_interval * attempts # 逐步增加等待时间 246 | logging.warning(getTranslation("token_attempt_failed").format(attempts, wait_time)) 247 | save_screenshot(tab, f"token_attempt_{attempts}") 248 | time.sleep(wait_time) 249 | 250 | except Exception as e: 251 | logging.error(getTranslation("deep_login_token_failed").format(str(e))) 252 | attempts += 1 253 | save_screenshot(tab, f"token_error_{attempts}") 254 | if attempts < max_attempts: 255 | wait_time = retry_interval * attempts 256 | logging.warning(getTranslation("retry_in_seconds").format(wait_time)) 257 | time.sleep(wait_time) 258 | 259 | # 所有尝试都失败后返回None 260 | logging.error(getTranslation("max_attempts_reached").format(max_attempts)) 261 | return None 262 | 263 | 264 | def update_cursor_auth(email=None, access_token=None, refresh_token=None): 265 | """ 266 | 更新Cursor的认证信息的便捷函数 267 | """ 268 | auth_manager = CursorAuthManager() 269 | return auth_manager.update_auth(email, access_token, refresh_token) 270 | 271 | 272 | def sign_up_account(browser, tab, sign_up_url, settings_url, first_name, last_name, account, password, email_handler, manual_code): 273 | """ 274 | Handle the account sign-up process 275 | 276 | Args: 277 | browser: Browser instance 278 | tab: Browser tab 279 | sign_up_url: URL for the signup page 280 | settings_url: URL for the settings page 281 | first_name: First name for the account 282 | last_name: Last name for the account 283 | account: Email account 284 | password: Password for the account 285 | email_handler: Email verification handler 286 | 287 | Returns: 288 | bool: True if signup was successful, False otherwise 289 | """ 290 | logging.info(getTranslation("start_registration")) 291 | logging.info(getTranslation("visiting_login_page").format(sign_up_url)) 292 | tab.get(sign_up_url) 293 | 294 | try: 295 | if tab.ele("@name=first_name"): 296 | logging.info(getTranslation("filling_personal_info")) 297 | tab.actions.click("@name=first_name").input(first_name) 298 | logging.info(getTranslation("input_first_name").format(first_name)) 299 | time.sleep(random.uniform(1, 3)) 300 | 301 | tab.actions.click("@name=last_name").input(last_name) 302 | logging.info(getTranslation("input_last_name").format(last_name)) 303 | time.sleep(random.uniform(1, 3)) 304 | 305 | tab.actions.click("@name=email").input(account) 306 | logging.info(getTranslation("input_email").format(account)) 307 | time.sleep(random.uniform(1, 3)) 308 | 309 | logging.info(getTranslation("submit_personal_info")) 310 | tab.actions.click("@type=submit") 311 | 312 | except Exception as e: 313 | logging.error(getTranslation("signup_page_access_failed").format(str(e))) 314 | return False 315 | 316 | handle_turnstile(tab) 317 | 318 | try: 319 | if tab.ele("@name=password"): 320 | logging.info(getTranslation("setting_password")) 321 | tab.ele("@name=password").input(password) 322 | time.sleep(random.uniform(1, 3)) 323 | 324 | logging.info(getTranslation("submit_password")) 325 | tab.ele("@type=submit").click() 326 | logging.info(getTranslation("password_setup_complete")) 327 | 328 | except Exception as e: 329 | logging.error(getTranslation("password_setup_failed").format(str(e))) 330 | return False 331 | 332 | if tab.ele("This email is not available."): 333 | logging.error(getTranslation("email_already_used")) 334 | return False 335 | 336 | handle_turnstile(tab) 337 | 338 | while True: 339 | try: 340 | if tab.ele("Account Settings"): 341 | logging.info(getTranslation("registration_successful")) 342 | break 343 | if tab.ele("@data-index=0"): 344 | logging.info(getTranslation("getting_verification_code")) 345 | if not manual_code: 346 | code = email_handler.get_verification_code() 347 | else: 348 | code = input(getTranslation("manual_code_input")) 349 | if not code: 350 | logging.error(getTranslation("verification_code_failed")) 351 | return False 352 | 353 | logging.info(getTranslation("verification_code_success").format(code)) 354 | logging.info(getTranslation("entering_verification_code")) 355 | i = 0 356 | for digit in code: 357 | tab.ele(f"@data-index={i}").input(digit) 358 | time.sleep(random.uniform(0.1, 0.3)) 359 | i += 1 360 | logging.info(getTranslation("verification_code_complete")) 361 | break 362 | except Exception as e: 363 | logging.error(getTranslation("verification_process_error").format(str(e))) 364 | 365 | handle_turnstile(tab) 366 | wait_time = random.randint(3, 6) 367 | for i in range(wait_time): 368 | logging.info(getTranslation("waiting_for_processing").format(wait_time-i)) 369 | time.sleep(1) 370 | 371 | logging.info(getTranslation("getting_account_info")) 372 | tab.get(settings_url) 373 | try: 374 | usage_selector = ( 375 | "css:div.col-span-2 > div > div > div > div > " 376 | "div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > " 377 | "span.font-mono.text-sm\\/\\[0\\.875rem\\]" 378 | ) 379 | usage_ele = tab.ele(usage_selector) 380 | if usage_ele: 381 | usage_info = usage_ele.text 382 | total_usage = usage_info.split("/")[-1].strip() 383 | logging.info(getTranslation("account_usage_limit").format(total_usage)) 384 | logging.info(getTranslation("visit_project_for_info")) 385 | 386 | except Exception as e: 387 | logging.error(getTranslation("get_account_limit_failed").format(str(e))) 388 | 389 | logging.info(getTranslation("registration_complete")) 390 | account_info = getTranslation("cursor_account_info").format(account, password) 391 | logging.info(account_info) 392 | time.sleep(5) 393 | return True 394 | 395 | 396 | def get_env_directory(): 397 | """ 398 | Get the directory where the .env file is located. 399 | Returns the directory path if .env exists, otherwise returns the current directory. 400 | """ 401 | # Check common locations for .env file 402 | possible_env_paths = [ 403 | ".env", # Current directory 404 | os.path.join(os.path.dirname(sys.executable), ".env"), # Next to executable 405 | os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), ".env") # Project root 406 | ] 407 | 408 | for env_path in possible_env_paths: 409 | if os.path.exists(env_path): 410 | return os.path.dirname(os.path.abspath(env_path)) 411 | 412 | # If .env is not found, return current directory 413 | return os.path.abspath(".") 414 | 415 | 416 | class EmailGenerator: 417 | def __init__( 418 | self, 419 | password="".join( 420 | random.choices( 421 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*", 422 | k=12, 423 | ) 424 | ), 425 | use_icloud=False, 426 | delete_after_use=False 427 | ): 428 | configInstance = Config() 429 | configInstance.print_config() 430 | self.names = self.load_names() 431 | self.default_password = password 432 | self.default_first_name = self.generate_random_name() 433 | self.default_last_name = self.generate_random_name() 434 | self.use_icloud = use_icloud 435 | self.delete_after_use = delete_after_use 436 | self.generated_email = None 437 | self.generateIcloudEmail = None 438 | self.deleteIcloudEmail = None 439 | 440 | # Try to load dotenv config if exists 441 | try: 442 | dotenv.load_dotenv() 443 | except Exception as e: 444 | logging.warning(getTranslation("env_file_load_failed").format(str(e))) 445 | 446 | # Try to import iCloud email generator if use_icloud is True 447 | if self.use_icloud: 448 | try: 449 | # Import the modules from the correct location 450 | from src.icloud.generateEmail import generateIcloudEmail 451 | from src.icloud.deleteEmail import deleteIcloudEmail 452 | self.generateIcloudEmail = generateIcloudEmail 453 | self.deleteIcloudEmail = deleteIcloudEmail 454 | logging.info(getTranslation("icloud_feature_enabled")) 455 | except ImportError: 456 | try: 457 | # Try relative import as fallback 458 | current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 459 | if current_dir not in sys.path: 460 | sys.path.append(current_dir) 461 | from icloud.generateEmail import generateIcloudEmail 462 | from icloud.deleteEmail import deleteIcloudEmail 463 | self.generateIcloudEmail = generateIcloudEmail 464 | self.deleteIcloudEmail = deleteIcloudEmail 465 | logging.info(getTranslation("icloud_feature_enabled")) 466 | except ImportError: 467 | logging.error(getTranslation("icloud_module_import_failed_local")) 468 | self.use_icloud = False 469 | 470 | def load_names(self): 471 | """Load names from names-dataset.txt file""" 472 | # Look for the file in the executable directory first, then in the project structure 473 | possible_paths = [ 474 | "names-dataset.txt", # In the current/executable directory 475 | os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 476 | "data", "names-dataset.txt") # Project structure path 477 | ] 478 | 479 | for names_file_path in possible_paths: 480 | try: 481 | with open(names_file_path, "r") as file: 482 | logging.info(getTranslation("names_dataset_loaded").format(names_file_path)) 483 | return file.read().split() 484 | except FileNotFoundError: 485 | continue 486 | 487 | logging.error(getTranslation("names_dataset_not_found")) 488 | # Return a small set of default names as fallback 489 | return ["John", "Jane", "Michael", "Emma", "Robert", "Olivia"] 490 | 491 | def generate_random_name(self): 492 | """生成随机用户名""" 493 | return random.choice(self.names) 494 | 495 | def get_emails_file_path(self): 496 | """Get the path to the emails.txt file, prioritizing accessible locations""" 497 | # Check if EMAIL_FILE_PATH is defined in .env 498 | env_path = os.environ.get("EMAIL_FILE_PATH") 499 | if env_path and os.path.exists(env_path): 500 | return env_path 501 | 502 | # First try to place emails.txt in the same directory as .env 503 | env_dir = get_env_directory() 504 | env_dir_path = os.path.join(env_dir, "emails.txt") 505 | 506 | # Try common locations 507 | possible_paths = [ 508 | env_dir_path, # Same directory as .env 509 | "data/emails.txt", # In the current/executable directory 510 | os.path.join(os.path.dirname(sys.executable), "data", "emails.txt"), # Next to executable 511 | os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 512 | "data", "emails.txt") # Project structure path 513 | ] 514 | 515 | for path in possible_paths: 516 | if os.path.exists(path): 517 | return path 518 | 519 | # Default to the same location as .env file 520 | default_path = env_dir_path 521 | try: 522 | # Make sure the directory exists 523 | os.makedirs(os.path.dirname(default_path), exist_ok=True) 524 | except: 525 | # If creating directory fails, use data directory as fallback 526 | default_path = "data/emails.txt" 527 | os.makedirs(os.path.dirname(default_path), exist_ok=True) 528 | 529 | return default_path 530 | 531 | def generate_email(self, length=4): 532 | """ 533 | 生成随机邮箱地址,如果启用了 iCloud 功能则使用 iCloud 隐藏邮箱 534 | """ 535 | # If iCloud is enabled, try to generate an iCloud email 536 | if self.use_icloud: 537 | try: 538 | emails = self.generateIcloudEmail(1, True) 539 | if emails and len(emails) > 0: 540 | self.generated_email = emails[0] 541 | return self.generated_email 542 | else: 543 | logging.warning(getTranslation("icloud_email_gen_failed")) 544 | except Exception as e: 545 | logging.error(getTranslation("icloud_email_gen_error").format(str(e))) 546 | logging.warning(getTranslation("using_local_email_list")) 547 | 548 | # If iCloud failed or not enabled, use local email list 549 | emails_file_path = self.get_emails_file_path() 550 | logging.info(f"Using emails file: {emails_file_path}") 551 | 552 | # Ensure the data directory exists 553 | os.makedirs(os.path.dirname(emails_file_path), exist_ok=True) 554 | 555 | # Check if emails.txt exists and has content 556 | try: 557 | if not os.path.exists(emails_file_path): 558 | with open(emails_file_path, "w") as f: 559 | pass 560 | logging.warning(getTranslation("empty_email_file_created").format(emails_file_path)) 561 | 562 | with open(emails_file_path, "r") as f: 563 | lines = f.readlines() 564 | 565 | if not lines: 566 | logging.warning(getTranslation("email_list_empty")) 567 | sys.exit(1) 568 | 569 | first_email = lines[0].strip() 570 | self.generated_email = first_email 571 | 572 | # Write remaining emails back to file 573 | with open(emails_file_path, "w") as f: 574 | f.writelines(lines[1:]) 575 | 576 | return self.generated_email 577 | except Exception as e: 578 | logging.error(getTranslation("email_file_read_error").format(str(e))) 579 | logging.warning(getTranslation("email_list_empty")) 580 | sys.exit(1) 581 | 582 | def delete_generated_email(self): 583 | """ 584 | Delete the generated iCloud email if delete_after_use is enabled 585 | 586 | Returns: 587 | bool: True if deletion was successful or not needed, False otherwise 588 | """ 589 | if not self.use_icloud or not self.delete_after_use or not self.generated_email: 590 | return True 591 | 592 | if not self.deleteIcloudEmail: 593 | logging.warning(getTranslation("delete_email_not_available")) 594 | return False 595 | 596 | try: 597 | logging.info(getTranslation("deleting_generated_email").format(self.generated_email)) 598 | results = self.deleteIcloudEmail(self.generated_email) 599 | 600 | if results and len(results) > 0: 601 | email, success, message = results[0] 602 | if success: 603 | logging.info(message) 604 | return True 605 | else: 606 | logging.error(message) 607 | return False 608 | else: 609 | logging.error(getTranslation("delete_email_no_result")) 610 | return False 611 | except Exception as e: 612 | logging.error(getTranslation("delete_email_exception").format(str(e))) 613 | return False 614 | 615 | def get_account_info(self): 616 | """获取完整的账号信息""" 617 | return { 618 | "email": self.generate_email(), 619 | "password": self.default_password, 620 | "first_name": self.default_first_name, 621 | "last_name": self.default_last_name, 622 | } 623 | 624 | 625 | def get_user_agent(): 626 | """获取user_agent""" 627 | try: 628 | # 使用JavaScript获取user agent 629 | browser_manager = BrowserManager() 630 | browser = browser_manager.init_browser() 631 | user_agent = browser.latest_tab.run_js("return navigator.userAgent") 632 | browser_manager.quit() 633 | return user_agent 634 | except Exception as e: 635 | logging.error(getTranslation("get_user_agent_failed").format(str(e))) 636 | return None 637 | 638 | 639 | def check_cursor_version(): 640 | """检查cursor版本""" 641 | pkg_path, main_path = patch_cursor_get_machine_id.get_cursor_paths() 642 | with open(pkg_path, "r", encoding="utf-8") as f: 643 | version = json.load(f)["version"] 644 | return patch_cursor_get_machine_id.version_check(version, min_version="0.45.0") 645 | 646 | 647 | def reset_machine_id(greater_than_0_45): 648 | if greater_than_0_45: 649 | # 提示请手动执行脚本 https://github.com/Ryan0204/cursor-auto-icloud/blob/main/patch_cursor_get_machine_id.py 650 | go_cursor_help.go_cursor_help() 651 | else: 652 | MachineIDResetter().reset_machine_ids() 653 | 654 | 655 | def print_end_message(): 656 | logging.info(getTranslation("operation_complete")) 657 | 658 | 659 | def save_account_to_csv(account_info, csv_path="accounts.csv"): 660 | """ 661 | Save account information to a CSV file. 662 | 663 | Args: 664 | account_info: Dictionary containing account details 665 | csv_path: Path to the CSV file 666 | """ 667 | # Check for CSV_FILE_PATH in environment variables 668 | env_csv_path = os.environ.get("CSV_FILE_PATH") 669 | if env_csv_path: 670 | csv_path = env_csv_path 671 | else: 672 | # Try to save accounts.csv in the same directory as .env 673 | env_dir = get_env_directory() 674 | csv_path = os.path.join(env_dir, "accounts.csv") 675 | 676 | file_path = Path(csv_path) 677 | logging.info(getTranslation("saving_account_to_csv").format(file_path)) 678 | 679 | # Check if file exists to determine if we need to write headers 680 | file_exists = file_path.exists() 681 | 682 | try: 683 | # Ensure the directory exists 684 | os.makedirs(os.path.dirname(os.path.abspath(csv_path)), exist_ok=True) 685 | 686 | with open(file_path, mode='a', newline='') as file: 687 | fieldnames = ['created_date', 'email', 'password', 'access_token', 'refresh_token', 'first_name', 'last_name'] 688 | writer = csv.DictWriter(file, fieldnames=fieldnames) 689 | 690 | # Write headers if file doesn't exist 691 | if not file_exists: 692 | writer.writeheader() 693 | 694 | # Add creation date to account info 695 | account_info_with_date = account_info.copy() 696 | account_info_with_date['created_date'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 697 | 698 | # Write account info 699 | writer.writerow(account_info_with_date) 700 | 701 | logging.info(getTranslation("account_saved_to_csv").format(csv_path)) 702 | return True 703 | except Exception as e: 704 | logging.error(getTranslation("save_account_failed").format(str(e))) 705 | return False 706 | 707 | 708 | def main(): 709 | """Main function for the Cursor Pro Keep Alive application.""" 710 | greater_than_0_45 = check_cursor_version() 711 | browser_manager = None 712 | 713 | # Initialize the language manager 714 | lang_manager = LanguageManager() 715 | 716 | # Define URLs used in the program 717 | login_url = "https://authenticator.cursor.sh" 718 | sign_up_url = "https://authenticator.cursor.sh/sign-up" 719 | settings_url = "https://www.cursor.com/settings" 720 | 721 | custom_email = None 722 | 723 | try: 724 | logging.info(getTranslation("program_init")) 725 | ExitCursor() 726 | 727 | # Main menu loop to handle language switching 728 | while True: 729 | # Using the new getTranslation function for more readable code 730 | print(getTranslation("select_operation_mode")) 731 | print(getTranslation("reset_machine_code_only")) 732 | print(getTranslation("complete_registration")) 733 | print(getTranslation("generate_icloud_email")) 734 | print(getTranslation("complete_registration_icloud")) 735 | print(getTranslation("select_language")) 736 | 737 | while True: 738 | try: 739 | # Using the original _ function for comparison 740 | choice = int(input(_("enter_option")).strip()) 741 | if choice in [1, 2, 3, 4, 5]: 742 | break 743 | else: 744 | print(_("invalid_option")) 745 | except ValueError: 746 | print(_("enter_valid_number")) 747 | 748 | if choice == 5: 749 | # Switch language 750 | lang_manager.select_language() 751 | continue # Return to the main menu with new language 752 | else: 753 | break # Exit the menu loop and proceed with the selected option 754 | 755 | # Set delete_icloud_email_after_use based on user choice if using iCloud 756 | delete_icloud_email_after_use = False 757 | manual_code = False 758 | if choice == 4: # If using iCloud email 759 | # Ask user if they want to delete the email after use 760 | delete_prompt = input(getTranslation("delete_email_prompt") + " (Y/N) [Y]: ").strip().upper() 761 | # Default is Yes (empty or Y) 762 | delete_icloud_email_after_use = delete_prompt != "N" 763 | if delete_icloud_email_after_use: 764 | logging.info(getTranslation("delete_after_use_enabled")) 765 | else: 766 | logging.info(getTranslation("delete_after_use_disabled")) 767 | 768 | if choice == 1: 769 | # 仅执行重置机器码 770 | reset_machine_id(greater_than_0_45) 771 | logging.info(getTranslation("reset_complete")) 772 | print_end_message() 773 | sys.exit(0) 774 | 775 | elif choice == 2: 776 | custom_email = input(getTranslation("custom_email")) 777 | manual_code_prompt = input(getTranslation("manual_code_prompt") + " (Y/N) [Y]: ").strip().upper() 778 | manual_code = manual_code_prompt != "N" 779 | 780 | elif choice == 3: 781 | # 生成 iCloud 隐藏邮箱 782 | try: 783 | count = int(input(getTranslation("enter_email_count")).strip()) 784 | if count <= 0: 785 | logging.error(getTranslation("email_count_gt_zero")) 786 | sys.exit(1) 787 | 788 | # Import the iCloud email generator 789 | try: 790 | # Try direct import first 791 | from src.icloud.generateEmail import generateIcloudEmail 792 | emails = generateIcloudEmail(count) 793 | except ImportError: 794 | try: 795 | # Try with modified path as fallback 796 | current_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 797 | if current_dir not in sys.path: 798 | sys.path.append(current_dir) 799 | from icloud.generateEmail import generateIcloudEmail 800 | emails = generateIcloudEmail(count) 801 | except ImportError: 802 | logging.error(getTranslation("icloud_module_import_failed")) 803 | print(getTranslation("install_dependencies")) 804 | print_end_message() 805 | sys.exit(1) 806 | 807 | if emails: 808 | print(getTranslation("generated_emails").format(len(emails))) 809 | for email in emails: 810 | print(email) 811 | else: 812 | print(getTranslation("no_emails_generated")) 813 | except ValueError: 814 | logging.error(getTranslation("invalid_count")) 815 | sys.exit(1) 816 | 817 | logging.info(getTranslation("initializing_browser")) 818 | 819 | # 获取user_agent 820 | user_agent = get_user_agent() 821 | if not user_agent: 822 | logging.error(getTranslation("getting_user_agent_failed")) 823 | user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" 824 | 825 | # 剔除user_agent中的"HeadlessChrome" 826 | user_agent = user_agent.replace("HeadlessChrome", "Chrome") 827 | 828 | browser_manager = BrowserManager() 829 | browser = browser_manager.init_browser(user_agent) 830 | 831 | # 获取并打印浏览器的user-agent 832 | user_agent = browser.latest_tab.run_js("return navigator.userAgent") 833 | 834 | logging.info(getTranslation("visit_project_for_info")) 835 | logging.info(getTranslation("config_info")) 836 | 837 | logging.info(getTranslation("generating_random_account")) 838 | 839 | # 使用 iCloud 隐藏邮箱 if choice is 4 840 | use_icloud = (choice == 4) 841 | email_generator = EmailGenerator(use_icloud=use_icloud, delete_after_use=delete_icloud_email_after_use) 842 | first_name = email_generator.default_first_name 843 | last_name = email_generator.default_last_name 844 | if choice == 2: 845 | account = custom_email 846 | else: 847 | account = email_generator.generate_email() 848 | password = email_generator.default_password 849 | 850 | logging.info(getTranslation("generated_email_account").format(account)) 851 | 852 | logging.info(getTranslation("initializing_email_verification")) 853 | email_handler = EmailVerificationHandler(account) 854 | 855 | auto_update_cursor_auth = True 856 | 857 | tab = browser.latest_tab 858 | 859 | tab.run_js("try { turnstile.reset() } catch(e) { }") 860 | 861 | logging.info(getTranslation("start_registration")) 862 | logging.info(getTranslation("visiting_login_page").format(login_url)) 863 | tab.get(login_url) 864 | 865 | if sign_up_account(browser, tab, sign_up_url, settings_url, first_name, last_name, account, password, email_handler, manual_code): 866 | logging.info(getTranslation("getting_session_token")) 867 | access_token, refresh_token = get_cursor_session_token(tab) 868 | if access_token and refresh_token: 869 | account_info = { 870 | 'email': account, 871 | 'password': password, 872 | 'access_token': access_token, 873 | 'refresh_token': refresh_token, 874 | 'first_name': first_name, 875 | 'last_name': last_name 876 | } 877 | save_account_to_csv(account_info) 878 | logging.info(getTranslation("updating_auth_info")) 879 | update_cursor_auth( 880 | email=account, access_token=access_token, refresh_token=refresh_token 881 | ) 882 | 883 | # Delete iCloud email if option is enabled 884 | if use_icloud and delete_icloud_email_after_use: 885 | logging.info(getTranslation("deleting_icloud_email_after_use")) 886 | email_generator.delete_generated_email() 887 | 888 | logging.info(getTranslation("visit_project_for_info")) 889 | logging.info(getTranslation("resetting_machine_code")) 890 | reset_machine_id(greater_than_0_45) 891 | logging.info(getTranslation("all_operations_complete")) 892 | print_end_message() 893 | else: 894 | logging.error(getTranslation("session_token_failed")) 895 | 896 | except Exception as e: 897 | logging.error(getTranslation("program_execution_error").format(str(e))) 898 | import traceback 899 | 900 | logging.error(traceback.format_exc()) 901 | finally: 902 | if browser_manager: 903 | browser_manager.quit() 904 | input(getTranslation("program_complete")) 905 | 906 | 907 | # If this script is run directly, execute the main function 908 | if __name__ == "__main__": 909 | main() 910 | -------------------------------------------------------------------------------- /src/utils/language.py: -------------------------------------------------------------------------------- 1 | import os 2 | from enum import Enum 3 | from typing import Dict 4 | 5 | class Language(Enum): 6 | """Language enum for supported languages""" 7 | ENGLISH = "en" 8 | CHINESE = "zh" 9 | 10 | class LanguageManager: 11 | """Manages language translations and settings for the application""" 12 | 13 | _instance = None 14 | 15 | def __new__(cls): 16 | """Singleton pattern to ensure only one instance of LanguageManager exists""" 17 | if cls._instance is None: 18 | cls._instance = super(LanguageManager, cls).__new__(cls) 19 | cls._instance._initialized = False 20 | return cls._instance 21 | 22 | def __init__(self): 23 | """Initialize the language manager with translations""" 24 | if self._initialized: 25 | return 26 | 27 | # Set default language (can be overridden by env var or user selection) 28 | env_lang = os.environ.get("LANGUAGE", "").lower() 29 | if env_lang == "en" or env_lang == "english": 30 | self.current_language = Language.ENGLISH 31 | elif env_lang == "zh" or env_lang == "chinese": 32 | self.current_language = Language.CHINESE 33 | else: 34 | self.current_language = Language.CHINESE # Default to Chinese for backward compatibility 35 | 36 | self._translations = self._load_translations() 37 | self._initialized = True 38 | 39 | def _load_translations(self) -> Dict[str, Dict[str, str]]: 40 | """Load all translations for the application""" 41 | return { 42 | # Main UI messages 43 | "program_init": { 44 | Language.ENGLISH: "\n=== Initializing Program ===", 45 | Language.CHINESE: "\n=== 初始化程序 ===" 46 | }, 47 | "select_operation_mode": { 48 | Language.ENGLISH: "\nPlease select operation mode:", 49 | Language.CHINESE: "\n请选择操作模式:" 50 | }, 51 | "reset_machine_code_only": { 52 | Language.ENGLISH: "1. Reset machine code only", 53 | Language.CHINESE: "1. 仅重置机器码" 54 | }, 55 | "complete_registration": { 56 | Language.ENGLISH: "2. Register using an existing iCloud email", 57 | Language.CHINESE: "2. 已有 iCloud 邮箱注册" 58 | }, 59 | "generate_icloud_email": { 60 | Language.ENGLISH: "3. Generate iCloud hidden email", 61 | Language.CHINESE: "3. 生成 iCloud 隐藏邮箱" 62 | }, 63 | "complete_registration_icloud": { 64 | Language.ENGLISH: "4. Complete registration process (using iCloud hidden email)", 65 | Language.CHINESE: "4. 完整注册流程(使用 iCloud 隐藏邮箱)" 66 | }, 67 | "select_language": { 68 | Language.ENGLISH: "5. Switch language (当前语言: English)", 69 | Language.CHINESE: "5. 切换语言 (Current language: 中文)" 70 | }, 71 | "enter_option": { 72 | Language.ENGLISH: "Please enter option (1-5): ", 73 | Language.CHINESE: "请输入选项 (1-5): " 74 | }, 75 | "invalid_option": { 76 | Language.ENGLISH: "Invalid option, please try again", 77 | Language.CHINESE: "无效的选项,请重新输入" 78 | }, 79 | "enter_valid_number": { 80 | Language.ENGLISH: "Please enter a valid number", 81 | Language.CHINESE: "请输入有效的数字" 82 | }, 83 | # Custom Email 84 | "custom_email":{ 85 | Language.ENGLISH: "Please input your iCloud email:", 86 | Language.CHINESE:"请输入 iCloud 邮箱:" 87 | }, 88 | # Machine code reset 89 | "reset_complete": { 90 | Language.ENGLISH: "Machine code reset complete", 91 | Language.CHINESE: "机器码重置完成" 92 | }, 93 | 94 | # iCloud email generation 95 | "enter_email_count": { 96 | Language.ENGLISH: "Please enter the number of emails to generate: ", 97 | Language.CHINESE: "请输入要生成的邮箱数量: " 98 | }, 99 | "email_count_gt_zero": { 100 | Language.ENGLISH: "Email count must be greater than 0", 101 | Language.CHINESE: "邮箱数量必须大于0" 102 | }, 103 | "icloud_module_import_failed": { 104 | Language.ENGLISH: "Failed to import iCloud email generation module", 105 | Language.CHINESE: "导入 iCloud 邮箱生成模块失败" 106 | }, 107 | "install_dependencies": { 108 | Language.ENGLISH: "Cannot import iCloud email generation module, please make sure all dependencies are installed", 109 | Language.CHINESE: "无法导入 iCloud 邮箱生成模块,请确保安装了所有依赖" 110 | }, 111 | "generated_emails": { 112 | Language.ENGLISH: "Successfully generated {0} email addresses:", 113 | Language.CHINESE: "成功生成 {0} 个邮箱地址:" 114 | }, 115 | "no_emails_generated": { 116 | Language.ENGLISH: "No email addresses were generated", 117 | Language.CHINESE: "未生成任何邮箱地址" 118 | }, 119 | "invalid_count": { 120 | Language.ENGLISH: "Invalid count", 121 | Language.CHINESE: "无效的数量" 122 | }, 123 | 124 | # Browser initialization 125 | "initializing_browser": { 126 | Language.ENGLISH: "Initializing browser...", 127 | Language.CHINESE: "正在初始化浏览器..." 128 | }, 129 | "getting_user_agent_failed": { 130 | Language.ENGLISH: "Failed to get user agent, using default", 131 | Language.CHINESE: "获取user agent失败,使用默认值" 132 | }, 133 | 134 | # Configuration info 135 | "config_info": { 136 | Language.ENGLISH: "\n=== Configuration Info ===", 137 | Language.CHINESE: "\n=== 配置信息 ===" 138 | }, 139 | "generating_random_account": { 140 | Language.ENGLISH: "Generating random account information...", 141 | Language.CHINESE: "正在生成随机账号信息..." 142 | }, 143 | "generated_email_account": { 144 | Language.ENGLISH: "Generated email account: {0}", 145 | Language.CHINESE: "生成的邮箱账号: {0}" 146 | }, 147 | "initializing_email_verification": { 148 | Language.ENGLISH: "Initializing email verification module...", 149 | Language.CHINESE: "正在初始化邮箱验证模块..." 150 | }, 151 | 152 | # Registration process 153 | "start_registration": { 154 | Language.ENGLISH: "\n=== Starting Registration Process ===", 155 | Language.CHINESE: "\n=== 开始注册流程 ===" 156 | }, 157 | "visiting_login_page": { 158 | Language.ENGLISH: "Visiting login page: {0}", 159 | Language.CHINESE: "正在访问登录页面: {0}" 160 | }, 161 | "getting_session_token": { 162 | Language.ENGLISH: "Getting session token...", 163 | Language.CHINESE: "正在获取会话令牌..." 164 | }, 165 | "updating_auth_info": { 166 | Language.ENGLISH: "Updating authentication information...", 167 | Language.CHINESE: "更新认证信息..." 168 | }, 169 | "resetting_machine_code": { 170 | Language.ENGLISH: "Resetting machine code...", 171 | Language.CHINESE: "重置机器码..." 172 | }, 173 | "all_operations_complete": { 174 | Language.ENGLISH: "All operations complete", 175 | Language.CHINESE: "所有操作已完成" 176 | }, 177 | "session_token_failed": { 178 | Language.ENGLISH: "Failed to get session token, registration process incomplete", 179 | Language.CHINESE: "获取会话令牌失败,注册流程未完成" 180 | }, 181 | 182 | # Spam folder related translations 183 | "checking_spam_folders": { 184 | Language.ENGLISH: "Verification code not found in inbox, checking spam/junk folders...", 185 | Language.CHINESE: "收件箱中未找到验证码,正在检查垃圾邮件文件夹..." 186 | }, 187 | "icloud_folder_list_failed": { 188 | Language.ENGLISH: "Failed to retrieve folder list from iCloud", 189 | Language.CHINESE: "无法获取iCloud文件夹列表" 190 | }, 191 | "checking_folder": { 192 | Language.ENGLISH: "Checking folder: {0}", 193 | Language.CHINESE: "正在检查文件夹: {0}" 194 | }, 195 | "verification_code_found_in_spam": { 196 | Language.ENGLISH: "Verification code found in spam: {0} (folder: {1})", 197 | Language.CHINESE: "垃圾邮件文件夹中找到验证码: {0} (文件夹: {1})" 198 | }, 199 | "error_checking_folder": { 200 | Language.ENGLISH: "Error checking folder {0}: {1}", 201 | Language.CHINESE: "检查文件夹 {0} 时出错: {1}" 202 | }, 203 | "no_verification_code_in_spam": { 204 | Language.ENGLISH: "No verification code found in spam folders", 205 | Language.CHINESE: "垃圾邮件文件夹中未找到验证码" 206 | }, 207 | "spam_folder_check_failed": { 208 | Language.ENGLISH: "Spam folder check failed: {0}", 209 | Language.CHINESE: "垃圾邮件文件夹检查失败: {0}" 210 | }, 211 | 212 | # Sign-up process specific 213 | "filling_personal_info": { 214 | Language.ENGLISH: "Filling personal information...", 215 | Language.CHINESE: "正在填写个人信息..." 216 | }, 217 | "input_first_name": { 218 | Language.ENGLISH: "Entered first name: {0}", 219 | Language.CHINESE: "已输入名字: {0}" 220 | }, 221 | "input_last_name": { 222 | Language.ENGLISH: "Entered last name: {0}", 223 | Language.CHINESE: "已输入姓氏: {0}" 224 | }, 225 | "input_email": { 226 | Language.ENGLISH: "Entered email: {0}", 227 | Language.CHINESE: "已输入邮箱: {0}" 228 | }, 229 | "submit_personal_info": { 230 | Language.ENGLISH: "Submitting personal information...", 231 | Language.CHINESE: "提交个人信息..." 232 | }, 233 | "signup_page_access_failed": { 234 | Language.ENGLISH: "Failed to access signup page: {0}", 235 | Language.CHINESE: "注册页面访问失败: {0}" 236 | }, 237 | "setting_password": { 238 | Language.ENGLISH: "Setting password...", 239 | Language.CHINESE: "正在设置密码..." 240 | }, 241 | "submit_password": { 242 | Language.ENGLISH: "Submitting password...", 243 | Language.CHINESE: "提交密码..." 244 | }, 245 | "password_setup_complete": { 246 | Language.ENGLISH: "Password setup complete, waiting for system response...", 247 | Language.CHINESE: "密码设置完成,等待系统响应..." 248 | }, 249 | "password_setup_failed": { 250 | Language.ENGLISH: "Password setup failed: {0}", 251 | Language.CHINESE: "密码设置失败: {0}" 252 | }, 253 | "email_already_used": { 254 | Language.ENGLISH: "Registration failed: Email already in use", 255 | Language.CHINESE: "注册失败:邮箱已被使用" 256 | }, 257 | "registration_successful": { 258 | Language.ENGLISH: "Registration successful - entered account settings page", 259 | Language.CHINESE: "注册成功 - 已进入账户设置页面" 260 | }, 261 | "getting_verification_code": { 262 | Language.ENGLISH: "Getting email verification code...", 263 | Language.CHINESE: "正在获取邮箱验证码..." 264 | }, 265 | "verification_code_failed": { 266 | Language.ENGLISH: "Failed to get verification code", 267 | Language.CHINESE: "获取验证码失败" 268 | }, 269 | "verification_code_success": { 270 | Language.ENGLISH: "Successfully got verification code: {0}", 271 | Language.CHINESE: "成功获取验证码: {0}" 272 | }, 273 | "entering_verification_code": { 274 | Language.ENGLISH: "Entering verification code...", 275 | Language.CHINESE: "正在输入验证码..." 276 | }, 277 | "verification_code_complete": { 278 | Language.ENGLISH: "Verification code input complete", 279 | Language.CHINESE: "验证码输入完成" 280 | }, 281 | "verification_process_error": { 282 | Language.ENGLISH: "Verification process error: {0}", 283 | Language.CHINESE: "验证码处理过程出错: {0}" 284 | }, 285 | "waiting_for_processing": { 286 | Language.ENGLISH: "Waiting for system processing... {0} seconds remaining", 287 | Language.CHINESE: "等待系统处理中... 剩余 {0} 秒" 288 | }, 289 | "getting_account_info": { 290 | Language.ENGLISH: "Getting account information...", 291 | Language.CHINESE: "正在获取账户信息..." 292 | }, 293 | "account_usage_limit": { 294 | Language.ENGLISH: "Account usage limit: {0}", 295 | Language.CHINESE: "账户可用额度上限: {0}" 296 | }, 297 | "get_account_limit_failed": { 298 | Language.ENGLISH: "Failed to get account limit information: {0}", 299 | Language.CHINESE: "获取账户额度信息失败: {0}" 300 | }, 301 | "registration_complete": { 302 | Language.ENGLISH: "\n=== Registration Complete ===", 303 | Language.CHINESE: "\n=== 注册完成 ===" 304 | }, 305 | "cursor_account_info": { 306 | Language.ENGLISH: "Cursor Account Information:\nEmail: {0}\nPassword: {1}", 307 | Language.CHINESE: "Cursor 账号信息:\n邮箱: {0}\n密码: {1}" 308 | }, 309 | 310 | # End messages 311 | "program_execution_error": { 312 | Language.ENGLISH: "Error during program execution: {0}", 313 | Language.CHINESE: "程序执行过程中出错: {0}" 314 | }, 315 | "program_complete": { 316 | Language.ENGLISH: "Press Enter to exit...", 317 | Language.CHINESE: "按回车键退出..." 318 | }, 319 | "operation_complete": { 320 | Language.ENGLISH: "\n\n\n\n\n\n============================\nAll operations complete\n\n=== Get More Information ===\nPlease visit the open source project for more information: https://github.com/Ryan0204/cursor-auto-icloud", 321 | Language.CHINESE: "\n\n\n\n\n\n============================\n所有操作已完成\n\n=== 获取更多信息 ===\n请前往开源项目查看更多信息:https://github.com/Ryan0204/cursor-auto-icloud" 322 | }, 323 | 324 | # Language selection 325 | "select_new_language": { 326 | Language.ENGLISH: "\nSelect language / 选择语言:\n1. English\n2. 中文\nPlease enter option (1-2): ", 327 | Language.CHINESE: "\nSelect language / 选择语言:\n1. English\n2. 中文\n请输入选项 (1-2): " 328 | }, 329 | "language_switched": { 330 | Language.ENGLISH: "Language switched to English", 331 | Language.CHINESE: "语言已切换为中文" 332 | }, 333 | 334 | # Application main 335 | "application_starting": { 336 | Language.ENGLISH: "Starting Cursor Pro Keep Alive application...", 337 | Language.CHINESE: "正在启动 Cursor Pro Keep Alive 应用程序..." 338 | }, 339 | "application_error": { 340 | Language.ENGLISH: "An error occurred: {0}", 341 | Language.CHINESE: "发生错误: {0}" 342 | }, 343 | 344 | # Reset Machine 345 | "appdata_not_set": { 346 | Language.ENGLISH: "APPDATA environment variable is not set", 347 | Language.CHINESE: "APPDATA 环境变量未设置" 348 | }, 349 | "unsupported_os": { 350 | Language.ENGLISH: "Unsupported operating system: {0}", 351 | Language.CHINESE: "不支持的操作系统: {0}" 352 | }, 353 | "checking_config_file": { 354 | Language.ENGLISH: "Checking configuration file", 355 | Language.CHINESE: "正在检查配置文件" 356 | }, 357 | "config_file_not_exist": { 358 | Language.ENGLISH: "Configuration file does not exist", 359 | Language.CHINESE: "配置文件不存在" 360 | }, 361 | "config_file_no_permission": { 362 | Language.ENGLISH: "Cannot read/write configuration file, please check file permissions!", 363 | Language.CHINESE: "无法读写配置文件,请检查文件权限!" 364 | }, 365 | "go_cursor_help_warning": { 366 | Language.ENGLISH: "If you have used go-cursor-help to modify the ID; please modify the read-only permission of the file", 367 | Language.CHINESE: "如果你使用过 go-cursor-help 来修改 ID; 请修改文件只读权限" 368 | }, 369 | "reading_current_config": { 370 | Language.ENGLISH: "Reading current configuration", 371 | Language.CHINESE: "读取当前配置" 372 | }, 373 | "generating_new_machine_ids": { 374 | Language.ENGLISH: "Generating new machine identifiers", 375 | Language.CHINESE: "生成新的机器标识" 376 | }, 377 | "saving_new_config": { 378 | Language.ENGLISH: "Saving new configuration", 379 | Language.CHINESE: "保存新配置" 380 | }, 381 | "machine_id_reset_success": { 382 | Language.ENGLISH: "Machine identifier reset successful!", 383 | Language.CHINESE: "机器标识重置成功!" 384 | }, 385 | "new_machine_ids": { 386 | Language.ENGLISH: "New machine identifiers", 387 | Language.CHINESE: "新的机器标识" 388 | }, 389 | "permission_error": { 390 | Language.ENGLISH: "Permission error", 391 | Language.CHINESE: "权限错误" 392 | }, 393 | "run_as_admin_suggestion": { 394 | Language.ENGLISH: "Please try running this program as administrator", 395 | Language.CHINESE: "请尝试以管理员身份运行此程序" 396 | }, 397 | "reset_process_error": { 398 | Language.ENGLISH: "Reset process error", 399 | Language.CHINESE: "重置过程出错" 400 | }, 401 | "cursor_machine_id_reset_tool": { 402 | Language.ENGLISH: "Cursor Machine ID Reset Tool", 403 | Language.CHINESE: "Cursor 机器标识重置工具" 404 | }, 405 | "press_enter_exit": { 406 | Language.ENGLISH: "Press Enter to exit", 407 | Language.CHINESE: "按回车键退出" 408 | }, 409 | 410 | # Auth Manager 411 | "no_values_to_update": { 412 | Language.ENGLISH: "No values provided for update", 413 | Language.CHINESE: "没有提供任何要更新的值" 414 | }, 415 | "value_updated_success": { 416 | Language.ENGLISH: "Successfully updated {0}", 417 | Language.CHINESE: "成功更新 {0}" 418 | }, 419 | "value_not_found_or_unchanged": { 420 | Language.ENGLISH: "{0} not found or value unchanged", 421 | Language.CHINESE: "未找到 {0} 或值未变化" 422 | }, 423 | "database_error": { 424 | Language.ENGLISH: "Database error: {0}", 425 | Language.CHINESE: "数据库错误: {0}" 426 | }, 427 | "general_error": { 428 | Language.ENGLISH: "An error occurred: {0}", 429 | Language.CHINESE: "发生错误: {0}" 430 | }, 431 | 432 | # iCloud Email Generator 433 | "generate_email_failed": { 434 | Language.ENGLISH: "Failed to generate email: {0}", 435 | Language.CHINESE: "生成邮箱失败: {0}" 436 | }, 437 | "unknown_error": { 438 | Language.ENGLISH: "Unknown error", 439 | Language.CHINESE: "未知错误" 440 | }, 441 | "generate_email_failed_no_address": { 442 | Language.ENGLISH: "Failed to generate email: Unable to get email address", 443 | Language.CHINESE: "生成邮箱失败: 无法获取邮箱地址" 444 | }, 445 | "reserve_email_failed": { 446 | Language.ENGLISH: "Failed to reserve email: {0}", 447 | Language.CHINESE: "保留邮箱失败: {0}" 448 | }, 449 | "email_generated_success": { 450 | Language.ENGLISH: "Email {0} generated successfully", 451 | Language.CHINESE: "邮箱 {0} 生成成功" 452 | }, 453 | "generate_email_error": { 454 | Language.ENGLISH: "Error occurred during email generation: {0}", 455 | Language.CHINESE: "生成邮箱过程中发生错误: {0}" 456 | }, 457 | "icloud_cookies_not_configured": { 458 | Language.ENGLISH: "iCloud Cookies not configured, please set ICLOUD_COOKIES in the .env file", 459 | Language.CHINESE: "iCloud Cookies 未配置,请在 .env 文件中设置 ICLOUD_COOKIES" 460 | }, 461 | "start_generating_emails": { 462 | Language.ENGLISH: "Starting to generate {0} iCloud hidden emails...", 463 | Language.CHINESE: "开始生成 {0} 个 iCloud 隐藏邮箱..." 464 | }, 465 | "emails_generated_success": { 466 | Language.ENGLISH: "Successfully generated {0} email addresses", 467 | Language.CHINESE: "成功生成 {0} 个邮箱地址" 468 | }, 469 | "emails_saved_to_file": { 470 | Language.ENGLISH: "Email addresses have been saved to {0}", 471 | Language.CHINESE: "邮箱地址已保存到 {0}" 472 | }, 473 | "invalid_count_parameter": { 474 | Language.ENGLISH: "Invalid count parameter: {0}", 475 | Language.CHINESE: "无效的数量参数: {0}" 476 | }, 477 | 478 | # iCloud Hide My Email 479 | "generating_icloud_hidden_email": { 480 | Language.ENGLISH: "Generating iCloud hidden email...", 481 | Language.CHINESE: "正在生成 iCloud 隐藏邮箱..." 482 | }, 483 | "generate_email_timeout": { 484 | Language.ENGLISH: "Email generation timed out", 485 | Language.CHINESE: "生成邮箱超时" 486 | }, 487 | "request_timeout": { 488 | Language.ENGLISH: "Request timed out", 489 | Language.CHINESE: "请求超时" 490 | }, 491 | "reserving_email": { 492 | Language.ENGLISH: "Reserving email {0}...", 493 | Language.CHINESE: "正在保留邮箱 {0}..." 494 | }, 495 | "reserve_email_timeout": { 496 | Language.ENGLISH: "Email reservation timed out", 497 | Language.CHINESE: "保留邮箱超时" 498 | }, 499 | "getting_email_list": { 500 | Language.ENGLISH: "Getting email list...", 501 | Language.CHINESE: "正在获取邮箱列表..." 502 | }, 503 | "list_email_timeout": { 504 | Language.ENGLISH: "Getting email list timed out", 505 | Language.CHINESE: "获取邮箱列表超时" 506 | }, 507 | "list_email_failed": { 508 | Language.ENGLISH: "Failed to get email list: {0}", 509 | Language.CHINESE: "获取邮箱列表失败: {0}" 510 | }, 511 | "deleting_email": { 512 | Language.ENGLISH: "Deleting email {0}...", 513 | Language.CHINESE: "正在删除邮箱 {0}..." 514 | }, 515 | "delete_email_timeout": { 516 | Language.ENGLISH: "Deleting email timed out", 517 | Language.CHINESE: "删除邮箱超时" 518 | }, 519 | "delete_email_failed": { 520 | Language.ENGLISH: "Failed to delete email: {0}", 521 | Language.CHINESE: "删除邮箱失败: {0}" 522 | }, 523 | 524 | # go_cursor_help.py 525 | "current_operating_system": { 526 | Language.ENGLISH: "Current operating system: {0}", 527 | Language.CHINESE: "当前操作系统: {0}" 528 | }, 529 | "executing_macos_command": { 530 | Language.ENGLISH: "Executing macOS command", 531 | Language.CHINESE: "执行macOS命令" 532 | }, 533 | "executing_linux_command": { 534 | Language.ENGLISH: "Executing Linux command", 535 | Language.CHINESE: "执行Linux命令" 536 | }, 537 | "executing_windows_command": { 538 | Language.ENGLISH: "Executing Windows command", 539 | Language.CHINESE: "执行Windows命令" 540 | }, 541 | 542 | # exit_cursor.py 543 | "starting_cursor_exit": { 544 | Language.ENGLISH: "Starting to exit Cursor...", 545 | Language.CHINESE: "开始退出Cursor..." 546 | }, 547 | "no_cursor_processes_found": { 548 | Language.ENGLISH: "No running Cursor processes found", 549 | Language.CHINESE: "未发现运行中的 Cursor 进程" 550 | }, 551 | "all_cursor_processes_closed": { 552 | Language.ENGLISH: "All Cursor processes have been closed normally", 553 | Language.CHINESE: "所有 Cursor 进程已正常关闭" 554 | }, 555 | "processes_not_closed_in_time": { 556 | Language.ENGLISH: "The following processes did not close within the time limit: {0}", 557 | Language.CHINESE: "以下进程未能在规定时间内关闭: {0}" 558 | }, 559 | "error_closing_cursor": { 560 | Language.ENGLISH: "Error occurred while closing Cursor processes: {0}", 561 | Language.CHINESE: "关闭 Cursor 进程时发生错误: {0}" 562 | }, 563 | 564 | # patch_cursor_get_machine_id.py 565 | "cursor_path_not_found_linux": { 566 | Language.ENGLISH: "Cursor installation path not found on Linux system", 567 | Language.CHINESE: "在 Linux 系统上未找到 Cursor 安装路径" 568 | }, 569 | "cursor_path_not_default": { 570 | Language.ENGLISH: "Your Cursor installation is not in the default path, please create a symbolic link with the following command:", 571 | Language.CHINESE: "可能您的Cursor不是默认安装路径,请创建软连接,命令如下:" 572 | }, 573 | "create_symlink_command": { 574 | Language.ENGLISH: 'cmd /c mklink /d "C:\\Users\\\\AppData\\Local\\Programs\\Cursor" "default installation path"', 575 | Language.CHINESE: 'cmd /c mklink /d "C:\\Users\\\\AppData\\Local\\Programs\\Cursor" "默认安装路径"' 576 | }, 577 | "example_command": { 578 | Language.ENGLISH: "For example:", 579 | Language.CHINESE: "例如:" 580 | }, 581 | "example_command_path": { 582 | Language.ENGLISH: 'cmd /c mklink /d "C:\\Users\\\\AppData\\Local\\Programs\\Cursor" "D:\\SoftWare\\cursor"', 583 | Language.CHINESE: 'cmd /c mklink /d "C:\\Users\\\\AppData\\Local\\Programs\\Cursor" "D:\\SoftWare\\cursor"' 584 | }, 585 | "file_not_exist": { 586 | Language.ENGLISH: "File does not exist: {0}", 587 | Language.CHINESE: "文件不存在: {0}" 588 | }, 589 | "file_no_write_permission": { 590 | Language.ENGLISH: "No write permission for file: {0}", 591 | Language.CHINESE: "没有文件写入权限: {0}" 592 | }, 593 | "invalid_version_format": { 594 | Language.ENGLISH: "Invalid version format: {0}", 595 | Language.CHINESE: "无效的版本号格式: {0}" 596 | }, 597 | "version_below_minimum": { 598 | Language.ENGLISH: "Version {0} is below the minimum requirement {1}", 599 | Language.CHINESE: "版本号 {0} 小于最小要求 {1}" 600 | }, 601 | "version_above_maximum": { 602 | Language.ENGLISH: "Version {0} is above the maximum requirement {1}", 603 | Language.CHINESE: "版本号 {0} 大于最大要求 {1}" 604 | }, 605 | "version_check_failed": { 606 | Language.ENGLISH: "Version check failed: {0}", 607 | Language.CHINESE: "版本检查失败: {0}" 608 | }, 609 | "file_modified_success": { 610 | Language.ENGLISH: "File modified successfully", 611 | Language.CHINESE: "文件修改成功" 612 | }, 613 | "file_modification_error": { 614 | Language.ENGLISH: "Error while modifying file: {0}", 615 | Language.CHINESE: "修改文件时发生错误: {0}" 616 | }, 617 | "mainjs_backup_created": { 618 | Language.ENGLISH: "main.js backed up: {0}", 619 | Language.CHINESE: "已备份 main.js: {0}" 620 | }, 621 | "backup_failed": { 622 | Language.ENGLISH: "File backup failed: {0}", 623 | Language.CHINESE: "备份文件失败: {0}" 624 | }, 625 | "mainjs_restored": { 626 | Language.ENGLISH: "main.js has been restored", 627 | Language.CHINESE: "已恢复 main.js" 628 | }, 629 | "backup_not_found": { 630 | Language.ENGLISH: "Backup file not found", 631 | Language.CHINESE: "未找到备份文件" 632 | }, 633 | "restore_backup_failed": { 634 | Language.ENGLISH: "Failed to restore backup: {0}", 635 | Language.CHINESE: "恢复备份失败: {0}" 636 | }, 637 | "script_execution_started": { 638 | Language.ENGLISH: "Script execution started...", 639 | Language.CHINESE: "开始执行脚本..." 640 | }, 641 | "backup_restore_complete": { 642 | Language.ENGLISH: "Backup restoration complete", 643 | Language.CHINESE: "备份恢复完成" 644 | }, 645 | "backup_restore_failed": { 646 | Language.ENGLISH: "Backup restoration failed", 647 | Language.CHINESE: "备份恢复失败" 648 | }, 649 | "current_cursor_version": { 650 | Language.ENGLISH: "Current Cursor version: {0}", 651 | Language.CHINESE: "当前 Cursor 版本: {0}" 652 | }, 653 | "reading_version_failed": { 654 | Language.ENGLISH: "Failed to read version: {0}", 655 | Language.CHINESE: "无法读取版本号: {0}" 656 | }, 657 | "version_not_supported": { 658 | Language.ENGLISH: "Version not supported (requires >= 0.45.x)", 659 | Language.CHINESE: "版本不符合要求(需 >= 0.45.x)" 660 | }, 661 | "version_check_passed": { 662 | Language.ENGLISH: "Version check passed, preparing to modify files", 663 | Language.CHINESE: "版本检查通过,准备修改文件" 664 | }, 665 | "backup_failed_abort": { 666 | Language.ENGLISH: "File backup failed, aborting operation", 667 | Language.CHINESE: "文件备份失败,终止操作" 668 | }, 669 | "script_execution_complete": { 670 | Language.ENGLISH: "Script execution complete", 671 | Language.CHINESE: "脚本执行完成" 672 | }, 673 | "execution_error": { 674 | Language.ENGLISH: "Error during execution: {0}", 675 | Language.CHINESE: "执行过程中发生错误: {0}" 676 | }, 677 | "press_enter_exit": { 678 | Language.ENGLISH: "\nExecution complete, press Enter to exit...", 679 | Language.CHINESE: "\n程序执行完毕,按回车键退出..." 680 | }, 681 | 682 | # New translation keys for config.py 683 | "env_file_not_exist": { 684 | Language.ENGLISH: "File {0} does not exist", 685 | Language.CHINESE: "文件 {0} 不存在" 686 | }, 687 | "icloud_email": { 688 | Language.ENGLISH: "iCloud Email", 689 | Language.CHINESE: "iCloud 邮箱" 690 | }, 691 | "icloud_app_password": { 692 | Language.ENGLISH: "iCloud App Password", 693 | Language.CHINESE: "iCloud 应用专用密码" 694 | }, 695 | "config_not_set": { 696 | Language.ENGLISH: "{name} not configured, please set {key} in the .env file", 697 | Language.CHINESE: "{name}未配置,请在 .env 文件中设置 {key}" 698 | }, 699 | "icloud_email_info": { 700 | Language.ENGLISH: "iCloud Email: {0}@icloud.com", 701 | Language.CHINESE: "iCloud 邮箱: {0}@icloud.com" 702 | }, 703 | "icloud_password_info": { 704 | Language.ENGLISH: "iCloud App Password: {0}", 705 | Language.CHINESE: "iCloud 应用专用密码: {0}" 706 | }, 707 | "env_loaded_success": { 708 | Language.ENGLISH: "Environment variables loaded successfully!", 709 | Language.CHINESE: "环境变量加载成功!" 710 | }, 711 | "error_message": { 712 | Language.ENGLISH: "Error: {0}", 713 | Language.CHINESE: "错误: {0}" 714 | }, 715 | "reading_version_failed": { 716 | Language.ENGLISH: "Failed to read version: {0}", 717 | Language.CHINESE: "无法读取版本号: {0}" 718 | }, 719 | # New translation keys for cursor_pro_keep_alive.py 720 | "screenshot_saved": { 721 | Language.ENGLISH: "Screenshot saved: {0}", 722 | Language.CHINESE: "截图已保存: {0}" 723 | }, 724 | "screenshot_save_failed": { 725 | Language.ENGLISH: "Failed to save screenshot: {0}", 726 | Language.CHINESE: "截图保存失败: {0}" 727 | }, 728 | "verification_success_page": { 729 | Language.ENGLISH: "Verification success - Reached {0} page", 730 | Language.CHINESE: "验证成功 - 已到达{0}页面" 731 | }, 732 | "detecting_turnstile": { 733 | Language.ENGLISH: "Detecting Turnstile verification...", 734 | Language.CHINESE: "正在检测 Turnstile 验证..." 735 | }, 736 | "verification_attempt": { 737 | Language.ENGLISH: "Verification attempt {0}", 738 | Language.CHINESE: "第 {0} 次尝试验证" 739 | }, 740 | "turnstile_detected": { 741 | Language.ENGLISH: "Turnstile verification detected, processing...", 742 | Language.CHINESE: "检测到 Turnstile 验证框,开始处理..." 743 | }, 744 | "turnstile_passed": { 745 | Language.ENGLISH: "Turnstile verification passed", 746 | Language.CHINESE: "Turnstile 验证通过" 747 | }, 748 | "attempt_failed": { 749 | Language.ENGLISH: "Current attempt failed: {0}", 750 | Language.CHINESE: "当前尝试未成功: {0}" 751 | }, 752 | "verification_max_retries_reached": { 753 | Language.ENGLISH: "Verification failed - Maximum retries reached: {0}", 754 | Language.CHINESE: "验证失败 - 已达到最大重试次数 {0}" 755 | }, 756 | "visit_project_for_info": { 757 | Language.ENGLISH: "Please visit the open source project for more information: https://github.com/Ryan0204/cursor-auto-icloud", 758 | Language.CHINESE: "请前往开源项目查看更多信息:https://github.com/Ryan0204/cursor-auto-icloud" 759 | }, 760 | "turnstile_exception": { 761 | Language.ENGLISH: "Turnstile verification process exception: {0}", 762 | Language.CHINESE: "Turnstile 验证过程发生异常: {0}" 763 | }, 764 | "getting_cookies": { 765 | Language.ENGLISH: "Getting cookies", 766 | Language.CHINESE: "开始获取cookie" 767 | }, 768 | "token_attempt_failed": { 769 | Language.ENGLISH: "Attempt {0} failed to get CursorSessionToken, retrying in {1} seconds...", 770 | Language.CHINESE: "第 {0} 次尝试未获取到CursorSessionToken,{1}秒后重试..." 771 | }, 772 | "token_max_attempts": { 773 | Language.ENGLISH: "Maximum attempts reached ({0}), failed to get CursorSessionToken", 774 | Language.CHINESE: "已达到最大尝试次数({0}),获取CursorSessionToken失败" 775 | }, 776 | "get_cookie_failed": { 777 | Language.ENGLISH: "Failed to get cookie: {0}", 778 | Language.CHINESE: "获取cookie失败: {0}" 779 | }, 780 | "retry_in_seconds": { 781 | Language.ENGLISH: "Will retry in {0} seconds...", 782 | Language.CHINESE: "将在 {0} 秒后重试..." 783 | }, 784 | "env_file_load_failed": { 785 | Language.ENGLISH: "Failed to load .env file: {0}", 786 | Language.CHINESE: "加载 .env 文件失败: {0}" 787 | }, 788 | "icloud_feature_enabled": { 789 | Language.ENGLISH: "iCloud hidden email feature enabled", 790 | Language.CHINESE: "已启用 iCloud 隐藏邮箱功能" 791 | }, 792 | "icloud_module_import_failed_local": { 793 | Language.ENGLISH: "Failed to import iCloud email module, will use local email list", 794 | Language.CHINESE: "导入 iCloud 邮箱生成模块失败,将使用本地邮箱列表" 795 | }, 796 | "names_dataset_loaded": { 797 | Language.ENGLISH: "Names dataset loaded from {0}", 798 | Language.CHINESE: "名称数据集已从 {0} 加载" 799 | }, 800 | "names_dataset_not_found": { 801 | Language.ENGLISH: "Names dataset file not found in any known location", 802 | Language.CHINESE: "未在任何已知位置找到名称数据集文件" 803 | }, 804 | "icloud_email_gen_failed": { 805 | Language.ENGLISH: "iCloud email generation failed, will use local email list", 806 | Language.CHINESE: "iCloud 邮箱生成失败,将使用本地邮箱列表" 807 | }, 808 | "icloud_email_gen_error": { 809 | Language.ENGLISH: "iCloud email generation error: {0}", 810 | Language.CHINESE: "iCloud 邮箱生成失败: {0}" 811 | }, 812 | "using_local_email_list": { 813 | Language.ENGLISH: "Using local email list", 814 | Language.CHINESE: "将使用本地邮箱列表" 815 | }, 816 | "empty_email_file_created": { 817 | Language.ENGLISH: "Created empty email list file at {0}", 818 | Language.CHINESE: "已在 {0} 创建空的邮箱列表文件" 819 | }, 820 | "email_list_empty": { 821 | Language.ENGLISH: "Email list is empty, program execution completed", 822 | Language.CHINESE: "邮箱列表为空,程序执行完毕" 823 | }, 824 | "email_file_read_error": { 825 | Language.ENGLISH: "Error reading email file: {0}", 826 | Language.CHINESE: "读取邮箱文件时出错: {0}" 827 | }, 828 | "get_user_agent_failed": { 829 | Language.ENGLISH: "Failed to get user agent: {0}", 830 | Language.CHINESE: "获取user agent失败: {0}" 831 | }, 832 | "saving_account_to_csv": { 833 | Language.ENGLISH: "Saving account information to CSV file: {0}", 834 | Language.CHINESE: "正在保存账号信息到CSV文件: {0}" 835 | }, 836 | "account_saved_to_csv": { 837 | Language.ENGLISH: "Account information saved to {0}", 838 | Language.CHINESE: "账号信息已保存至 {0}" 839 | }, 840 | "save_account_failed": { 841 | Language.ENGLISH: "Failed to save account information: {0}", 842 | Language.CHINESE: "保存账号信息失败: {0}" 843 | }, 844 | # New translation keys for get_email_code.py 845 | "verification_code_attempt": { 846 | Language.ENGLISH: "Attempting to get verification code (attempt {0}/{1})...", 847 | Language.CHINESE: "尝试获取验证码 (第 {0}/{1} 次)..." 848 | }, 849 | "verification_code_not_found_retry": { 850 | Language.ENGLISH: "Verification code not found, retrying in {0} seconds...", 851 | Language.CHINESE: "未获取到验证码,{0} 秒后重试..." 852 | }, 853 | "verification_code_fetch_failed": { 854 | Language.ENGLISH: "Failed to get verification code: {0}", 855 | Language.CHINESE: "获取验证码失败: {0}" 856 | }, 857 | "error_will_retry": { 858 | Language.ENGLISH: "An error occurred, will retry in {0} seconds...", 859 | Language.CHINESE: "发生错误,{0} 秒后重试..." 860 | }, 861 | "max_retries_reached_with_error": { 862 | Language.ENGLISH: "Failed to get verification code and reached maximum retries: {0}", 863 | Language.CHINESE: "获取验证码失败且已达最大重试次数: {0}" 864 | }, 865 | "verification_code_not_found_after_attempts": { 866 | Language.ENGLISH: "Verification code not found after {0} attempts.", 867 | Language.CHINESE: "经过 {0} 次尝试后仍未获取到验证码。" 868 | }, 869 | "using_icloud_imap": { 870 | Language.ENGLISH: "Using iCloud IMAP to get email...", 871 | Language.CHINESE: "使用 iCloud IMAP 获取邮件..." 872 | }, 873 | "verification_code_timeout": { 874 | Language.ENGLISH: "Verification code retrieval timed out", 875 | Language.CHINESE: "获取验证码超时" 876 | }, 877 | "icloud_email_list_failed": { 878 | Language.ENGLISH: "Failed to get iCloud email list: {0}", 879 | Language.CHINESE: "获取 iCloud 邮件列表失败: {0}" 880 | }, 881 | "no_emails_in_icloud": { 882 | Language.ENGLISH: "No emails found in iCloud folder: {0}", 883 | Language.CHINESE: "在iCloud文件夹中没有找到邮件: {0}" 884 | }, 885 | "icloud_imap_fetch_failed": { 886 | Language.ENGLISH: "iCloud IMAP fetch failed: {0}", 887 | Language.CHINESE: "iCloud IMAP fetch failed: {0}" 888 | }, 889 | "icloud_imap_fetch_status_failed": { 890 | Language.ENGLISH: "iCloud IMAP fetch failed with status: {0}", 891 | Language.CHINESE: "iCloud IMAP fetch failed with status: {0}" 892 | }, 893 | "verification_code_found_in_email": { 894 | Language.ENGLISH: "Verification code found in iCloud email: {0}", 895 | Language.CHINESE: "从 iCloud 邮件中找到验证码: {0}" 896 | }, 897 | "no_verification_code_in_email": { 898 | Language.ENGLISH: "No verification code found in iCloud emails", 899 | Language.CHINESE: "在 iCloud 邮件中未找到验证码" 900 | }, 901 | "icloud_imap_operation_failed": { 902 | Language.ENGLISH: "iCloud IMAP operation failed: {0}", 903 | Language.CHINESE: "iCloud IMAP 操作失败: {0}" 904 | }, 905 | "email_body_decode_failed": { 906 | Language.ENGLISH: "Failed to decode email body: {0}", 907 | Language.CHINESE: "解码邮件正文失败: {0}" 908 | }, 909 | "none_type_attribute_error": { 910 | Language.ENGLISH: "INBOX is empty, checking other folders", 911 | Language.CHINESE: "INBOX是空的,现在检查其他文件夹" 912 | }, 913 | # New IMAP error handling translations 914 | "checking_imap_folder": { 915 | Language.ENGLISH: "Checking IMAP folder: {0}", 916 | Language.CHINESE: "正在检查IMAP文件夹: {0}" 917 | }, 918 | "empty_messages_response": { 919 | Language.ENGLISH: "IMAP server returned empty messages response", 920 | Language.CHINESE: "IMAP服务器返回了空的邮件响应" 921 | }, 922 | "empty_first_message_item": { 923 | Language.ENGLISH: "IMAP server returned empty first message item", 924 | Language.CHINESE: "IMAP服务器返回了空的第一条邮件项" 925 | }, 926 | "unexpected_message_type": { 927 | Language.ENGLISH: "Unexpected message type from IMAP server: {0}", 928 | Language.CHINESE: "IMAP服务器返回了意外的邮件类型: {0}" 929 | }, 930 | "inbox_empty_error": { 931 | Language.ENGLISH: "Inbox is empty - no emails in folder: {0}", 932 | Language.CHINESE: "收件箱是空的 - 文件夹中没有邮件: {0}" 933 | }, 934 | # Token refresh translations 935 | "token_refresh_success": { 936 | Language.ENGLISH: "Successfully refreshed token", 937 | Language.CHINESE: "成功刷新令牌" 938 | }, 939 | "token_refresh_failed": { 940 | Language.ENGLISH: "Failed to refresh token: {0}", 941 | Language.CHINESE: "刷新令牌失败: {0}" 942 | }, 943 | "token_refresh_exception": { 944 | Language.ENGLISH: "Exception during token refresh: {0}", 945 | Language.CHINESE: "刷新令牌过程中发生异常: {0}" 946 | }, 947 | # New translations for get_cursor_session_token function 948 | "start_getting_session_token": { 949 | Language.ENGLISH: "Starting to get session token", 950 | Language.CHINESE: "开始获取会话令牌" 951 | }, 952 | "try_deep_login": { 953 | Language.ENGLISH: "Trying to get token using deep login method", 954 | Language.CHINESE: "尝试使用深度登录方式获取token" 955 | }, 956 | "visiting_deep_login_url": { 957 | Language.ENGLISH: "Visiting deep login URL: {0}", 958 | Language.CHINESE: "访问深度登录URL: {0}" 959 | }, 960 | "clicking_confirm_login": { 961 | Language.ENGLISH: "Clicking login confirmation button", 962 | Language.CHINESE: "点击确认登录按钮" 963 | }, 964 | "polling_auth_status": { 965 | Language.ENGLISH: "Polling authentication status: {0}", 966 | Language.CHINESE: "轮询认证状态: {0}" 967 | }, 968 | "token_userid_success": { 969 | Language.ENGLISH: "Successfully obtained account token and userId", 970 | Language.CHINESE: "成功获取账号token和userId" 971 | }, 972 | "api_request_failed": { 973 | Language.ENGLISH: "API request failed with status code: {0}", 974 | Language.CHINESE: "API请求失败,状态码: {0}" 975 | }, 976 | "login_confirm_button_not_found": { 977 | Language.ENGLISH: "Login confirmation button not found", 978 | Language.CHINESE: "未找到登录确认按钮" 979 | }, 980 | "deep_login_token_failed": { 981 | Language.ENGLISH: "Deep login token acquisition failed: {0}", 982 | Language.CHINESE: "深度登录获取token失败: {0}" 983 | }, 984 | "max_attempts_reached": { 985 | Language.ENGLISH: "Maximum attempts reached ({0}), failed to get session token", 986 | Language.CHINESE: "已达到最大尝试次数({0}),获取会话令牌失败" 987 | }, 988 | # Email deletion translations 989 | "no_emails_found": { 990 | Language.ENGLISH: "No emails found in iCloud account", 991 | Language.CHINESE: "在 iCloud 账户中找不到邮箱" 992 | }, 993 | "email_not_found": { 994 | Language.ENGLISH: "Email not found: {0}", 995 | Language.CHINESE: "未找到邮箱: {0}" 996 | }, 997 | "email_deleted_success": { 998 | Language.ENGLISH: "Email deleted successfully: {0}", 999 | Language.CHINESE: "邮箱删除成功: {0}" 1000 | }, 1001 | "email_deletion_failed": { 1002 | Language.ENGLISH: "Email deletion failed: {0}", 1003 | Language.CHINESE: "邮箱删除失败: {0}" 1004 | }, 1005 | "no_emails_to_delete": { 1006 | Language.ENGLISH: "No emails specified for deletion", 1007 | Language.CHINESE: "未指定要删除的邮箱" 1008 | }, 1009 | "email_deletion_error": { 1010 | Language.ENGLISH: "Error during email deletion: {0}", 1011 | Language.CHINESE: "删除邮箱过程中发生错误: {0}" 1012 | }, 1013 | "deleting_emails": { 1014 | Language.ENGLISH: "Deleting {0} email(s)...", 1015 | Language.CHINESE: "正在删除 {0} 个邮箱..." 1016 | }, 1017 | "success": { 1018 | Language.ENGLISH: "Success", 1019 | Language.CHINESE: "成功" 1020 | }, 1021 | "failed": { 1022 | Language.ENGLISH: "Failed", 1023 | Language.CHINESE: "失败" 1024 | }, 1025 | "no_emails_specified": { 1026 | Language.ENGLISH: "No emails specified. Usage: deleteEmail.py email1@example.com [email2@example.com ...]", 1027 | Language.CHINESE: "未指定邮箱。用法: deleteEmail.py email1@example.com [email2@example.com ...]" 1028 | }, 1029 | "delete_email_not_available": { 1030 | Language.ENGLISH: "Email deletion function not available", 1031 | Language.CHINESE: "邮箱删除功能不可用" 1032 | }, 1033 | "deleting_generated_email": { 1034 | Language.ENGLISH: "Deleting generated email: {0}", 1035 | Language.CHINESE: "正在删除生成的邮箱: {0}" 1036 | }, 1037 | "delete_email_no_result": { 1038 | Language.ENGLISH: "No result received from email deletion", 1039 | Language.CHINESE: "未收到邮箱删除结果" 1040 | }, 1041 | "delete_email_exception": { 1042 | Language.ENGLISH: "Error occurred while deleting email: {0}", 1043 | Language.CHINESE: "删除邮箱时发生错误: {0}" 1044 | }, 1045 | "delete_after_use_enabled": { 1046 | Language.ENGLISH: "Delete email after use option enabled", 1047 | Language.CHINESE: "已启用使用后删除邮箱选项" 1048 | }, 1049 | "deleting_icloud_email_after_use": { 1050 | Language.ENGLISH: "Deleting iCloud email after successful registration", 1051 | Language.CHINESE: "注册成功后删除 iCloud 邮箱" 1052 | }, 1053 | "delete_email_prompt": { 1054 | Language.ENGLISH: "Delete iCloud email after successful registration?", 1055 | Language.CHINESE: "注册成功后删除 iCloud 邮箱?" 1056 | }, 1057 | "manual_code_prompt":{ 1058 | Language.ENGLISH: "Enter verification code manually?", 1059 | Language.CHINESE: "手动输入验证码?" 1060 | }, 1061 | "manual_code_input":{ 1062 | Language.ENGLISH: "Enter the verification code manually:", 1063 | Language.CHINESE: "请手动输入验证码:" 1064 | }, 1065 | "delete_after_use_disabled": { 1066 | Language.ENGLISH: "Keep email after use option selected", 1067 | Language.CHINESE: "已选择保留邮箱选项" 1068 | }, 1069 | "deactivating_email": { 1070 | Language.ENGLISH: "Deactivating email with ID: {0}", 1071 | Language.CHINESE: "正在停用ID为 {0} 的邮箱" 1072 | }, 1073 | "deactivate_email_timeout": { 1074 | Language.ENGLISH: "Timeout while trying to deactivate email", 1075 | Language.CHINESE: "停用邮箱时超时" 1076 | }, 1077 | "deactivate_email_failed": { 1078 | Language.ENGLISH: "Failed to deactivate email: {0}", 1079 | Language.CHINESE: "停用邮箱失败: {0}" 1080 | }, 1081 | "email_deactivation_failed": { 1082 | Language.ENGLISH: "Email deactivation failed: {0}", 1083 | Language.CHINESE: "邮箱停用失败: {0}" 1084 | }, 1085 | "email_missing_id": { 1086 | Language.ENGLISH: "Email is missing anonymousId: {0}", 1087 | Language.CHINESE: "邮箱缺少匿名ID: {0}" 1088 | }, 1089 | # Logger translations 1090 | "debug_prefix_format": { 1091 | Language.ENGLISH: "[Open Source Project: https://github.com/Ryan0204/cursor-auto-icloud] {0}", 1092 | Language.CHINESE: "[开源项目:https://github.com/Ryan0204/cursor-auto-icloud] {0}" 1093 | }, 1094 | "logger_initialized": { 1095 | Language.ENGLISH: "Logger initialized, log directory: {0}", 1096 | Language.CHINESE: "日志记录器已初始化,日志目录: {0}" 1097 | }, 1098 | "main_task_starting": { 1099 | Language.ENGLISH: "Starting the main task...", 1100 | Language.CHINESE: "开始执行主任务..." 1101 | }, 1102 | "simulated_error": { 1103 | Language.ENGLISH: "Simulated error occurred.", 1104 | Language.CHINESE: "模拟错误发生。" 1105 | }, 1106 | "main_task_completed": { 1107 | Language.ENGLISH: "Main task completed successfully.", 1108 | Language.CHINESE: "主任务成功完成。" 1109 | }, 1110 | "value_error_occurred": { 1111 | Language.ENGLISH: "ValueError occurred: {0}", 1112 | Language.CHINESE: "发生值错误: {0}" 1113 | }, 1114 | "unexpected_error_occurred": { 1115 | Language.ENGLISH: "Unexpected error occurred: {0}", 1116 | Language.CHINESE: "发生意外错误: {0}" 1117 | }, 1118 | "task_execution_finished": { 1119 | Language.ENGLISH: "Task execution finished.", 1120 | Language.CHINESE: "任务执行完成。" 1121 | }, 1122 | "application_started": { 1123 | Language.ENGLISH: "Application started.", 1124 | Language.CHINESE: "应用程序已启动。" 1125 | }, 1126 | "application_exited": { 1127 | Language.ENGLISH: "Application exited.", 1128 | Language.CHINESE: "应用程序已退出。" 1129 | }, 1130 | "log_directory_location": { 1131 | Language.ENGLISH: "Log files are saved in: {0}", 1132 | Language.CHINESE: "日志文件保存在: {0}" 1133 | }, 1134 | } 1135 | 1136 | def get_text(self, key: str, *args) -> str: 1137 | """ 1138 | Get translated text for the current language 1139 | 1140 | Args: 1141 | key: The translation key 1142 | *args: Optional format arguments 1143 | 1144 | Returns: 1145 | str: The translated text 1146 | """ 1147 | if key not in self._translations: 1148 | # Fallback to key if translation not found 1149 | return key 1150 | 1151 | translation = self._translations[key].get(self.current_language, key) 1152 | 1153 | # Apply formatting if args are provided 1154 | if args: 1155 | try: 1156 | return translation.format(*args) 1157 | except: 1158 | return translation 1159 | 1160 | return translation 1161 | 1162 | def switch_language(self, language: Language) -> None: 1163 | """ 1164 | Switch the current language 1165 | 1166 | Args: 1167 | language: The language to switch to 1168 | """ 1169 | self.current_language = language 1170 | 1171 | def toggle_language(self) -> Language: 1172 | """ 1173 | Toggle between available languages 1174 | 1175 | Returns: 1176 | Language: The new language 1177 | """ 1178 | if self.current_language == Language.ENGLISH: 1179 | self.current_language = Language.CHINESE 1180 | else: 1181 | self.current_language = Language.ENGLISH 1182 | 1183 | return self.current_language 1184 | 1185 | def select_language(self) -> Language: 1186 | """ 1187 | Prompt user to select a language 1188 | 1189 | Returns: 1190 | Language: The selected language 1191 | """ 1192 | while True: 1193 | try: 1194 | choice = input(self.get_text("select_new_language")).strip() 1195 | if choice == "1": 1196 | self.current_language = Language.ENGLISH 1197 | print(self.get_text("language_switched")) 1198 | break 1199 | elif choice == "2": 1200 | self.current_language = Language.CHINESE 1201 | print(self.get_text("language_switched")) 1202 | break 1203 | else: 1204 | print(self.get_text("invalid_option")) 1205 | except ValueError: 1206 | print(self.get_text("enter_valid_number")) 1207 | 1208 | return self.current_language 1209 | 1210 | 1211 | # Helper function for easier access to translations 1212 | def _(key: str, *args) -> str: 1213 | """ 1214 | Shorthand function to get translated text 1215 | 1216 | Args: 1217 | key: The translation key 1218 | *args: Optional format arguments 1219 | 1220 | Returns: 1221 | str: The translated text 1222 | """ 1223 | return LanguageManager().get_text(key, *args) 1224 | 1225 | # More descriptive alternative to _() 1226 | def getTranslation(key: str, *args) -> str: 1227 | """ 1228 | Get translated text for the current language setting 1229 | 1230 | This is a more descriptive alternative to the shorthand _() function 1231 | 1232 | Args: 1233 | key: The translation key 1234 | *args: Optional format arguments 1235 | 1236 | Returns: 1237 | str: The translated text 1238 | """ 1239 | return LanguageManager().get_text(key, *args) --------------------------------------------------------------------------------