mirror of
				https://github.com/yeongpin/cursor-free-vip.git
				synced 2025-10-31 04:26:22 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			268 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			268 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import os
 | ||
| import sys
 | ||
| import platform
 | ||
| import shutil
 | ||
| from colorama import Fore, Style, init
 | ||
| import subprocess
 | ||
| from config import get_config
 | ||
| import re
 | ||
| import tempfile
 | ||
| 
 | ||
| # Initialize colorama
 | ||
| init()
 | ||
| 
 | ||
| # Define emoji constants
 | ||
| EMOJI = {
 | ||
|     "PROCESS": "🔄",
 | ||
|     "SUCCESS": "✅",
 | ||
|     "ERROR": "❌",
 | ||
|     "INFO": "ℹ️",
 | ||
|     "FOLDER": "📁",
 | ||
|     "FILE": "📄",
 | ||
|     "STOP": "🛑",
 | ||
|     "CHECK": "✔️"
 | ||
| }
 | ||
| 
 | ||
| class AutoUpdateDisabler:
 | ||
|     def __init__(self, translator=None):
 | ||
|         self.translator = translator
 | ||
|         self.system = platform.system()
 | ||
|         
 | ||
|         # Get path from configuration file
 | ||
|         config = get_config(translator)
 | ||
|         if config:
 | ||
|             if self.system == "Windows":
 | ||
|                 self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
 | ||
|                 self.update_yml_path = config.get('WindowsPaths', 'update_yml_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"))
 | ||
|                 self.product_json_path = config.get('WindowsPaths', 'product_json_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"))
 | ||
|             elif self.system == "Darwin":
 | ||
|                 self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
 | ||
|                 self.update_yml_path = config.get('MacPaths', 'update_yml_path', fallback="/Applications/Cursor.app/Contents/Resources/app-update.yml")
 | ||
|                 self.product_json_path = config.get('MacPaths', 'product_json_path', fallback="/Applications/Cursor.app/Contents/Resources/app/product.json")
 | ||
|             elif self.system == "Linux":
 | ||
|                 self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
 | ||
|                 self.update_yml_path = config.get('LinuxPaths', 'update_yml_path', fallback=os.path.expanduser("~/.config/cursor/resources/app-update.yml"))
 | ||
|                 self.product_json_path = config.get('LinuxPaths', 'product_json_path', fallback=os.path.expanduser("~/.config/cursor/resources/app/product.json"))
 | ||
|         else:
 | ||
|             # If configuration loading fails, use default paths
 | ||
|             self.updater_paths = {
 | ||
|                 "Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"),
 | ||
|                 "Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"),
 | ||
|                 "Linux": os.path.expanduser("~/.config/cursor-updater")
 | ||
|             }
 | ||
|             self.updater_path = self.updater_paths.get(self.system)
 | ||
|             
 | ||
|             self.update_yml_paths = {
 | ||
|                 "Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "update.yml"),
 | ||
|                 "Darwin": "/Applications/Cursor.app/Contents/Resources/app-update.yml",
 | ||
|                 "Linux": os.path.expanduser("~/.config/cursor/resources/app-update.yml")
 | ||
|             }
 | ||
|             self.update_yml_path = self.update_yml_paths.get(self.system)
 | ||
| 
 | ||
|             self.product_json_paths = {
 | ||
|                 "Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app", "product.json"),
 | ||
|                 "Darwin": "/Applications/Cursor.app/Contents/Resources/app/product.json",
 | ||
|                 "Linux": os.path.expanduser("~/.config/cursor/resources/app/product.json")
 | ||
|             }
 | ||
|             self.product_json_path = self.product_json_paths.get(self.system)
 | ||
| 
 | ||
|     def _remove_update_url(self):
 | ||
|         """Remove update URL"""
 | ||
|         try:
 | ||
|             original_stat = os.stat(self.product_json_path)
 | ||
|             original_mode = original_stat.st_mode
 | ||
|             original_uid = original_stat.st_uid
 | ||
|             original_gid = original_stat.st_gid
 | ||
| 
 | ||
|             with tempfile.NamedTemporaryFile(mode="w", delete=False) as tmp_file:
 | ||
|                 with open(self.product_json_path, "r", encoding="utf-8") as product_json_file:
 | ||
|                     content = product_json_file.read()
 | ||
|                 
 | ||
|                 patterns = {
 | ||
|                     r"https://api2.cursor.sh/aiserver.v1.AuthService/DownloadUpdate": r"",
 | ||
|                     r"https://api2.cursor.sh/updates": r"",
 | ||
|                     r"http://cursorapi.com/updates": r"",
 | ||
|                 }
 | ||
|                 
 | ||
