From e30c74007be39ca66080ff13386efd4e05cd7dba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=BF=97=E5=BC=BA?= <357099073@qq.com> Date: Mon, 11 May 2026 14:27:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __pycache__/main.cpython-313.pyc | Bin 93016 -> 111803 bytes layout/main1.ui | 991 +++++++++++++++++++++++++++++++ main.py | 248 +++++++- 3 files changed, 1233 insertions(+), 6 deletions(-) create mode 100644 layout/main1.ui diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index 47af507ce01efe0001a153c364e35f0f82221919..3d647d5593d8db0689bd8ec4dc02cd458dfe4f4d 100644 GIT binary patch delta 30148 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

!8pB59Y|i4F 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 D+d-C* delta 17662 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 + + MainWindow + + + + 0 + 0 + 900 + 650 + + + + 牛马Cursor登录器 + + + + + 8 + + + 8 + + + 8 + + + 8 + + + + + Qt::Orientation::Vertical + + + false + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Horizontal + + + false + + + + + 150 + 0 + + + + + 220 + 16777215 + + + + QListWidget { + border: 1px solid palette(mid); + border-radius: 4px; + background: palette(base); + color: palette(text); + outline: none; + } + QListWidget::item { + min-height: 38px; + padding: 8px 12px; + border-bottom: 1px solid palette(midlight); + } + QListWidget::item:selected { + color: palette(highlighted-text); + background: palette(highlight); + } + QListWidget::item:hover:!selected { + background: palette(alternate-base); + } + + + 0 + + + + Token切换 + + + + + 一键续杯 + + + + + Cursor 配置 + + + + + 帮助 + + + + + + 0 + + + + + + + Token 输入 + + + + + + 请粘贴 Cursor Token 到这里... + + + + + + + + + + + + + 0 + 50 + + + + + 16777215 + 50 + + + + + 12 + true + + + + QPushButton { + background-color: #0078D4; + color: white; + border-radius: 5px; + } + QPushButton:hover { + background-color: #106EBE; + } + QPushButton:pressed { + background-color: #005A9E; + } + + + 🚀 开始换号 + + + + + + + + 160 + 50 + + + + + 220 + 50 + + + + + 12 + true + + + + QPushButton { + background-color: #FF9800; + color: white; + border-radius: 5px; + } + QPushButton:hover { + background-color: #F57C00; + } + QPushButton:pressed { + background-color: #E65100; + } + + + 在线购买Token + + + + + + + + + + + + + 设备号 + + + + + + true + + + 设备号将在程序启动后自动生成... + + + + + + + + 88 + 0 + + + + 复制 + + + + + + + + + + 激活码 + + + + + + 激活码: + + + + + + + 请输入激活码... + + + + + + + + 88 + 0 + + + + 激活 + + + + + + + + 88 + 0 + + + + 在线购买 + + + + + + + + + + 会员状态 + + + + + + + + 当前状态:未刷新 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 88 + 0 + + + + 刷新 + + + + + + + + + + + 会员等级: + + + + + + + - + + + + + + + 账号类型: + + + + + + + - + + + + + + + 激活时间: + + + + + + + - + + + + + + + 到期时间: + + + + + + + - + + + + + + + + + + + + + + + 444 + 48 + + + + + 16777215 + 48 + + + + + 12 + true + + + + QPushButton { + background-color: #D83B01; + color: white; + border-radius: 5px; + } + QPushButton:hover { + background-color: #C23900; + } + QPushButton:pressed { + background-color: #A4262C; + } + + + 一键续杯 + + + + + + + + + color: #D83B01; font-weight: bold; + + + 请用完后再点击续杯,如发现大量未使用完就续杯情况,一律封号处理,请悉知! + + + true + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + Cursor 路径 + + + + + + 路径: + + + + + + + 请选择 Cursor 安装路径... + + + + + + + 浏览 + + + + + + + + 88 + 0 + + + + 自动查找 + + + + + + + + + + + 0 + 40 + + + + 打开 Cursor + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 140 + 0 + + + + + 190 + 16777215 + + + + QListWidget { + border: 1px solid palette(mid); + border-radius: 4px; + background: palette(base); + color: palette(text); + outline: none; + } + QListWidget::item { + min-height: 34px; + padding: 8px 12px; + border-bottom: 1px solid palette(midlight); + } + QListWidget::item:selected { + color: palette(highlighted-text); + background: palette(highlight); + } + QListWidget::item:hover:!selected { + background: palette(alternate-base); + } + + + 0 + + + + 应急检修 + + + + + 使用说明 + + + + + 捐赠支持 + + + + + 关于软件 + + + + + + + + 0 + + + + + + + 应急检修 + + + + + + 打开应急检修工具面板,可进行 DB Browser 下载、清除 Cursor 缓存、Token 提取等操作。 + + + true + + + + + + + + 0 + 42 + + + + 打开应急检修 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + 使用说明 + + + + + + 查看软件使用说明图片,支持滚轮查看和缩放。 + + + true + + + + + + + + 0 + 42 + + + + 打开使用说明 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + 捐赠支持 + + + + + + 打开捐赠支持窗口,查看微信和支付宝捐赠二维码。 + + + true + + + + + + + + 0 + 42 + + + + 打开捐赠支持 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + 关于软件 + + + + + + 查看当前软件版本、用途和交流群信息。 + + + true + + + + + + + + 0 + 42 + + + + 查看关于软件 + + + + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + 0 + 140 + + + + 日志展示 + + + + + + true + + + 日志输出... + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 88 + 0 + + + + 检查更新 + + + + + + + + 88 + 0 + + + + 清空日志 + + + + + + + + + + + + + + + 0 + 0 + 900 + 33 + + + + + 设置 + + + + + + + + true + + + + + 退出 + + + + + 应急检修 + + + + + 使用说明 + + + + + 捐赠支持 + + + + + 关于软件 + + + + + + + tabList + currentRowChanged(int) + contentStack + setCurrentIndex(int) + + + 93 + 120 + + + 420 + 120 + + + + + helpTabList + currentRowChanged(int) + helpContentStack + setCurrentIndex(int) + + + 220 + 180 + + + 520 + 180 + + + + + \ No newline at end of file diff --git a/main.py b/main.py index 151e385..d759d5e 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,7 @@ import sys import json import base64 +import hashlib import shutil import uuid import random @@ -20,7 +21,7 @@ from datetime import datetime from pathlib import Path from typing import Optional -__VERSION__ = "0.0.6" +__VERSION__ = "0.0.7" from PySide6.QtWidgets import ( QApplication, @@ -42,7 +43,7 @@ from PySide6.QtWidgets import ( ) from PySide6.QtCore import QThread, Signal, Qt, QTimer, QMetaObject, Q_ARG, Slot from PySide6.QtUiTools import QUiLoader -from PySide6.QtGui import QFont, QPixmap, QColor, QPainter, QPalette, QIcon +from PySide6.QtGui import QFont, QPixmap, QColor, QPainter, QPalette, QIcon, QAction def get_resource_path(relative_path): @@ -147,6 +148,104 @@ def generate_machine_id(): return str(uuid.uuid4()) +def _run_command_text(args): + """执行系统命令并返回文本输出,失败时返回空字符串。""" + try: + creationflags = getattr(subprocess, "CREATE_NO_WINDOW", 0) if sys.platform == "win32" else 0 + return subprocess.check_output( + args, + stderr=subprocess.DEVNULL, + stdin=subprocess.DEVNULL, + text=True, + encoding="utf-8", + errors="ignore", + creationflags=creationflags, + ).strip() + except Exception: + return "" + + +def _get_wmic_value(alias: str, field: str) -> str: + """通过 WMIC 获取硬件字段值,兼容空值/表头输出。""" + output = _run_command_text(["wmic", alias, "get", field, "/value"]) + for line in output.splitlines(): + line = line.strip() + prefix = f"{field}=" + if line.startswith(prefix): + value = line[len(prefix):].strip() + if value and value.lower() not in ("none", "null", "to be filled by o.e.m."): + return value + + output = _run_command_text(["wmic", alias, "get", field]) + lines = [line.strip() for line in output.splitlines() if line.strip()] + for line in lines[1:]: + if line and line.lower() not in ("none", "null", "to be filled by o.e.m."): + return line + return "" + + +def _get_windows_cim_value(class_name: str, field: str) -> str: + """WMIC 不可用时,通过 PowerShell CIM 获取硬件字段值。""" + command = ( + f"(Get-CimInstance {class_name} | " + f"Select-Object -First 1 -ExpandProperty {field})" + ) + value = _run_command_text([ + "powershell", + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command", + command, + ]).strip() + if value and value.lower() not in ("none", "null", "to be filled by o.e.m."): + return value + return "" + + +def _get_cpu_id() -> str: + """获取 CPU 标识。""" + if sys.platform == "win32": + return ( + _get_wmic_value("cpu", "ProcessorId") + or _get_windows_cim_value("Win32_Processor", "ProcessorId") + or os.environ.get("PROCESSOR_IDENTIFIER", "").strip() + ) + return os.environ.get("PROCESSOR_IDENTIFIER", "").strip() or os.uname().machine + + +def _get_baseboard_id() -> str: + """获取主板标识。""" + if sys.platform == "win32": + serial = ( + _get_wmic_value("baseboard", "SerialNumber") + or _get_windows_cim_value("Win32_BaseBoard", "SerialNumber") + ) + product = ( + _get_wmic_value("baseboard", "Product") + or _get_windows_cim_value("Win32_BaseBoard", "Product") + ) + manufacturer = ( + _get_wmic_value("baseboard", "Manufacturer") + or _get_windows_cim_value("Win32_BaseBoard", "Manufacturer") + ) + return "|".join(part for part in (manufacturer, product, serial) if part) + return socket.gethostname() + + +def get_renewal_device_id(): + """生成用于一键续杯的本机设备号:CPU + MAC + 主板。""" + try: + cpu_id = _get_cpu_id() + mac = f"{uuid.getnode():012x}" + baseboard_id = _get_baseboard_id() + raw = f"cpu={cpu_id}|mac={mac}|baseboard={baseboard_id}" + digest = hashlib.sha256(raw.encode("utf-8")).digest() + return base64.urlsafe_b64encode(digest).decode("ascii").rstrip("=") + except Exception: + return str(uuid.uuid4()) + + def get_cursor_config_path(): home = Path.home() if sys.platform == "win32": @@ -727,7 +826,7 @@ class MainWindow(QMainWindow): self._splash_pulse("正在加载") # 获取UI文件路径 - ui_path = get_resource_path(os.path.join("layout", "main.ui")) + ui_path = get_resource_path(os.path.join("layout", "main1.ui")) # 详细的调试信息 debug_info = f"UI文件路径: {ui_path}\n" @@ -802,6 +901,8 @@ class MainWindow(QMainWindow): self.txtToken = self.findChild(QTextEdit, "txtToken") self.txtLog = self.findChild(QTextEdit, "txtLog") self.txtCursorPath = self.findChild(QLineEdit, "txtCursorPath") + self.txtDeviceId = self.findChild(QLineEdit, "txtDeviceId") + self.txtActivationCode = self.findChild(QLineEdit, "txtActivationCode") self.btnChange = self.findChild(QPushButton, "btnChange") self.btnOpenCursor = self.findChild(QPushButton, "btnOpenCursor") self.btnOnlineShop = self.findChild(QPushButton, "btnOnlineShop") @@ -813,6 +914,22 @@ class MainWindow(QMainWindow): self.btnCheckUpdate = self.findChild(QPushButton, "btnCheckUpdate") self.btnEmergencyRepair = self.findChild(QPushButton, "btnEmergencyRepair") self.btnUsageGuide = self.findChild(QPushButton, "btnUsageGuide") + self.btnAbout = self.findChild(QPushButton, "btnAbout") + self.btnCopyDeviceId = self.findChild(QPushButton, "btnCopyDeviceId") + self.btnActivateRenewal = self.findChild(QPushButton, "btnActivateRenewal") + self.btnBuyActivationCode = self.findChild(QPushButton, "btnBuyActivationCode") + self.btnRefreshMemberStatus = self.findChild(QPushButton, "btnRefreshMemberStatus") + self.btnOneClickRenewal = self.findChild(QPushButton, "btnOneClickRenewal") + self.lblMemberStatus = self.findChild(QLabel, "lblMemberStatus") + self.lblMemberLevel = self.findChild(QLabel, "lblMemberLevel") + self.lblAccountType = self.findChild(QLabel, "lblAccountType") + self.lblActivatedAt = self.findChild(QLabel, "lblActivatedAt") + self.lblExpiredAt = self.findChild(QLabel, "lblExpiredAt") + self.actionExit = self.findChild(QAction, "actionExit") + self.actionEmergencyRepair = self.findChild(QAction, "actionEmergencyRepair") + self.actionUsageGuide = self.findChild(QAction, "actionUsageGuide") + self.actionDonate = self.findChild(QAction, "actionDonate") + self.actionAbout = self.findChild(QAction, "actionAbout") self._load_cached_logs_to_ui() # 调试信息 @@ -829,15 +946,21 @@ class MainWindow(QMainWindow): # 设置默认Cursor路径 if self.txtCursorPath: self.txtCursorPath.setText(get_default_cursor_path()) + if self.txtDeviceId: + self.txtDeviceId.setText(get_renewal_device_id()) + self._reset_member_status_display() # 为在线商城按钮设置图标(使用 Qt 内置图标,避免依赖外部资源) - if self.btnOnlineShop: + if self.btnOnlineShop or self.btnBuyActivationCode: shop_icon = QIcon.fromTheme("shopping-cart") if shop_icon.isNull(): shop_icon = QIcon.fromTheme("emblem-sales") if shop_icon.isNull(): shop_icon = self.style().standardIcon(QStyle.SP_DialogOpenButton) - self.btnOnlineShop.setIcon(shop_icon) + if self.btnOnlineShop: + self.btnOnlineShop.setIcon(shop_icon) + if self.btnBuyActivationCode: + self.btnBuyActivationCode.setIcon(shop_icon) # 信号连接 if self.btnChange: @@ -862,6 +985,28 @@ class MainWindow(QMainWindow): self.btnEmergencyRepair.clicked.connect(self.on_emergency_repair_clicked) if self.btnUsageGuide: self.btnUsageGuide.clicked.connect(self.on_usage_guide_clicked) + if self.btnAbout: + self.btnAbout.clicked.connect(self.on_about_clicked) + if self.btnCopyDeviceId: + self.btnCopyDeviceId.clicked.connect(self.on_copy_device_id_clicked) + if self.btnActivateRenewal: + self.btnActivateRenewal.clicked.connect(self.on_activate_renewal_clicked) + if self.btnBuyActivationCode: + self.btnBuyActivationCode.clicked.connect(self.on_open_online_shop_clicked) + if self.btnRefreshMemberStatus: + self.btnRefreshMemberStatus.clicked.connect(self.on_refresh_member_status_clicked) + if self.btnOneClickRenewal: + self.btnOneClickRenewal.clicked.connect(self.on_one_click_renewal_clicked) + if self.actionExit: + self.actionExit.triggered.connect(self.close) + if self.actionEmergencyRepair: + self.actionEmergencyRepair.triggered.connect(self.on_emergency_repair_clicked) + if self.actionUsageGuide: + self.actionUsageGuide.triggered.connect(self.on_usage_guide_clicked) + if self.actionDonate: + self.actionDonate.triggered.connect(self.on_donate_clicked) + if self.actionAbout: + self.actionAbout.triggered.connect(self.on_about_clicked) # 设置状态栏:左侧显示QQ群,右侧显示版本号 qq_icon_label = QLabel() qq_icon = QIcon.fromTheme("im-qq") @@ -1232,6 +1377,86 @@ class MainWindow(QMainWindow): self.log(f"❌ 打开AI中转站失败: {str(e)}") QMessageBox.warning(self, "警告", f"打开AI中转站失败: {str(e)}") + def on_copy_device_id_clicked(self): + """复制一键续杯设备号。""" + device_id = self.txtDeviceId.text().strip() if self.txtDeviceId else "" + if not device_id: + QMessageBox.warning(self, "提示", "设备号为空,无法复制。") + return + QApplication.clipboard().setText(device_id) + self.log("✅ 设备号已复制到剪贴板") + QMessageBox.information(self, "成功", "设备号已复制到剪贴板。") + + def on_activate_renewal_clicked(self): + """提交一键续杯激活码。""" + activation_code = self.txtActivationCode.text().strip() if self.txtActivationCode else "" + device_id = self.txtDeviceId.text().strip() if self.txtDeviceId else "" + if not activation_code: + QMessageBox.warning(self, "提示", "请输入激活码。") + return + self.log(f"🔑 正在激活一键续杯,设备号:{device_id}") + self.log("ℹ️ 激活码已提交,请根据服务端接口补充实际激活逻辑。") + QMessageBox.information(self, "提示", "激活码已提交,当前版本尚未配置服务端激活接口。") + self.on_refresh_member_status_clicked() + + def _reset_member_status_display(self): + """重置会员状态展示。""" + if self.lblMemberStatus: + self.lblMemberStatus.setText("当前状态:未刷新") + if self.lblMemberLevel: + self.lblMemberLevel.setText("-") + if self.lblAccountType: + self.lblAccountType.setText("-") + if self.lblActivatedAt: + self.lblActivatedAt.setText("-") + if self.lblExpiredAt: + self.lblExpiredAt.setText("-") + + def on_refresh_member_status_clicked(self): + """刷新会员状态展示。""" + device_id = self.txtDeviceId.text().strip() if self.txtDeviceId else "" + if not device_id: + QMessageBox.warning(self, "提示", "设备号为空,无法刷新会员状态。") + return + + if self.lblMemberStatus: + self.lblMemberStatus.setText("当前状态:待接入服务端") + if self.lblMemberLevel: + self.lblMemberLevel.setText("未激活") + if self.lblAccountType: + self.lblAccountType.setText("普通账号") + if self.lblActivatedAt: + self.lblActivatedAt.setText("-") + if self.lblExpiredAt: + self.lblExpiredAt.setText("-") + + self.log("🔄 已刷新会员状态展示,当前版本尚未配置服务端查询接口。") + + def on_one_click_renewal_clicked(self): + """执行一键续杯。""" + device_id = self.txtDeviceId.text().strip() if self.txtDeviceId else "" + if not device_id: + QMessageBox.warning(self, "提示", "设备号为空,无法执行一键续杯。") + return + + confirm = QMessageBox.question( + self, + "确认一键续杯", + "请确认当前额度已经用完后再续杯。\n\n" + "如发现大量未使用完就续杯情况,一律封号处理,请悉知!\n\n" + "确定要继续执行一键续杯吗?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No, + ) + if confirm != QMessageBox.Yes: + self.log("ℹ️ 已取消一键续杯。") + return + + self.log(f"☕ 正在执行一键续杯,设备号:{device_id}") + self.log("ℹ️ 一键续杯请求已提交,请根据服务端接口补充实际续杯逻辑。") + QMessageBox.information(self, "提示", "一键续杯请求已提交,当前版本尚未配置服务端续杯接口。") + self.on_refresh_member_status_clicked() + def _extract_session_token(self, raw_value: str) -> str: """从输入文本中提取 SessionToken,兼容纯 token / Cookie 串。""" s = (raw_value or "").strip().strip('"').strip("'") @@ -1344,6 +1569,17 @@ class MainWindow(QMainWindow): dialog = DonateDialog(self) dialog.exec() + def on_about_clicked(self): + """显示关于软件信息。""" + QMessageBox.about( + self, + "关于软件", + f"牛马Cursor登录器\n\n" + f"当前版本:{__VERSION__}\n\n" + "用于 Cursor 账号登录、配置管理和常用工具操作。\n\n" + "QQ群:720797421" + ) + def on_usage_guide_clicked(self): """打开使用说明图片(非模态,可与其他窗口并行)。""" if self._usage_guide_dialog and self._usage_guide_dialog.isVisible(): @@ -1735,4 +1971,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main()