优化: 统一成功提示弹窗样式,优化会员状态检查

This commit is contained in:
ruisu
2025-02-19 19:16:29 +08:00
parent 8bff4ebdf7
commit 21f85ca6c1
3 changed files with 393 additions and 40 deletions

View File

@@ -399,7 +399,7 @@ def reset_auth_with_password(password: str = None) -> tuple[bool, str]:
auth_manager = CursorAuthManager() auth_manager = CursorAuthManager()
if auth_manager.update_auth(email, access_token, refresh_token): if auth_manager.update_auth(email, access_token, refresh_token):
logging.info("认证信息更新成功") logging.info("认证信息更新成功")
# 重置机器码 # 重置机器码
logging.info("正在重置机器码...") logging.info("正在重置机器码...")

View File

@@ -1,5 +1,6 @@
import sys import sys
import os import os
import json
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
QLabel, QLineEdit, QPushButton, QTextEdit, QMessageBox, QLabel, QLineEdit, QPushButton, QTextEdit, QMessageBox,
QHBoxLayout, QDialog) QHBoxLayout, QDialog)
@@ -22,12 +23,64 @@ except Exception as e:
log_error(f"导入模块错误: {str(e)}\n{traceback.format_exc()}") log_error(f"导入模块错误: {str(e)}\n{traceback.format_exc()}")
raise 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): class SuccessDialog(QDialog):
def __init__(self, message, parent=None): def __init__(self, message, parent=None):
super().__init__(parent) super().__init__(parent)
self.setWindowTitle("激活成功") self.setWindowTitle("激活成功")
self.setFixedSize(400, 300) # 增加窗口大小 self.setFixedSize(400, 300)
self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.FramelessWindowHint) # 移除可能导致问题的窗口标志
self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
# 设置样式 # 设置样式
self.setStyleSheet(""" self.setStyleSheet("""
@@ -136,34 +189,150 @@ class PasswordDialog(QDialog):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.setWindowTitle("输入系统密码") 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 = QVBoxLayout()
layout.setSpacing(15) layout.setSpacing(8)
layout.setContentsMargins(20, 20, 20, 20) 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 = QLineEdit()
self.password_input.setEchoMode(QLineEdit.EchoMode.Password) self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
self.password_input.setPlaceholderText("请输入系统密码") self.password_input.setPlaceholderText("请输入Mac系统管理员密码")
layout.addWidget(self.password_input) 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() button_layout = QHBoxLayout()
ok_button = QPushButton("确定") button_layout.setSpacing(8)
cancel_button = QPushButton("取消") cancel_button = QPushButton("取消")
cancel_button.setObjectName("cancelButton")
ok_button.clicked.connect(self.accept) cancel_button.setCursor(Qt.CursorShape.PointingHandCursor)
cancel_button.clicked.connect(self.reject) 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(cancel_button)
button_layout.addWidget(ok_button)
layout.addSpacing(5)
layout.addLayout(button_layout) layout.addLayout(button_layout)
self.setLayout(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): 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): class CursorGUI(QMainWindow):
def __init__(self): def __init__(self):
@@ -171,6 +340,14 @@ class CursorGUI(QMainWindow):
self.setWindowTitle("听泉Cursor助手 v3.0.2") self.setWindowTitle("听泉Cursor助手 v3.0.2")
self.setFixedSize(600, 600) 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(""" self.setStyleSheet("""
QMainWindow { QMainWindow {
@@ -266,7 +443,7 @@ class CursorGUI(QMainWindow):
layout.addWidget(self.status_text) layout.addWidget(self.status_text)
# 激活区域 # 激活区域
activate_label = QLabel("激活(盈加)会员,多个激活的可盈加整体时长") activate_label = QLabel("激活会员,多个激活码叠加整体时长")
activate_layout = QHBoxLayout() activate_layout = QHBoxLayout()
activate_layout.setSpacing(10) # 设置水平间距 activate_layout.setSpacing(10) # 设置水平间距
self.activate_input = QLineEdit() self.activate_input = QLineEdit()
@@ -348,13 +525,15 @@ class CursorGUI(QMainWindow):
# 获取设备信息 # 获取设备信息
device_info = status_data.get("device_info", {}) if success else {} device_info = status_data.get("device_info", {}) if success else {}
# 设置状态文本 # 设置状态文本和更新全局状态
if success and status_data.get("is_active"): if success and status_data.get("is_active"):
status_emoji = "" status_emoji = ""
status_text = "正常" status_text = "正常"
self.is_member_active = True
else: else:
status_emoji = "" status_emoji = ""
status_text = "未激活" status_text = "未激活"
self.is_member_active = False
# 格式化显示文本 # 格式化显示文本
display_text = f"会员状态:{status_text} {status_emoji}\n" display_text = f"会员状态:{status_text} {status_emoji}\n"
@@ -389,10 +568,36 @@ class CursorGUI(QMainWindow):
clipboard.setText(self.id_text.text()) clipboard.setText(self.id_text.text())
QMessageBox.information(self, "提示", "设备ID已复制到剪贴板") 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): def activate_account(self):
"""激活账号""" """激活账号"""
code = self.activate_input.text().strip() code = self.activate_input.text().strip()
if not code: if not code:
logging.debug("激活码为空,显示警告")
QMessageBox.warning(self, "警告", "请输入激活码") QMessageBox.warning(self, "警告", "请输入激活码")
return return
@@ -402,12 +607,16 @@ class CursorGUI(QMainWindow):
QApplication.processEvents() QApplication.processEvents()
try: try:
logging.debug(f"开始检查激活码: {code}")
account_manager = backend.CursorAccountManager() account_manager = backend.CursorAccountManager()
success, message, account_info = account_manager.check_activation_code(code) success, message, account_info = account_manager.check_activation_code(code)
logging.debug(f"激活码检查结果: success={success}, message={message}, account_info={account_info}")
if success: if success:
# 更新状态显示 logging.debug("激活成功,开始更新状态")
# 更新状态显示和全局状态
self.update_status() self.update_status()
self.is_member_active = True
# 构建成功消息 # 构建成功消息
success_message = ( success_message = (
@@ -417,28 +626,133 @@ class CursorGUI(QMainWindow):
f"总天数:{account_info.get('total_days', 0)}\n" f"总天数:{account_info.get('total_days', 0)}\n"
f"剩余天数:{account_info.get('days_left', 0)}" f"剩余天数:{account_info.get('days_left', 0)}"
) )
logging.debug(f"准备显示成功弹窗,消息内容: {success_message}")
# 显示成功弹窗 # 使用统一的成功提示弹窗
dialog = SuccessDialog(success_message, self) self.show_success_message(success_message)
dialog.exec() logging.debug("成功弹窗显示完成")
# 清空输入框 # 清空输入框
self.activate_input.clear() self.activate_input.clear()
logging.debug("激活流程完成")
else: else:
logging.debug(f"激活失败,显示错误消息: {message}")
QMessageBox.warning(self, "错误", message) QMessageBox.warning(self, "错误", message)
except Exception as e: except Exception as e:
error_msg = f"激活过程出错: {str(e)}\n{traceback.format_exc()}"
logging.error(error_msg)
QMessageBox.warning(self, "错误", f"激活过程出错: {str(e)}") QMessageBox.warning(self, "错误", f"激活过程出错: {str(e)}")
finally: finally:
# 恢复激活按钮状态 # 恢复激活按钮状态
logging.debug("恢复激活按钮状态")
self.activate_btn.setEnabled(True) self.activate_btn.setEnabled(True)
self.activate_btn.setText("激活") 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): def refresh_auth(self):
"""刷新授权""" """刷新授权"""
# 显示密码输入对话框 # 检查会员状态,未激活则显示激活提示
dialog = PasswordDialog(self) if not self.is_member_active:
if dialog.exec() == QDialog.DialogCode.Accepted: self.show_activation_dialog()
password = dialog.get_password() 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) self.refresh_btn.setEnabled(False)
@@ -448,57 +762,95 @@ class CursorGUI(QMainWindow):
try: try:
success, message = backend.reset_auth_with_password(password) success, message = backend.reset_auth_with_password(password)
if success: if success:
QMessageBox.information(self, "成功", "重置成功") self.show_success_message("重置成功")
self.update_status() self.update_status()
break # 成功后退出循环
else: else:
# 密码错误,清除保存的密码
password_manager.save_password("")
QMessageBox.warning(self, "错误", message) QMessageBox.warning(self, "错误", message)
# 继续循环,重新输入密码
except Exception as e: except Exception as e:
QMessageBox.warning(self, "错误", f"重置失败: {str(e)}") QMessageBox.warning(self, "错误", f"重置失败: {str(e)}")
break # 遇到其他错误时退出循环
finally: finally:
# 恢复按钮状态 # 恢复按钮状态
self.refresh_btn.setEnabled(True) self.refresh_btn.setEnabled(True)
self.refresh_btn.setText("刷新Cursor授权 (对话次数用完了提示limit时点一次)") self.refresh_btn.setText("刷新Cursor授权 (对话次数用完了提示limit时点一次)")
else:
QMessageBox.warning(self, "取消", "重置操作已取消")
def install_patch(self): def install_patch(self):
"""安装补丁""" """安装补丁"""
# 检查会员状态,未激活则显示激活提示
if not self.is_member_active:
self.show_activation_dialog()
return
# 会员已激活,继续执行原有逻辑
try: try:
# 检查版本 # 检查版本
greater_than_0_45 = backend.check_cursor_version() greater_than_0_45 = backend.check_cursor_version()
logging.debug(f"Cursor版本检查结果: {'> 0.45' if greater_than_0_45 else '<= 0.45'}")
# 显示密码输入对话框 while True: # 添加循环以支持重试
dialog = PasswordDialog(self) # 先尝试获取保存的密码
if dialog.exec() == QDialog.DialogCode.Accepted: password_manager = PasswordManager()
password = dialog.get_password() 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.setEnabled(False)
self.patch_btn.setText("安装中...") self.patch_btn.setText("突破中...")
QApplication.processEvents() QApplication.processEvents()
try: try:
# 设置环境变量 # 设置环境变量
os.environ['SUDO_PASSWORD'] = password os.environ['SUDO_PASSWORD'] = password
logging.debug("开始执行补丁安装...")
# 执行重置 # 执行重置
backend.reset_machine_id(greater_than_0_45) backend.reset_machine_id(greater_than_0_45)
QMessageBox.information(self, "成功", "补丁安装成功")
# 直接显示成功消息
logging.debug("补丁安装完成")
self.show_success_message("补丁安装成功")
self.update_status() self.update_status()
break # 成功后退出循环
except Exception as e: except Exception as e:
QMessageBox.warning(self, "错误", f"安装失败: {str(e)}") logging.error(f"补丁安装过程发生错误: {str(e)}")
# 密码错误,清除保存的密码
password_manager.save_password("")
QMessageBox.warning(self, "错误", "密码验证失败,请重新输入")
continue # 继续循环,重新输入密码
finally: finally:
# 恢复按钮状态 # 恢复按钮状态
self.patch_btn.setEnabled(True) self.patch_btn.setEnabled(True)
self.patch_btn.setText("突破0.45.x限制 (Too many free trials问题点这里)") self.patch_btn.setText("突破0.45.x限制 (Too many free trials问题点这里)")
else:
QMessageBox.warning(self, "取消", "安装操作已取消")
except Exception as e: except Exception as e:
logging.error(f"安装补丁失败: {str(e)}")
QMessageBox.warning(self, "错误", f"安装补丁失败: {str(e)}") QMessageBox.warning(self, "错误", f"安装补丁失败: {str(e)}")
def update_cursor(self): def update_cursor(self):
"""更新Cursor版本""" """更新Cursor版本"""
# TODO: 实现版本更新逻辑 # 检查会员状态,未激活则显示激活提示
if not self.is_member_active:
self.show_activation_dialog()
return
# 会员已激活,继续执行原有逻辑
QMessageBox.information(self, "提示", "更新功能开发中") QMessageBox.information(self, "提示", "更新功能开发中")
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -7,8 +7,8 @@ class PrefixFormatter(logging.Formatter):
"""自定义格式化器,为 DEBUG 级别日志添加开源项目前缀""" """自定义格式化器,为 DEBUG 级别日志添加开源项目前缀"""
def format(self, record): def format(self, record):
if record.levelno == logging.DEBUG: # 只给 DEBUG 级别添加前缀 if record.levelno == 10: # DEBUG 级别的数值是 10
record.msg = f"[开源项目https://github.com/chengazhen/cursor-auto-free] {record.msg}" record.msg = f"[听泉助手https://cursorapi.nosqli.com] {record.msg}"
return super().format(record) return super().format(record)
def setup_logger(): def setup_logger():
@@ -31,17 +31,17 @@ def setup_logger():
# 创建日志记录器 # 创建日志记录器
logger = logging.getLogger('CursorHelper') logger = logging.getLogger('CursorHelper')
logger.setLevel(logging.DEBUG) logger.setLevel(logging.DEBUG) # 设置为DEBUG级别以记录所有日志
# 创建文件处理器 # 创建文件处理器
file_handler = logging.FileHandler(log_file, encoding='utf-8') 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')) file_handler.setFormatter(PrefixFormatter('%(asctime)s - %(levelname)s - %(message)s'))
# 创建控制台处理器 # 创建控制台处理器
console_handler = logging.StreamHandler() console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO) console_handler.setLevel(logging.DEBUG) # 控制台也输出DEBUG级别的日志
console_handler.setFormatter(PrefixFormatter('%(message)s')) console_handler.setFormatter(PrefixFormatter('%(levelname)s: %(message)s'))
# 添加处理器到日志记录器 # 添加处理器到日志记录器
logger.addHandler(file_handler) logger.addHandler(file_handler)
@@ -50,6 +50,7 @@ def setup_logger():
# 记录初始化信息 # 记录初始化信息
logger.info('日志系统初始化成功') logger.info('日志系统初始化成功')
logger.info(f'日志文件路径: {log_file}') logger.info(f'日志文件路径: {log_file}')
logger.debug('调试日志已启用')
return logger return logger