import os import json import requests import logging import subprocess import uuid import hashlib from typing import Optional, Dict, Tuple from pathlib import Path from utils.config import Config def get_hardware_id() -> str: """获取硬件唯一标识""" try: # 获取CPU信息 cpu_info = subprocess.check_output('wmic cpu get ProcessorId').decode() cpu_id = cpu_info.split('\n')[1].strip() # 获取主板序列号 board_info = subprocess.check_output('wmic baseboard get SerialNumber').decode() board_id = board_info.split('\n')[1].strip() # 获取BIOS序列号 bios_info = subprocess.check_output('wmic bios get SerialNumber').decode() bios_id = bios_info.split('\n')[1].strip() # 组合信息并生成哈希 combined = f"{cpu_id}:{board_id}:{bios_id}" return hashlib.md5(combined.encode()).hexdigest() except Exception as e: logging.error(f"获取硬件ID失败: {str(e)}") # 如果获取失败,使用UUID作为备选方案 return str(uuid.uuid4()) class CursorAuthManager: def __init__(self): self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor" self.app_path = self.cursor_path / "resources" / "app" self.package_json = self.app_path / "package.json" def update_auth(self, email: str, access_token: str, refresh_token: str) -> bool: """更新Cursor认证信息""" try: # 读取package.json with open(self.package_json, "r", encoding="utf-8") as f: data = json.load(f) # 更新认证信息 data["email"] = email data["accessToken"] = access_token data["refreshToken"] = refresh_token # 保存更新后的文件 with open(self.package_json, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) logging.info(f"认证信息更新成功: {email}") return True except Exception as e: logging.error(f"更新认证信息失败: {str(e)}") return False class AccountSwitcher: def __init__(self): self.cursor_path = Path(os.path.expanduser("~")) / "AppData" / "Local" / "Programs" / "Cursor" self.app_path = self.cursor_path / "resources" / "app" self.package_json = self.app_path / "package.json" self.auth_manager = CursorAuthManager() self.config = Config() self.hardware_id = get_hardware_id() def get_device_info(self) -> dict: """获取设备信息""" try: import platform import socket import requests # 获取操作系统信息 os_info = f"{platform.system()} {platform.version()}" # 获取设备名称 device_name = platform.node() # 获取地理位置(可选) try: ip_info = requests.get('https://ipapi.co/json/', timeout=5).json() location = f"{ip_info.get('country_name', '')}-{ip_info.get('region', '')}-{ip_info.get('city', '')}" except: location = "" return { "os": os_info, "device_name": device_name, "location": location } except Exception as e: logging.error(f"获取设备信息失败: {str(e)}") return { "os": "Windows 10", "device_name": "未知设备", "location": "" } def check_activation_code(self, code: str) -> Tuple[bool, str, Optional[Dict]]: """验证激活码 Returns: Tuple[bool, str, Optional[Dict]]: (是否成功, 提示消息, 账号信息) """ try: # 获取当前状态 member_info = self.config.load_member_info() # 分割多个激活码 codes = [c.strip() for c in code.split(',')] success_codes = [] failed_codes = [] activation_results = [] # 获取设备信息 device_info = self.get_device_info() # 逐个验证激活码 for single_code in codes: if not single_code: continue # 验证激活码 endpoint = "https://cursorapi.nosqli.com/admin/api.member/activate" data = { "code": single_code, "machine_id": self.hardware_id, "os": device_info["os"], "device_name": device_info["device_name"], "location": device_info["location"] } headers = { "Content-Type": "application/json" } try: response = requests.post( endpoint, json=data, headers=headers, timeout=10 ) response_data = response.json() if response_data.get("code") == 200: result_data = response_data.get("data", {}) logging.info(f"激活码 {single_code} 验证成功: {response_data.get('msg', '')}") activation_results.append(result_data) success_codes.append(single_code) elif response_data.get("code") == 400: error_msg = response_data.get("msg", "参数错误") if "已被使用" in error_msg or "已激活" in error_msg: logging.warning(f"激活码 {single_code} 已被使用") failed_codes.append(f"{single_code} (已被使用)") else: logging.error(f"激活码 {single_code} 验证失败: {error_msg}") failed_codes.append(f"{single_code} ({error_msg})") elif response_data.get("code") == 500: error_msg = response_data.get("msg", "系统错误") logging.error(f"激活码 {single_code} 验证失败: {error_msg}") failed_codes.append(f"{single_code} ({error_msg})") else: error_msg = response_data.get("msg", "未知错误") logging.error(f"激活码 {single_code} 验证失败: {error_msg}") failed_codes.append(f"{single_code} ({error_msg})") except requests.RequestException as e: logging.error(f"激活码 {single_code} 请求失败: {str(e)}") failed_codes.append(f"{single_code} (网络请求失败)") except Exception as e: logging.error(f"激活码 {single_code} 处理失败: {str(e)}") failed_codes.append(f"{single_code} (处理失败)") if not success_codes: failed_msg = "\n".join(failed_codes) return False, f"激活失败:\n{failed_msg}", None try: # 使用最后一次激活的结果作为最终状态 final_result = activation_results[-1] # 保存会员信息 member_info = { "hardware_id": final_result.get("machine_id", self.hardware_id), "expire_time": final_result.get("expire_time", ""), "days_left": final_result.get("days_left", 0), # 使用days_left "total_days": final_result.get("total_days", 0), # 使用total_days "status": final_result.get("status", "inactive"), "device_info": final_result.get("device_info", device_info), "activation_time": final_result.get("activation_time", ""), "activation_records": final_result.get("activation_records", []) # 保存激活记录 } self.config.save_member_info(member_info) # 生成结果消息 message = f"激活成功\n" # 显示每个成功激活码的信息 for i, result in enumerate(activation_results, 1): message += f"\n第{i}个激活码:\n" message += f"- 新增天数: {result.get('added_days', 0)}天\n" # 使用added_days显示本次新增天数 # 格式化时间显示 activation_time = result.get('activation_time', '') if activation_time: try: from datetime import datetime dt = datetime.strptime(activation_time, "%Y-%m-%d %H:%M:%S") activation_time = dt.strftime("%Y-%m-%d %H:%M:%S") except: pass message += f"- 激活时间: {activation_time}\n" message += f"\n最终状态:" message += f"\n- 总天数: {final_result.get('total_days', 0)}天" # 累计总天数 message += f"\n- 剩余天数: {final_result.get('days_left', 0)}天" # 剩余天数 # 格式化到期时间显示 expire_time = final_result.get('expire_time', '') if expire_time: try: dt = datetime.strptime(expire_time, "%Y-%m-%d %H:%M:%S") expire_time = dt.strftime("%Y-%m-%d %H:%M:%S") except: pass message += f"\n- 到期时间: {expire_time}" # 显示激活记录历史 message += "\n\n历史激活记录:" for record in final_result.get('activation_records', []): activation_time = record.get('activation_time', '') if activation_time: try: dt = datetime.strptime(activation_time, "%Y-%m-%d %H:%M:%S") activation_time = dt.strftime("%Y-%m-%d %H:%M:%S") except: pass message += f"\n- 激活码: {record.get('code', '')}" message += f"\n 天数: {record.get('days', 0)}天" message += f"\n 时间: {activation_time}\n" if failed_codes: message += f"\n\n以下激活码验证失败:\n" + "\n".join(failed_codes) return True, message, member_info except Exception as e: logging.error(f"处理激活结果时出错: {str(e)}") return False, f"处理激活结果失败: {str(e)}", None except Exception as e: logging.error(f"激活码验证过程出错: {str(e)}") return False, f"激活失败: {str(e)}", None def reset_machine_id(self) -> bool: """重置机器码""" try: # 读取package.json with open(self.package_json, "r", encoding="utf-8") as f: data = json.load(f) # 删除machineId if "machineId" in data: del data["machineId"] # 保存文件 with open(self.package_json, "w", encoding="utf-8") as f: json.dump(data, f, indent=2) logging.info("机器码重置完成") return True except Exception as e: logging.error(f"重置机器码失败: {str(e)}") return False def activate_and_switch(self, activation_code: str) -> Tuple[bool, str]: """激活并切换账号 Returns: Tuple[bool, str]: (是否成功, 提示消息) """ try: # 验证激活码 success, message, account_info = self.check_activation_code(activation_code) return success, message except Exception as e: logging.error(f"激活过程出错: {str(e)}") return False, f"激活失败: {str(e)}" def get_member_status(self) -> Optional[Dict]: """获取会员状态 Returns: Optional[Dict]: 会员状态信息 """ try: # 读取保存的会员信息 member_info = self.config.load_member_info() # 构造状态检查请求 endpoint = "https://cursorapi.nosqli.com/admin/api.member/status" data = { "machine_id": self.hardware_id } headers = { "Content-Type": "application/json" } try: response = requests.post(endpoint, json=data, headers=headers, timeout=10) response_data = response.json() if response_data.get("code") == 200: # 正常状态 data = response_data.get("data", {}) status = data.get("status", "inactive") # 构造会员信息 member_info = { "hardware_id": data.get("machine_id", self.hardware_id), "expire_time": data.get("expire_time", ""), "days_left": data.get("days_left", 0), # 使用days_left "total_days": data.get("total_days", 0), # 使用total_days "status": status, "activation_records": data.get("activation_records", []) # 保存激活记录 } # 打印调试信息 logging.info(f"API返回数据: {data}") logging.info(f"处理后的会员信息: {member_info}") self.config.save_member_info(member_info) return member_info elif response_data.get("code") == 401: # 未激活或已过期 logging.warning("会员未激活或已过期") return self._get_inactive_status() elif response_data.get("code") == 400: # 参数错误 error_msg = response_data.get("msg", "参数错误") logging.error(f"获取会员状态失败: {error_msg}") return self._get_inactive_status() elif response_data.get("code") == 500: # 系统错误 error_msg = response_data.get("msg", "系统错误") logging.error(f"获取会员状态失败: {error_msg}") return self._get_inactive_status() else: # 其他未知错误 error_msg = response_data.get("msg", "未知错误") logging.error(f"获取会员状态失败: {error_msg}") return self._get_inactive_status() except requests.RequestException as e: logging.error(f"请求会员状态失败: {str(e)}") return self._get_inactive_status() except Exception as e: logging.error(f"获取会员状态出错: {str(e)}") return self._get_inactive_status() def _get_inactive_status(self) -> Dict: """获取未激活状态的默认信息""" return { "hardware_id": self.hardware_id, "expire_time": "", "days": 0, "total_days": 0, "status": "inactive", "last_activation": {}, "activation_records": [] } def main(): """主函数""" try: switcher = AccountSwitcher() print("\n=== Cursor账号切换工具 ===") print("1. 激活并切换账号") print("2. 仅重置机器码") while True: try: choice = int(input("\n请选择操作 (1 或 2): ").strip()) if choice in [1, 2]: break else: print("无效的选项,请重新输入") except ValueError: print("请输入有效的数字") if choice == 1: activation_code = input("请输入激活码: ").strip() if switcher.activate_and_switch(activation_code): print("\n账号激活成功!") else: print("\n账号激活失败,请查看日志了解详细信息") else: if switcher.reset_machine_id(): print("\n机器码重置成功!") else: print("\n机器码重置失败,请查看日志了解详细信息") except Exception as e: logging.error(f"程序执行出错: {str(e)}") print("\n程序执行出错,请查看日志了解详细信息") finally: input("\n按回车键退出...") if __name__ == "__main__": main()