|                 for pattern, replacement in patterns.items():
 | ||
|                     content = re.sub(pattern, replacement, content)
 | ||
| 
 | ||
|                 tmp_file.write(content)
 | ||
|                 tmp_path = tmp_file.name
 | ||
| 
 | ||
|             shutil.copy2(self.product_json_path, self.product_json_path + ".old")
 | ||
|             shutil.move(tmp_path, self.product_json_path)
 | ||
| 
 | ||
|             os.chmod(self.product_json_path, original_mode)
 | ||
|             if os.name != "nt":
 | ||
|                 os.chown(self.product_json_path, original_uid, original_gid)
 | ||
| 
 | ||
|             print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.file_modified')}{Style.RESET_ALL}")
 | ||
|             return True
 | ||
| 
 | ||
|         except Exception as e:
 | ||
|             print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
 | ||
|             if "tmp_path" in locals():
 | ||
|                 os.unlink(tmp_path)
 | ||
|             return False
 | ||
| 
 | ||
|     def _kill_cursor_processes(self):
 | ||
|         """End all Cursor processes"""
 | ||
|         try:
 | ||
|             print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('update.killing_processes') if self.translator else '正在结束 Cursor 进程...'}{Style.RESET_ALL}")
 | ||
|             
 | ||
|             if self.system == "Windows":
 | ||
|                 subprocess.run(['taskkill', '/F', '/IM', 'Cursor.exe', '/T'], capture_output=True)
 | ||
|             else:
 | ||
|                 subprocess.run(['pkill', '-f', 'Cursor'], capture_output=True)
 | ||
|                 
 | ||
|             print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.processes_killed') if self.translator else 'Cursor 进程已结束'}{Style.RESET_ALL}")
 | ||
|             return True
 | ||
|             
 | ||
|         except Exception as e:
 | ||
|             print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.kill_process_failed', error=str(e)) if self.translator else f'结束进程失败: {e}'}{Style.RESET_ALL}")
 | ||
|             return False
 | ||
| 
 | ||
|     def _remove_updater_directory(self):
 | ||
|         """Delete updater directory"""
 | ||
|         try:
 | ||
|             updater_path = self.updater_path
 | ||
|             if not updater_path:
 | ||
|                 raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
 | ||
| 
 | ||
|             print(f"{Fore.CYAN}{EMOJI['FOLDER']} {self.translator.get('update.removing_directory') if self.translator else '正在删除更新程序目录...'}{Style.RESET_ALL}")
 | ||
|             
 | ||
|             if os.path.exists(updater_path):
 | ||
|                 try:
 | ||
|                     if os.path.isdir(updater_path):
 | ||
|                         shutil.rmtree(updater_path)
 | ||
|                     else:
 | ||
|                         os.remove(updater_path)
 | ||
|                     print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}")
 | ||
|                 except PermissionError:
 | ||
|                     print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.directory_locked', path=updater_path) if self.translator else f'更新程序目录已被锁定,跳过删除: {updater_path}'}{Style.RESET_ALL}")
 | ||
|             return True
 | ||
|             
 | ||
|         except Exception as e:
 | ||
|             print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}")
 | ||
|             return True
 | ||
|     
 | ||
|     def _clear_update_yml_file(self):
 | ||
|         """Clear update.yml file"""
 | ||
|         try:
 | ||
|             update_yml_path = self.update_yml_path
 | ||
|             if not update_yml_path:
 | ||
|                 raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
 | ||
|             
 | ||
|             print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.clearing_update_yml') if self.translator else '正在清空更新配置文件...'}{Style.RESET_ALL}")
 | ||
|             
 | ||
|             if os.path.exists(update_yml_path):
 | ||
|                 try:
 | ||
|                     with open(update_yml_path, 'w') as f:
 | ||
|                         f.write('')
 | ||
|                     print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.update_yml_cleared') if self.translator else '更新配置文件已清空'}{Style.RESET_ALL}")
 | ||
|                 except PermissionError:
 | ||
|                     print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已被锁定,跳过清空'}{Style.RESET_ALL}")
 | ||
|             else:
 | ||
|                 print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.update_yml_not_found') if self.translator else '更新配置文件不存在'}{Style.RESET_ALL}")
 | ||
|             return True
 | ||
|                 
 | ||
|         except Exception as e:
 | ||
|             print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.clear_update_yml_failed', error=str(e)) if self.translator else f'清空更新配置文件失败: {e}'}{Style.RESET_ALL}")
 | ||
|             return False
 | ||
| 
 | ||
|     def _create_blocking_file(self):
 | ||
|         """Create blocking files"""
 | ||
|         try:
 | ||
|             # 检查 updater_path
 | ||
