diff --git a/cursor_account_manager.py b/cursor_account_manager.py index cee18fb..ff43bc3 100644 --- a/cursor_account_manager.py +++ b/cursor_account_manager.py @@ -399,7 +399,7 @@ def reset_auth_with_password(password: str = None) -> tuple[bool, str]: auth_manager = CursorAuthManager() if auth_manager.update_auth(email, access_token, refresh_token): logging.info("认证信息更新成功") - + # 重置机器码 logging.info("正在重置机器码...") diff --git a/cursor_gui.py b/cursor_gui.py index ce798b9..f12c093 100644 --- a/cursor_gui.py +++ b/cursor_gui.py @@ -1,5 +1,6 @@ import sys import os +import json from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton, QTextEdit, QMessageBox, QHBoxLayout, QDialog) @@ -22,12 +23,64 @@ except Exception as e: log_error(f"导入模块错误: {str(e)}\n{traceback.format_exc()}") raise +# 添加密码管理类 +class PasswordManager: + def __init__(self): + # 获取应用程序的实际运行路径 + if getattr(sys, 'frozen', False): + # 如果是打包后的应用 + if sys.platform == 'darwin': + # macOS下使用应用程序包内的Resources目录 + bundle_dir = os.path.dirname(sys.executable) + app_root = os.path.abspath(os.path.join(bundle_dir, '..', 'Resources')) + self.app_support_dir = os.path.join(app_root, 'config') + else: + # 其他系统使用可执行文件所在目录 + app_root = os.path.dirname(sys.executable) + self.app_support_dir = os.path.join(app_root, 'config') + else: + # 开发环境下使用用户目录 + if sys.platform == 'darwin': + self.app_support_dir = os.path.expanduser('~/Library/Application Support/听泉Cursor助手') + else: + self.app_support_dir = os.path.expanduser('~/.听泉Cursor助手') + + self.config_file = os.path.join(self.app_support_dir, 'config.json') + os.makedirs(self.app_support_dir, exist_ok=True) + logging.debug(f"密码管理器初始化,配置文件路径: {self.config_file}") + + def save_password(self, password): + """保存密码(这里可以添加简单加密)""" + try: + config = {'password': password} + with open(self.config_file, 'w', encoding='utf-8') as f: + json.dump(config, f) + logging.debug(f"密码保存{'成功' if password else '已清除'}") + return True + except Exception as e: + logging.error(f"保存密码失败: {str(e)}") + return False + + def get_password(self): + """获取保存的密码""" + try: + if os.path.exists(self.config_file): + with open(self.config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + password = config.get('password') + logging.debug(f"读取到保存的密码: {'有' if password else '无'}") + return password + except Exception as e: + logging.error(f"读取密码失败: {str(e)}") + return None + class SuccessDialog(QDialog): def __init__(self, message, parent=None): super().__init__(parent) self.setWindowTitle("激活成功") - self.setFixedSize(400, 300) # 增加窗口大小 - self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.FramelessWindowHint) + self.setFixedSize(400, 300) + # 移除可能导致问题的窗口标志 + self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint) # 设置样式 self.setStyleSheet(""" @@ -136,34 +189,150 @@ class PasswordDialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle("输入系统密码") - self.setFixedSize(400, 150) + self.setFixedSize(450, 250) # 增加高度以容纳新控件 + + # 设置样式 + self.setStyleSheet(""" + QDialog { + background-color: #ffffff; + border: 1px solid #cccccc; + border-radius: 10px; + } + QLabel { + color: #333333; + font-size: 14px; + padding: 5px; + } + QLabel#tipLabel { + color: #666666; + font-size: 13px; + padding: 12px 15px; + background-color: #f8f8f8; + border-radius: 6px; + line-height: 20px; + min-height: 45px; + } + QLineEdit { + padding: 8px 12px; + border: 2px solid #cccccc; + border-radius: 6px; + background-color: white; + font-size: 14px; + min-height: 20px; + } + QLineEdit:focus { + border-color: #0078d4; + } + QPushButton { + background-color: #0078d4; + color: white; + border: none; + padding: 8px 20px; + border-radius: 6px; + font-size: 14px; + min-width: 80px; + } + QPushButton:hover { + background-color: #006cbd; + } + QPushButton:pressed { + background-color: #005ba1; + } + QPushButton#cancelButton { + background-color: #f0f0f0; + color: #333333; + } + QPushButton#cancelButton:hover { + background-color: #e0e0e0; + } + QPushButton#cancelButton:pressed { + background-color: #d0d0d0; + } + QCheckBox { + color: #333333; + font-size: 13px; + } + QCheckBox::indicator { + width: 18px; + height: 18px; + } + QCheckBox::indicator:unchecked { + border: 2px solid #cccccc; + background: white; + border-radius: 4px; + } + QCheckBox::indicator:checked { + border: 2px solid #0078d4; + background: #0078d4; + border-radius: 4px; + } + """) layout = QVBoxLayout() - layout.setSpacing(15) + layout.setSpacing(8) layout.setContentsMargins(20, 20, 20, 20) + # 提示文字 + tip_label = QLabel("提示:这里需要输入您的Mac电脑开机密码(系统管理员密码),\n用于执行需要管理员权限的操作。") + tip_label.setObjectName("tipLabel") + tip_label.setWordWrap(True) + layout.addWidget(tip_label) + # 密码输入框 self.password_input = QLineEdit() self.password_input.setEchoMode(QLineEdit.EchoMode.Password) - self.password_input.setPlaceholderText("请输入系统密码") + self.password_input.setPlaceholderText("请输入Mac系统管理员密码") layout.addWidget(self.password_input) + # 记住密码选项 + from PyQt6.QtWidgets import QCheckBox + self.remember_checkbox = QCheckBox("记住密码") + self.remember_checkbox.setChecked(True) # 默认勾选 + layout.addWidget(self.remember_checkbox) + # 按钮区域 button_layout = QHBoxLayout() - ok_button = QPushButton("确定") + button_layout.setSpacing(8) + cancel_button = QPushButton("取消") - - ok_button.clicked.connect(self.accept) + cancel_button.setObjectName("cancelButton") + cancel_button.setCursor(Qt.CursorShape.PointingHandCursor) cancel_button.clicked.connect(self.reject) + cancel_button.setFixedWidth(80) - button_layout.addWidget(ok_button) + ok_button = QPushButton("确定") + ok_button.setCursor(Qt.CursorShape.PointingHandCursor) + ok_button.clicked.connect(self.accept) + ok_button.setFixedWidth(80) + + button_layout.addStretch() button_layout.addWidget(cancel_button) + button_layout.addWidget(ok_button) + + layout.addSpacing(5) layout.addLayout(button_layout) self.setLayout(layout) + + # 设置窗口位置为父窗口中心 + if parent: + self.move( + parent.x() + (parent.width() - self.width()) // 2, + parent.y() + (parent.height() - self.height()) // 2 + ) + + # 尝试加载保存的密码 + self.password_manager = PasswordManager() + saved_password = self.password_manager.get_password() + if saved_password: + self.password_input.setText(saved_password) def get_password(self): - return self.password_input.text() + password = self.password_input.text() + # 如果选择记住密码,则保存 + if self.remember_checkbox.isChecked(): + self.password_manager.save_password(password) + return password class CursorGUI(QMainWindow): def __init__(self): @@ -171,6 +340,14 @@ class CursorGUI(QMainWindow): self.setWindowTitle("听泉Cursor助手 v3.0.2") self.setFixedSize(600, 600) + # 添加全局会员状态 + self.is_member_active = False + + # 设置定时器,每3分钟检查一次会员状态 + self.status_timer = QTimer(self) + self.status_timer.timeout.connect(self.check_member_status) + self.status_timer.start(180000) # 180000毫秒 = 3分钟 + # 设置整体样式 self.setStyleSheet(""" QMainWindow { @@ -266,7 +443,7 @@ class CursorGUI(QMainWindow): layout.addWidget(self.status_text) # 激活区域 - activate_label = QLabel("激活(盈加)会员,多个激活的可盈加整体时长") + activate_label = QLabel("激活会员,多个激活码叠加整体时长") activate_layout = QHBoxLayout() activate_layout.setSpacing(10) # 设置水平间距 self.activate_input = QLineEdit() @@ -348,13 +525,15 @@ class CursorGUI(QMainWindow): # 获取设备信息 device_info = status_data.get("device_info", {}) if success else {} - # 设置状态文本 + # 设置状态文本和更新全局状态 if success and status_data.get("is_active"): status_emoji = "✅" status_text = "正常" + self.is_member_active = True else: status_emoji = "❌" status_text = "未激活" + self.is_member_active = False # 格式化显示文本 display_text = f"会员状态:{status_text} {status_emoji}\n" @@ -389,10 +568,36 @@ class CursorGUI(QMainWindow): clipboard.setText(self.id_text.text()) QMessageBox.information(self, "提示", "设备ID已复制到剪贴板") + def show_success_message(self, message): + """显示统一的成功提示弹窗""" + msg = QMessageBox(self) + msg.setIcon(QMessageBox.Icon.Information) + msg.setWindowTitle("提示") + msg.setText(message) + msg.setStyleSheet(""" + QMessageBox { + background-color: white; + } + QPushButton { + background-color: #0078d4; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + font-size: 14px; + min-width: 80px; + } + QPushButton:hover { + background-color: #006cbd; + } + """) + msg.exec() + def activate_account(self): """激活账号""" code = self.activate_input.text().strip() if not code: + logging.debug("激活码为空,显示警告") QMessageBox.warning(self, "警告", "请输入激活码") return @@ -402,12 +607,16 @@ class CursorGUI(QMainWindow): QApplication.processEvents() try: + logging.debug(f"开始检查激活码: {code}") account_manager = backend.CursorAccountManager() success, message, account_info = account_manager.check_activation_code(code) + logging.debug(f"激活码检查结果: success={success}, message={message}, account_info={account_info}") if success: - # 更新状态显示 + logging.debug("激活成功,开始更新状态") + # 更新状态显示和全局状态 self.update_status() + self.is_member_active = True # 构建成功消息 success_message = ( @@ -417,28 +626,133 @@ class CursorGUI(QMainWindow): f"总天数:{account_info.get('total_days', 0)}天\n" f"剩余天数:{account_info.get('days_left', 0)}天" ) + logging.debug(f"准备显示成功弹窗,消息内容: {success_message}") - # 显示成功弹窗 - dialog = SuccessDialog(success_message, self) - dialog.exec() + # 使用统一的成功提示弹窗 + self.show_success_message(success_message) + logging.debug("成功弹窗显示完成") # 清空输入框 self.activate_input.clear() + logging.debug("激活流程完成") else: + logging.debug(f"激活失败,显示错误消息: {message}") QMessageBox.warning(self, "错误", message) except Exception as e: + error_msg = f"激活过程出错: {str(e)}\n{traceback.format_exc()}" + logging.error(error_msg) QMessageBox.warning(self, "错误", f"激活过程出错: {str(e)}") finally: # 恢复激活按钮状态 + logging.debug("恢复激活按钮状态") self.activate_btn.setEnabled(True) self.activate_btn.setText("激活") + def show_activation_dialog(self): + """显示统一的激活提示弹窗""" + msg = QMessageBox(self) + msg.setIcon(QMessageBox.Icon.Warning) + msg.setWindowTitle("提示") + msg.setText("请输入激活码") + + # 设置详细信息 + msg.setInformativeText("获取会员激活码,请通过以下方式:\n\n" + + "• 官方自助网站:cursor.nosqli.com\n" + + "• 微信客服号:behikcigxr\n" + + "• 闲鱼店铺:xxx\n\n" + + "诚招代理商,欢迎加盟合作!") + + # 添加按钮 + visit_btn = msg.addButton("复制网站", QMessageBox.ButtonRole.ActionRole) + copy_wx_btn = msg.addButton("复制微信", QMessageBox.ButtonRole.ActionRole) + ok_btn = msg.addButton("确定", QMessageBox.ButtonRole.AcceptRole) + + msg.setDefaultButton(ok_btn) + + # 设置样式 + msg.setStyleSheet(""" + QMessageBox { + background-color: white; + } + QPushButton { + padding: 8px 16px; + border-radius: 4px; + font-size: 14px; + min-width: 80px; + } + QPushButton[text="确定"] { + background-color: #0078d4; + color: white; + border: none; + } + QPushButton[text="确定"]:hover { + background-color: #006cbd; + } + QPushButton[text="复制网站"], QPushButton[text="复制微信"] { + background-color: white; + border: 1px solid #0078d4; + color: #0078d4; + } + QPushButton[text="复制网站"]:hover, QPushButton[text="复制微信"]:hover { + background-color: #f0f9ff; + } + """) + + # 显示对话框 + clicked = msg.exec() + + # 处理按钮点击 + if msg.clickedButton() == visit_btn: + clipboard = QApplication.clipboard() + clipboard.setText("cursor.nosqli.com") + QMessageBox.information(self, "提示", "网址已复制到剪贴板") + elif msg.clickedButton() == copy_wx_btn: + clipboard = QApplication.clipboard() + clipboard.setText("behikcigxr") + QMessageBox.information(self, "提示", "微信号已复制到剪贴板") + + return False + + def check_member_status(self): + """检查会员状态""" + try: + account_manager = backend.CursorAccountManager() + success, status_data = account_manager.check_member_status() + + if success and status_data.get("is_active"): + self.is_member_active = True + return True + self.is_member_active = False + return False + except Exception as e: + logging.error(f"检查会员状态失败: {str(e)}") + self.is_member_active = False + return False + def refresh_auth(self): """刷新授权""" - # 显示密码输入对话框 - dialog = PasswordDialog(self) - if dialog.exec() == QDialog.DialogCode.Accepted: - password = dialog.get_password() + # 检查会员状态,未激活则显示激活提示 + if not self.is_member_active: + self.show_activation_dialog() + return + + # 会员已激活,继续执行原有逻辑 + while True: + # 先尝试获取保存的密码 + password_manager = PasswordManager() + saved_password = password_manager.get_password() + + if saved_password: + # 如果有保存的密码,直接使用 + password = saved_password + else: + # 如果没有保存的密码,显示密码输入对话框 + dialog = PasswordDialog(self) + if dialog.exec() == QDialog.DialogCode.Accepted: + password = dialog.get_password() + else: + QMessageBox.warning(self, "取消", "重置操作已取消") + return # 显示加载状态 self.refresh_btn.setEnabled(False) @@ -448,57 +762,95 @@ class CursorGUI(QMainWindow): try: success, message = backend.reset_auth_with_password(password) if success: - QMessageBox.information(self, "成功", "重置成功") + self.show_success_message("重置成功") self.update_status() + break # 成功后退出循环 else: + # 密码错误,清除保存的密码 + password_manager.save_password("") QMessageBox.warning(self, "错误", message) + # 继续循环,重新输入密码 except Exception as e: QMessageBox.warning(self, "错误", f"重置失败: {str(e)}") + break # 遇到其他错误时退出循环 finally: # 恢复按钮状态 self.refresh_btn.setEnabled(True) self.refresh_btn.setText("刷新Cursor授权 (对话次数用完了提示limit时点一次)") - else: - QMessageBox.warning(self, "取消", "重置操作已取消") def install_patch(self): """安装补丁""" + # 检查会员状态,未激活则显示激活提示 + if not self.is_member_active: + self.show_activation_dialog() + return + + # 会员已激活,继续执行原有逻辑 try: # 检查版本 greater_than_0_45 = backend.check_cursor_version() + logging.debug(f"Cursor版本检查结果: {'> 0.45' if greater_than_0_45 else '<= 0.45'}") - # 显示密码输入对话框 - dialog = PasswordDialog(self) - if dialog.exec() == QDialog.DialogCode.Accepted: - password = dialog.get_password() + while True: # 添加循环以支持重试 + # 先尝试获取保存的密码 + password_manager = PasswordManager() + saved_password = password_manager.get_password() + + if saved_password: + logging.debug("使用保存的密码进行验证") + password = saved_password + else: + logging.debug("没有保存的密码,显示密码输入对话框") + dialog = PasswordDialog(self) + if dialog.exec() == QDialog.DialogCode.Accepted: + password = dialog.get_password() + logging.debug("用户输入了新密码") + else: + logging.debug("用户取消了密码输入") + QMessageBox.warning(self, "取消", "操作已取消") + return # 显示加载状态 self.patch_btn.setEnabled(False) - self.patch_btn.setText("安装中...") + self.patch_btn.setText("突破中...") QApplication.processEvents() try: # 设置环境变量 os.environ['SUDO_PASSWORD'] = password + logging.debug("开始执行补丁安装...") # 执行重置 backend.reset_machine_id(greater_than_0_45) - QMessageBox.information(self, "成功", "补丁安装成功") + + # 直接显示成功消息 + logging.debug("补丁安装完成") + self.show_success_message("补丁安装成功") self.update_status() + break # 成功后退出循环 + except Exception as e: - QMessageBox.warning(self, "错误", f"安装失败: {str(e)}") + logging.error(f"补丁安装过程发生错误: {str(e)}") + # 密码错误,清除保存的密码 + password_manager.save_password("") + QMessageBox.warning(self, "错误", "密码验证失败,请重新输入") + continue # 继续循环,重新输入密码 finally: # 恢复按钮状态 self.patch_btn.setEnabled(True) self.patch_btn.setText("突破0.45.x限制 (Too many free trials问题点这里)") - else: - QMessageBox.warning(self, "取消", "安装操作已取消") except Exception as e: + logging.error(f"安装补丁失败: {str(e)}") QMessageBox.warning(self, "错误", f"安装补丁失败: {str(e)}") def update_cursor(self): """更新Cursor版本""" - # TODO: 实现版本更新逻辑 + # 检查会员状态,未激活则显示激活提示 + if not self.is_member_active: + self.show_activation_dialog() + return + + # 会员已激活,继续执行原有逻辑 QMessageBox.information(self, "提示", "更新功能开发中") if __name__ == "__main__": diff --git a/logger.py b/logger.py index 841b329..2c7e3b4 100644 --- a/logger.py +++ b/logger.py @@ -7,8 +7,8 @@ class PrefixFormatter(logging.Formatter): """自定义格式化器,为 DEBUG 级别日志添加开源项目前缀""" def format(self, record): - if record.levelno == logging.DEBUG: # 只给 DEBUG 级别添加前缀 - record.msg = f"[开源项目:https://github.com/chengazhen/cursor-auto-free] {record.msg}" + if record.levelno == 10: # DEBUG 级别的数值是 10 + record.msg = f"[听泉助手:https://cursorapi.nosqli.com] {record.msg}" return super().format(record) def setup_logger(): @@ -31,17 +31,17 @@ def setup_logger(): # 创建日志记录器 logger = logging.getLogger('CursorHelper') - logger.setLevel(logging.DEBUG) + logger.setLevel(logging.DEBUG) # 设置为DEBUG级别以记录所有日志 # 创建文件处理器 file_handler = logging.FileHandler(log_file, encoding='utf-8') - file_handler.setLevel(logging.DEBUG) + file_handler.setLevel(logging.DEBUG) # 文件处理器也设置为DEBUG级别 file_handler.setFormatter(PrefixFormatter('%(asctime)s - %(levelname)s - %(message)s')) # 创建控制台处理器 console_handler = logging.StreamHandler() - console_handler.setLevel(logging.INFO) - console_handler.setFormatter(PrefixFormatter('%(message)s')) + console_handler.setLevel(logging.DEBUG) # 控制台也输出DEBUG级别的日志 + console_handler.setFormatter(PrefixFormatter('%(levelname)s: %(message)s')) # 添加处理器到日志记录器 logger.addHandler(file_handler) @@ -50,6 +50,7 @@ def setup_logger(): # 记录初始化信息 logger.info('日志系统初始化成功') logger.info(f'日志文件路径: {log_file}') + logger.debug('调试日志已启用') return logger