|             updater_path = self.updater_path
 | ||
|             if not updater_path:
 | ||
|                 raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
 | ||
| 
 | ||
|             print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}")
 | ||
|             
 | ||
|             # 创建 updater_path 阻止文件
 | ||
|             try:
 | ||
|                 os.makedirs(os.path.dirname(updater_path), exist_ok=True)
 | ||
|                 open(updater_path, 'w').close()
 | ||
|                 
 | ||
|                 # 设置 updater_path 为只读
 | ||
|                 if self.system == "Windows":
 | ||
|                     os.system(f'attrib +r "{updater_path}"')
 | ||
|                 else:
 | ||
|                     os.chmod(updater_path, 0o444)  # 设置为只读
 | ||
|                 
 | ||
|                 print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}: {updater_path}{Style.RESET_ALL}")
 | ||
|             except PermissionError:
 | ||
|                 print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.block_file_locked') if self.translator else '阻止文件已被锁定,跳过创建'}{Style.RESET_ALL}")
 | ||
|             
 | ||
|             # 检查 update_yml_path
 | ||
|             update_yml_path = self.update_yml_path
 | ||
|             if update_yml_path and os.path.exists(os.path.dirname(update_yml_path)):
 | ||
|                 try:
 | ||
|                     # 创建 update_yml_path 阻止文件
 | ||
|                     with open(update_yml_path, 'w') as f:
 | ||
|                         f.write('# This file is locked to prevent auto-updates\nversion: 0.0.0\n')
 | ||
|                     
 | ||
|                     # 设置 update_yml_path 为只读
 | ||
|                     if self.system == "Windows":
 | ||
|                         os.system(f'attrib +r "{update_yml_path}"')
 | ||
|                     else:
 | ||
|                         os.chmod(update_yml_path, 0o444)  # 设置为只读
 | ||
|                     
 | ||
|                     print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.yml_locked') if self.translator else '更新配置文件已锁定'}: {update_yml_path}{Style.RESET_ALL}")
 | ||
|                 except PermissionError:
 | ||
|                     print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('update.yml_already_locked') if self.translator else '更新配置文件已被锁定,跳过修改'}{Style.RESET_ALL}")
 | ||
|             
 | ||
|             return True
 | ||
|             
 | ||
|         except Exception as e:
 | ||
|             print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.create_block_file_failed', error=str(e)) if self.translator else f'创建阻止文件失败: {e}'}{Style.RESET_ALL}")
 | ||
|             return True  # 返回 True 以继续执行后续步骤
 | ||
| 
 | ||
|     def disable_auto_update(self):
 | ||
|         """Disable auto update"""
 | ||
|         try:
 | ||
|             print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('update.start_disable') if self.translator else '开始禁用自动更新...'}{Style.RESET_ALL}")
 | ||
|             
 | ||
|             # 1. End processes
 | ||
|             if not self._kill_cursor_processes():
 | ||
|                 return False
 | ||
|                 
 | ||
|             # 2. Delete directory - 即使失败也继续执行
 | ||
|             self._remove_updater_directory()
 | ||
|                 
 | ||
|             # 3. Clear update.yml file
 | ||
|             if not self._clear_update_yml_file():
 | ||
|                 return False
 | ||
|                 
 | ||
|             # 4. Create blocking file
 | ||
|             if not self._create_blocking_file():
 | ||
|                 return False
 | ||
|                 
 | ||
|             # 5. Remove update URL from product.json
 | ||
|             if not self._remove_update_url():
 | ||
|                 return False
 | ||
|                 
 | ||
|             print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
 | ||
|             return True
 | ||
|             
 | ||
|         except Exception as e:
 | ||
|             print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.disable_failed', error=str(e)) if self.translator else f'禁用自动更新失败: {e}'}{Style.RESET_ALL}")
 | ||
|             return False
 | ||
| 
 | ||
| def run(translator=None):
 | ||
|     """Convenient function for directly calling the disable function"""
 | ||
|     print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
 | ||
|     print(f"{Fore.CYAN}{EMOJI['STOP']} {translator.get('update.title') if translator else 'Disable Cursor Auto Update'}{Style.RESET_ALL}")
 | ||
|     print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
 | ||
| 
 | ||
|     disabler = AutoUpdateDisabler(translator)
 | ||
|     disabler.disable_auto_update()
 | ||
| 
 | ||
|     print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
 | ||
|     input(f"{EMOJI['INFO']} {translator.get('update.press_enter') if translator else 'Press Enter to Continue...'}")
 | ||
| 
 | ||
| if __name__ == "__main__":
 | ||
|     from main import translator as main_translator
 | ||
|     run(main_translator)  | 
