diff --git a/__pycache__/common_utils.cpython-312.pyc b/__pycache__/common_utils.cpython-312.pyc
index 32e7117..db7aba5 100644
Binary files a/__pycache__/common_utils.cpython-312.pyc and b/__pycache__/common_utils.cpython-312.pyc differ
diff --git a/__pycache__/cursor_token_refresher.cpython-312.pyc b/__pycache__/cursor_token_refresher.cpython-312.pyc
index 0aa17b5..6573607 100644
Binary files a/__pycache__/cursor_token_refresher.cpython-312.pyc and b/__pycache__/cursor_token_refresher.cpython-312.pyc differ
diff --git a/__pycache__/machine_resetter.cpython-312.pyc b/__pycache__/machine_resetter.cpython-312.pyc
index ee26c7d..86fd17e 100644
Binary files a/__pycache__/machine_resetter.cpython-312.pyc and b/__pycache__/machine_resetter.cpython-312.pyc differ
diff --git a/__pycache__/update_disabler.cpython-312.pyc b/__pycache__/update_disabler.cpython-312.pyc
index c95f316..cacfb78 100644
Binary files a/__pycache__/update_disabler.cpython-312.pyc and b/__pycache__/update_disabler.cpython-312.pyc differ
diff --git a/build.bat b/build.bat
index e5795cc..7cea7c2 100644
--- a/build.bat
+++ b/build.bat
@@ -27,7 +27,21 @@ set VERSION=!VERSION: =!
for /f "tokens=1-3 delims=." %%a in ("!VERSION!") do (
set MAJOR=%%a
set MINOR=%%b
- set /a PATCH=%%c+1
+ set PATCH=%%c
+
+ :: 检查PATCH是否为9,如果是则增加MINOR版本并重置PATCH为0
+ if "!PATCH!"=="9" (
+ set /a MINOR=!MINOR!+1
+ set PATCH=0
+
+ :: 检查MINOR是否为10,如果是则增加MAJOR版本并重置MINOR为0
+ if "!MINOR!"=="10" (
+ set /a MAJOR=!MAJOR!+1
+ set MINOR=0
+ )
+ ) else (
+ set /a PATCH=!PATCH!+1
+ )
)
:: 组合新版本号
diff --git a/common_utils.py b/common_utils.py
index 87ec8d2..9aab94b 100644
--- a/common_utils.py
+++ b/common_utils.py
@@ -400,7 +400,7 @@ def activate_device(machine_id: str, activation_code: str) -> Tuple[bool, str, O
return True, f"激活成功,剩余{state_data['days_left']}天", state_data
elif result.get("code") == 400:
- error_msg = result.get("msg", "激活码无效或已被使用")
+ error_msg = result.get("msg", "激活码已使用完,请勿重复使用")
_logger.warning(f"激活失败: {error_msg}")
return False, error_msg, None
diff --git a/config.py b/config.py
index be1eb61..2d8313d 100644
--- a/config.py
+++ b/config.py
@@ -6,7 +6,8 @@ class Config:
def __init__(self):
"""初始化配置"""
# API基础URL
- self.base_url = "https://cursorapi.nosqli.com"
+ self.base_url = "https://api.cursorpro.com.cn"
+ #self.base_url = "https://cursorapi.nosqli.com"
# API端点
self.api_endpoints = {
diff --git a/cursor_db_export.py b/cursor_db_export.py
new file mode 100644
index 0000000..87ace9c
--- /dev/null
+++ b/cursor_db_export.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sqlite3
+import os
+import sys
+import json
+import csv
+import datetime
+
+def export_cursor_tables():
+ """导出Cursor数据库中的ItemTable和cursorDiskKV表"""
+ print("Cursor 数据库表导出工具")
+ print("-" * 50)
+
+ # 获取数据库路径
+ if sys.platform == "win32":
+ appdata = os.getenv("APPDATA")
+ if appdata is None:
+ print("错误: APPDATA 环境变量未设置!")
+ return
+ db_path = os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb")
+ else:
+ print("目前只支持Windows系统")
+ return
+
+ if not os.path.exists(db_path):
+ print(f"错误: Cursor数据库文件不存在: {db_path}")
+ return
+
+ print(f"使用Cursor数据库: {db_path}")
+
+ # 创建导出目录
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
+ export_dir = f"cursor_db_export_{timestamp}"
+ os.makedirs(export_dir, exist_ok=True)
+ print(f"将导出数据到目录: {export_dir}")
+
+ try:
+ # 连接到数据库
+ conn = sqlite3.connect(db_path)
+ cursor = conn.cursor()
+
+ tables_to_export = ["ItemTable", "cursorDiskKV"]
+
+ for table_name in tables_to_export:
+ print(f"\n正在导出表 '{table_name}'...")
+
+ # 检查表是否存在
+ cursor.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';")
+ if not cursor.fetchone():
+ print(f"表 '{table_name}' 不存在!")
+ continue
+
+ # 获取表的列名
+ cursor.execute(f"PRAGMA table_info({table_name});")
+ columns = cursor.fetchall()
+ col_names = [col[1] for col in columns]
+
+ # 获取所有数据
+ cursor.execute(f"SELECT * FROM {table_name};")
+ rows = cursor.fetchall()
+
+ if not rows:
+ print(f"表 '{table_name}' 中没有数据")
+ continue
+
+ record_count = len(rows)
+ print(f"找到 {record_count} 条记录")
+
+ # 导出为JSON
+ json_file = os.path.join(export_dir, f"{table_name}.json")
+ with open(json_file, 'w', encoding='utf-8') as f:
+ json_data = []
+ for row in rows:
+ row_dict = {}
+ for i, col_name in enumerate(col_names):
+ # 处理特殊类型
+ if isinstance(row[i], bytes):
+ try:
+ # 尝试解码为JSON
+ row_dict[col_name] = json.loads(row[i])
+ except:
+ # 如果失败则使用hex表示
+ row_dict[col_name] = row[i].hex()
+ else:
+ row_dict[col_name] = row[i]
+ json_data.append(row_dict)
+ json.dump(json_data, f, ensure_ascii=False, indent=2)
+ print(f"已导出JSON: {json_file}")
+
+ # 导出为CSV
+ csv_file = os.path.join(export_dir, f"{table_name}.csv")
+ with open(csv_file, 'w', encoding='utf-8', newline='') as f:
+ csv_writer = csv.writer(f)
+ csv_writer.writerow(col_names) # 写入表头
+ for row in rows:
+ # 处理每个字段,确保CSV可以正确处理
+ processed_row = []
+ for item in row:
+ if isinstance(item, bytes):
+ try:
+ # 尝试解码为字符串
+ processed_row.append(str(item))
+ except:
+ # 如果失败则使用hex表示
+ processed_row.append(item.hex())
+ else:
+ processed_row.append(item)
+ csv_writer.writerow(processed_row)
+ print(f"已导出CSV: {csv_file}")
+
+ # 导出为SQL插入语句
+ sql_file = os.path.join(export_dir, f"{table_name}.sql")
+ with open(sql_file, 'w', encoding='utf-8') as f:
+ f.write(f"-- 导出自 {db_path}\n")
+ f.write(f"-- 表: {table_name}\n")
+ f.write(f"-- 时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
+ f.write(f"-- 记录数: {record_count}\n\n")
+
+ # 获取表结构
+ cursor.execute(f"SELECT sql FROM sqlite_master WHERE type='table' AND name='{table_name}';")
+ table_sql = cursor.fetchone()[0]
+ f.write(f"{table_sql};\n\n")
+
+ # 写入INSERT语句
+ for row in rows:
+ values = []
+ for item in row:
+ if item is None:
+ values.append("NULL")
+ elif isinstance(item, (int, float)):
+ values.append(str(item))
+ elif isinstance(item, bytes):
+ hex_value = ''.join([f'\\x{b:02x}' for b in item])
+ values.append(f"X'{item.hex()}'")
+ else:
+ # 转义字符串中的引号
+ item_str = str(item).replace("'", "''")
+ values.append(f"'{item_str}'")
+
+ f.write(f"INSERT INTO {table_name} ({', '.join(col_names)}) VALUES ({', '.join(values)});\n")
+ print(f"已导出SQL: {sql_file}")
+
+ # 如果是cursorDiskKV表,尝试解析其中的JSON数据
+ if table_name == "cursorDiskKV":
+ json_parsed_file = os.path.join(export_dir, f"{table_name}_parsed.json")
+ with open(json_parsed_file, 'w', encoding='utf-8') as f:
+ parsed_data = {}
+ for row in rows:
+ # 通常cursorDiskKV表有key和value两列
+ key = row[0] if isinstance(row[0], str) else str(row[0])
+ value = row[1]
+
+ # 尝试解析value为JSON
+ if isinstance(value, bytes):
+ try:
+ parsed_value = json.loads(value)
+ parsed_data[key] = parsed_value
+ except:
+ parsed_data[key] = value.hex()
+ elif isinstance(value, str):
+ try:
+ parsed_value = json.loads(value)
+ parsed_data[key] = parsed_value
+ except:
+ parsed_data[key] = value
+ else:
+ parsed_data[key] = value
+
+ json.dump(parsed_data, f, ensure_ascii=False, indent=2)
+ print(f"已导出解析后的JSON: {json_parsed_file}")
+
+ conn.close()
+ print(f"\n导出完成!所有数据已保存到目录: {os.path.abspath(export_dir)}")
+
+ except sqlite3.Error as e:
+ print(f"SQLite 错误: {e}")
+ except Exception as e:
+ print(f"发生错误: {e}")
+ import traceback
+ traceback.print_exc()
+
+if __name__ == "__main__":
+ export_cursor_tables()
\ No newline at end of file
diff --git a/cursor_db_viewer.py b/cursor_db_viewer.py
new file mode 100644
index 0000000..1e5f0b3
--- /dev/null
+++ b/cursor_db_viewer.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sqlite3
+import os
+import sys
+
+def view_cursor_db():
+ """查看Cursor数据库内容"""
+ print("Cursor SQLite3 数据库查看器")
+ print("-" * 50)
+
+ # 获取数据库路径
+ if sys.platform == "win32":
+ appdata = os.getenv("APPDATA")
+ if appdata is None:
+ print("错误: APPDATA 环境变量未设置!")
+ return
+ db_path = os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb")
+ else:
+ print("目前只支持Windows系统")
+ return
+
+ if not os.path.exists(db_path):
+ print(f"错误: Cursor数据库文件不存在: {db_path}")
+ return
+
+ print(f"使用Cursor数据库: {db_path}")
+
+ try:
+ # 连接到数据库
+ conn = sqlite3.connect(db_path)
+ cursor = conn.cursor()
+
+ while True:
+ print("\n" + "-" * 50)
+ print("可用操作:")
+ print("1. 显示所有表")
+ print("2. 查看表结构")
+ print("3. 查看表数据")
+ print("4. 执行自定义SQL查询")
+ print("5. 退出")
+
+ choice = input("\n请选择操作 (1-5): ").strip()
+
+ if choice == "1":
+ # 显示所有表
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
+ tables = cursor.fetchall()
+
+ if not tables:
+ print("数据库中没有表")
+ else:
+ print(f"\n数据库中的表 ({len(tables)}):")
+ for i, table in enumerate(tables):
+ print(f"{i+1}. {table[0]}")
+
+ elif choice == "2":
+ # 查看表结构
+ table_name = input("请输入表名: ").strip()
+ cursor.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';")
+ if not cursor.fetchone():
+ print(f"表 '{table_name}' 不存在!")
+ continue
+
+ cursor.execute(f"PRAGMA table_info({table_name});")
+ columns = cursor.fetchall()
+ print(f"\n表 '{table_name}' 的结构:")
+ print("-" * 50)
+ print(f"{'序号':5} {'列名':20} {'类型':15} {'是否可空':10} {'默认值':15}")
+ print("-" * 70)
+ for col in columns:
+ col_id, name, type_, notnull, default_val, pk = col
+ print(f"{col_id:5} {name:20} {type_:15} {'否' if notnull else '是':10} {str(default_val)[:15]:15}")
+
+ elif choice == "3":
+ # 查看表数据
+ table_name = input("请输入表名: ").strip()
+ cursor.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';")
+ if not cursor.fetchone():
+ print(f"表 '{table_name}' 不存在!")
+ continue
+
+ limit = input("显示多少条记录 (默认10): ").strip() or "10"
+ try:
+ limit = int(limit)
+ except ValueError:
+ print("请输入有效数字!")
+ continue
+
+ cursor.execute(f"SELECT COUNT(*) FROM {table_name};")
+ total = cursor.fetchone()[0]
+ print(f"\n表 '{table_name}' 共有 {total} 条记录,显示前 {limit} 条:")
+
+ if total == 0:
+ print("表中没有数据")
+ continue
+
+ # 获取列名
+ cursor.execute(f"PRAGMA table_info({table_name});")
+ columns = cursor.fetchall()
+ col_names = [col[1] for col in columns]
+
+ # 显示数据
+ cursor.execute(f"SELECT * FROM {table_name} LIMIT {limit};")
+ rows = cursor.fetchall()
+
+ # 打印列名
+ header = " | ".join(col_names)
+ print("\n" + header)
+ print("-" * len(header))
+
+ # 打印数据
+ for row in rows:
+ row_str = []
+ for item in row:
+ item_str = str(item)
+ if len(item_str) > 20:
+ item_str = item_str[:17] + "..."
+ row_str.append(item_str)
+ print(" | ".join(row_str))
+
+ elif choice == "4":
+ # 执行自定义SQL
+ sql = input("请输入SQL查询语句: ").strip()
+ if not sql:
+ continue
+
+ try:
+ cursor.execute(sql)
+ if sql.lower().startswith(("select", "pragma")):
+ rows = cursor.fetchall()
+ if not rows:
+ print("查询返回0条结果")
+ continue
+
+ # 获取列名
+ col_names = [description[0] for description in cursor.description]
+
+ # 打印列名
+ header = " | ".join(col_names)
+ print("\n" + header)
+ print("-" * len(header))
+
+ # 最多显示50条
+ display_rows = rows[:50]
+ for row in display_rows:
+ row_str = []
+ for item in row:
+ item_str = str(item)
+ if len(item_str) > 20:
+ item_str = item_str[:17] + "..."
+ row_str.append(item_str)
+ print(" | ".join(row_str))
+
+ if len(rows) > 50:
+ print(f"...(还有 {len(rows)-50} 条记录)")
+
+ print(f"\n总共 {len(rows)} 条记录")
+ else:
+ conn.commit()
+ print("查询执行成功")
+ except sqlite3.Error as e:
+ print(f"SQL错误: {e}")
+
+ elif choice == "5":
+ # 退出
+ break
+
+ else:
+ print("无效选择,请输入1-5之间的数字")
+
+ conn.close()
+ print("\n已关闭数据库连接。再见!")
+
+ except sqlite3.Error as e:
+ print(f"SQLite 错误: {e}")
+ except Exception as e:
+ print(f"发生错误: {e}")
+
+if __name__ == "__main__":
+ view_cursor_db()
\ No newline at end of file
diff --git a/cursor_token_refresher.py b/cursor_token_refresher.py
index cbbe4e1..179fec1 100644
--- a/cursor_token_refresher.py
+++ b/cursor_token_refresher.py
@@ -37,7 +37,8 @@ class CursorTokenRefresher:
"""
updates = []
# 登录状态
- updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
+ updates.append(("cursorAuth/cachedSignUpType", "Google"))
+ updates.append(("cursorAuth/stripeMembershipType", "free"))
if email is not None:
updates.append(("cursorAuth/cachedEmail", email))
@@ -116,7 +117,8 @@ class CursorTokenRefresher:
refresh_token = account_data.get("refresh_token")
expire_time = account_data.get("expire_time")
days_left = account_data.get("days_left")
-
+ password = account_data.get("password")
+
if not all([email, access_token, refresh_token]):
return False, "获取账号信息不完整", None
@@ -126,6 +128,7 @@ class CursorTokenRefresher:
refresh_token=refresh_token):
account_info = {
"email": email,
+ "password": password,
"expire_time": expire_time,
"days_left": days_left
}
diff --git a/cursor_win_id_modifier.ps1 b/cursor_win_id_modifier.ps1
new file mode 100644
index 0000000..77ae2f2
--- /dev/null
+++ b/cursor_win_id_modifier.ps1
@@ -0,0 +1,570 @@
+# 设置输出编码为 UTF-8
+$OutputEncoding = [System.Text.Encoding]::UTF8
+[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
+
+# 颜色定义
+$RED = "`e[31m"
+$GREEN = "`e[32m"
+$YELLOW = "`e[33m"
+$BLUE = "`e[34m"
+$NC = "`e[0m"
+
+# 配置文件路径
+$STORAGE_FILE = "$env:APPDATA\Cursor\User\globalStorage\storage.json"
+$BACKUP_DIR = "$env:APPDATA\Cursor\User\globalStorage\backups"
+
+# 检查管理员权限
+function Test-Administrator {
+ $user = [Security.Principal.WindowsIdentity]::GetCurrent()
+ $principal = New-Object Security.Principal.WindowsPrincipal($user)
+ return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
+}
+
+if (-not (Test-Administrator)) {
+ Write-Host "$RED[错误]$NC 请以管理员身份运行此脚本"
+ Write-Host "请右键点击脚本,选择'以管理员身份运行'"
+ Read-Host "按回车键退出"
+ exit 1
+}
+
+# 显示 Logo
+Clear-Host
+Write-Host @"
+
+ ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
+ ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
+ ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
+ ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
+ ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
+ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
+
+"@
+Write-Host "$BLUE================================$NC"
+Write-Host "$GREEN Cursor 设备ID 修改工具 $NC"
+Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】 $NC"
+Write-Host "$YELLOW 一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
+Write-Host "$YELLOW [重要提示] 本工具免费,如果对您有帮助,请关注公众号【煎饼果子卷AI】 $NC"
+Write-Host "$BLUE================================$NC"
+Write-Host ""
+
+# 获取并显示 Cursor 版本
+function Get-CursorVersion {
+ try {
+ # 主要检测路径
+ $packagePath = "$env:LOCALAPPDATA\Programs\cursor\resources\app\package.json"
+
+ if (Test-Path $packagePath) {
+ $packageJson = Get-Content $packagePath -Raw | ConvertFrom-Json
+ if ($packageJson.version) {
+ Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)"
+ return $packageJson.version
+ }
+ }
+
+ # 备用路径检测
+ $altPath = "$env:LOCALAPPDATA\cursor\resources\app\package.json"
+ if (Test-Path $altPath) {
+ $packageJson = Get-Content $altPath -Raw | ConvertFrom-Json
+ if ($packageJson.version) {
+ Write-Host "$GREEN[信息]$NC 当前安装的 Cursor 版本: v$($packageJson.version)"
+ return $packageJson.version
+ }
+ }
+
+ Write-Host "$YELLOW[警告]$NC 无法检测到 Cursor 版本"
+ Write-Host "$YELLOW[提示]$NC 请确保 Cursor 已正确安装"
+ return $null
+ }
+ catch {
+ Write-Host "$RED[错误]$NC 获取 Cursor 版本失败: $_"
+ return $null
+ }
+}
+
+# 获取并显示版本信息
+$cursorVersion = Get-CursorVersion
+Write-Host ""
+
+Write-Host "$YELLOW[重要提示]$NC 最新的 0.45.x (以支持)"
+Write-Host ""
+
+# 检查并关闭 Cursor 进程
+Write-Host "$GREEN[信息]$NC 检查 Cursor 进程..."
+
+function Get-ProcessDetails {
+ param($processName)
+ Write-Host "$BLUE[调试]$NC 正在获取 $processName 进程详细信息:"
+ Get-WmiObject Win32_Process -Filter "name='$processName'" |
+ Select-Object ProcessId, ExecutablePath, CommandLine |
+ Format-List
+}
+
+# 定义最大重试次数和等待时间
+$MAX_RETRIES = 5
+$WAIT_TIME = 1
+
+# 处理进程关闭
+function Close-CursorProcess {
+ param($processName)
+
+ $process = Get-Process -Name $processName -ErrorAction SilentlyContinue
+ if ($process) {
+ Write-Host "$YELLOW[警告]$NC 发现 $processName 正在运行"
+ Get-ProcessDetails $processName
+
+ Write-Host "$YELLOW[警告]$NC 尝试关闭 $processName..."
+ Stop-Process -Name $processName -Force
+
+ $retryCount = 0
+ while ($retryCount -lt $MAX_RETRIES) {
+ $process = Get-Process -Name $processName -ErrorAction SilentlyContinue
+ if (-not $process) { break }
+
+ $retryCount++
+ if ($retryCount -ge $MAX_RETRIES) {
+ Write-Host "$RED[错误]$NC 在 $MAX_RETRIES 次尝试后仍无法关闭 $processName"
+ Get-ProcessDetails $processName
+ Write-Host "$RED[错误]$NC 请手动关闭进程后重试"
+ Read-Host "按回车键退出"
+ exit 1
+ }
+ Write-Host "$YELLOW[警告]$NC 等待进程关闭,尝试 $retryCount/$MAX_RETRIES..."
+ Start-Sleep -Seconds $WAIT_TIME
+ }
+ Write-Host "$GREEN[信息]$NC $processName 已成功关闭"
+ }
+}
+
+# 关闭所有 Cursor 进程
+Close-CursorProcess "Cursor"
+Close-CursorProcess "cursor"
+
+# 创建备份目录
+if (-not (Test-Path $BACKUP_DIR)) {
+ New-Item -ItemType Directory -Path $BACKUP_DIR | Out-Null
+}
+
+# 备份现有配置
+if (Test-Path $STORAGE_FILE) {
+ Write-Host "$GREEN[信息]$NC 正在备份配置文件..."
+ $backupName = "storage.json.backup_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
+ Copy-Item $STORAGE_FILE "$BACKUP_DIR\$backupName"
+}
+
+# 生成新的 ID
+Write-Host "$GREEN[信息]$NC 正在生成新的 ID..."
+
+# 在颜色定义后添加此函数
+function Get-RandomHex {
+ param (
+ [int]$length
+ )
+
+ $bytes = New-Object byte[] ($length)
+ $rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::new()
+ $rng.GetBytes($bytes)
+ $hexString = [System.BitConverter]::ToString($bytes) -replace '-',''
+ $rng.Dispose()
+ return $hexString
+}
+
+# 改进 ID 生成函数
+function New-StandardMachineId {
+ $template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
+ $result = $template -replace '[xy]', {
+ param($match)
+ $r = [Random]::new().Next(16)
+ $v = if ($match.Value -eq "x") { $r } else { ($r -band 0x3) -bor 0x8 }
+ return $v.ToString("x")
+ }
+ return $result
+}
+
+# 在生成 ID 时使用新函数
+$MAC_MACHINE_ID = New-StandardMachineId
+$UUID = [System.Guid]::NewGuid().ToString()
+# 将 auth0|user_ 转换为字节数组的十六进制
+$prefixBytes = [System.Text.Encoding]::UTF8.GetBytes("auth0|user_")
+$prefixHex = -join ($prefixBytes | ForEach-Object { '{0:x2}' -f $_ })
+# 生成32字节(64个十六进制字符)的随机数作为 machineId 的随机部分
+$randomPart = Get-RandomHex -length 32
+$MACHINE_ID = "$prefixHex$randomPart"
+$SQM_ID = "{$([System.Guid]::NewGuid().ToString().ToUpper())}"
+
+# 在Update-MachineGuid函数前添加权限检查
+if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
+ Write-Host "$RED[错误]$NC 请使用管理员权限运行此脚本"
+ Start-Process powershell "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs
+ exit
+}
+
+function Update-MachineGuid {
+ try {
+ # 先检查注册表路径是否存在
+ $registryPath = "HKLM:\SOFTWARE\Microsoft\Cryptography"
+ if (-not (Test-Path $registryPath)) {
+ throw "注册表路径不存在: $registryPath"
+ }
+
+ # 获取当前的 MachineGuid
+ $currentGuid = Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction Stop
+ if (-not $currentGuid) {
+ throw "无法获取当前的 MachineGuid"
+ }
+
+ $originalGuid = $currentGuid.MachineGuid
+ Write-Host "$GREEN[信息]$NC 当前注册表值:"
+ Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"
+ Write-Host " MachineGuid REG_SZ $originalGuid"
+
+ # 创建备份目录(如果不存在)
+ if (-not (Test-Path $BACKUP_DIR)) {
+ New-Item -ItemType Directory -Path $BACKUP_DIR -Force | Out-Null
+ }
+
+ # 创建备份文件
+ $backupFile = "$BACKUP_DIR\MachineGuid_$(Get-Date -Format 'yyyyMMdd_HHmmss').reg"
+ $backupResult = Start-Process "reg.exe" -ArgumentList "export", "`"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography`"", "`"$backupFile`"" -NoNewWindow -Wait -PassThru
+
+ if ($backupResult.ExitCode -eq 0) {
+ Write-Host "$GREEN[信息]$NC 注册表项已备份到:$backupFile"
+ } else {
+ Write-Host "$YELLOW[警告]$NC 备份创建失败,继续执行..."
+ }
+
+ # 生成新GUID
+ $newGuid = [System.Guid]::NewGuid().ToString()
+
+ # 更新注册表
+ Set-ItemProperty -Path $registryPath -Name MachineGuid -Value $newGuid -Force -ErrorAction Stop
+
+ # 验证更新
+ $verifyGuid = (Get-ItemProperty -Path $registryPath -Name MachineGuid -ErrorAction Stop).MachineGuid
+ if ($verifyGuid -ne $newGuid) {
+ throw "注册表验证失败:更新后的值 ($verifyGuid) 与预期值 ($newGuid) 不匹配"
+ }
+
+ Write-Host "$GREEN[信息]$NC 注册表更新成功:"
+ Write-Host "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography"
+ Write-Host " MachineGuid REG_SZ $newGuid"
+ return $true
+ }
+ catch {
+ Write-Host "$RED[错误]$NC 注册表操作失败:$($_.Exception.Message)"
+
+ # 尝试恢复备份
+ if ($backupFile -and (Test-Path $backupFile)) {
+ Write-Host "$YELLOW[恢复]$NC 正在从备份恢复..."
+ $restoreResult = Start-Process "reg.exe" -ArgumentList "import", "`"$backupFile`"" -NoNewWindow -Wait -PassThru
+
+ if ($restoreResult.ExitCode -eq 0) {
+ Write-Host "$GREEN[恢复成功]$NC 已还原原始注册表值"
+ } else {
+ Write-Host "$RED[错误]$NC 恢复失败,请手动导入备份文件:$backupFile"
+ }
+ } else {
+ Write-Host "$YELLOW[警告]$NC 未找到备份文件或备份创建失败,无法自动恢复"
+ }
+ return $false
+ }
+}
+
+# 创建或更新配置文件
+Write-Host "$GREEN[信息]$NC 正在更新配置..."
+
+try {
+ # 检查配置文件是否存在
+ if (-not (Test-Path $STORAGE_FILE)) {
+ Write-Host "$RED[错误]$NC 未找到配置文件: $STORAGE_FILE"
+ Write-Host "$YELLOW[提示]$NC 请先安装并运行一次 Cursor 后再使用此脚本"
+ Read-Host "按回车键退出"
+ exit 1
+ }
+
+ # 读取现有配置文件
+ try {
+ $originalContent = Get-Content $STORAGE_FILE -Raw -Encoding UTF8
+
+ # 将 JSON 字符串转换为 PowerShell 对象
+ $config = $originalContent | ConvertFrom-Json
+
+ # 备份当前值
+ $oldValues = @{
+ 'machineId' = $config.'telemetry.machineId'
+ 'macMachineId' = $config.'telemetry.macMachineId'
+ 'devDeviceId' = $config.'telemetry.devDeviceId'
+ 'sqmId' = $config.'telemetry.sqmId'
+ }
+
+ # 更新特定的值
+ $config.'telemetry.machineId' = $MACHINE_ID
+ $config.'telemetry.macMachineId' = $MAC_MACHINE_ID
+ $config.'telemetry.devDeviceId' = $UUID
+ $config.'telemetry.sqmId' = $SQM_ID
+
+ # 将更新后的对象转换回 JSON 并保存
+ $updatedJson = $config | ConvertTo-Json -Depth 10
+ [System.IO.File]::WriteAllText(
+ [System.IO.Path]::GetFullPath($STORAGE_FILE),
+ $updatedJson,
+ [System.Text.Encoding]::UTF8
+ )
+ Write-Host "$GREEN[信息]$NC 成功更新配置文件"
+ } catch {
+ # 如果出错,尝试恢复原始内容
+ if ($originalContent) {
+ [System.IO.File]::WriteAllText(
+ [System.IO.Path]::GetFullPath($STORAGE_FILE),
+ $originalContent,
+ [System.Text.Encoding]::UTF8
+ )
+ }
+ throw "处理 JSON 失败: $_"
+ }
+ # 直接执行更新 MachineGuid,不再询问
+ Update-MachineGuid
+ # 显示结果
+ Write-Host ""
+ Write-Host "$GREEN[信息]$NC 已更新配置:"
+ Write-Host "$BLUE[调试]$NC machineId: $MACHINE_ID"
+ Write-Host "$BLUE[调试]$NC macMachineId: $MAC_MACHINE_ID"
+ Write-Host "$BLUE[调试]$NC devDeviceId: $UUID"
+ Write-Host "$BLUE[调试]$NC sqmId: $SQM_ID"
+
+ # 显示文件树结构
+ Write-Host ""
+ Write-Host "$GREEN[信息]$NC 文件结构:"
+ Write-Host "$BLUE$env:APPDATA\Cursor\User$NC"
+ Write-Host "├── globalStorage"
+ Write-Host "│ ├── storage.json (已修改)"
+ Write-Host "│ └── backups"
+
+ # 列出备份文件
+ $backupFiles = Get-ChildItem "$BACKUP_DIR\*" -ErrorAction SilentlyContinue
+ if ($backupFiles) {
+ foreach ($file in $backupFiles) {
+ Write-Host "│ └── $($file.Name)"
+ }
+ } else {
+ Write-Host "│ └── (空)"
+ }
+
+ # 显示公众号信息
+ Write-Host ""
+ Write-Host "$GREEN================================$NC"
+ Write-Host "$YELLOW 关注公众号【煎饼果子卷AI】一起交流更多Cursor技巧和AI知识(脚本免费、关注公众号加群有更多技巧和大佬) $NC"
+ Write-Host "$GREEN================================$NC"
+ Write-Host ""
+ Write-Host "$GREEN[信息]$NC 请重启 Cursor 以应用新的配置"
+ Write-Host ""
+
+ # 询问是否要禁用自动更新
+ Write-Host ""
+ Write-Host "$YELLOW[询问]$NC 是否要禁用 Cursor 自动更新功能?"
+ Write-Host "0) 否 - 保持默认设置 (按回车键)"
+ Write-Host "1) 是 - 禁用自动更新"
+ $choice = Read-Host "请输入选项 (0)"
+
+ if ($choice -eq "1") {
+ Write-Host ""
+ Write-Host "$GREEN[信息]$NC 正在处理自动更新..."
+ $updaterPath = "$env:LOCALAPPDATA\cursor-updater"
+
+ # 定义手动设置教程
+ function Show-ManualGuide {
+ Write-Host ""
+ Write-Host "$YELLOW[警告]$NC 自动设置失败,请尝试手动操作:"
+ Write-Host "$YELLOW手动禁用更新步骤:$NC"
+ Write-Host "1. 以管理员身份打开 PowerShell"
+ Write-Host "2. 复制粘贴以下命令:"
+ Write-Host "$BLUE命令1 - 删除现有目录(如果存在):$NC"
+ Write-Host "Remove-Item -Path `"$updaterPath`" -Force -Recurse -ErrorAction SilentlyContinue"
+ Write-Host ""
+ Write-Host "$BLUE命令2 - 创建阻止文件:$NC"
+ Write-Host "New-Item -Path `"$updaterPath`" -ItemType File -Force | Out-Null"
+ Write-Host ""
+ Write-Host "$BLUE命令3 - 设置只读属性:$NC"
+ Write-Host "Set-ItemProperty -Path `"$updaterPath`" -Name IsReadOnly -Value `$true"
+ Write-Host ""
+ Write-Host "$BLUE命令4 - 设置权限(可选):$NC"
+ Write-Host "icacls `"$updaterPath`" /inheritance:r /grant:r `"`$($env:USERNAME):(R)`""
+ Write-Host ""
+ Write-Host "$YELLOW验证方法:$NC"
+ Write-Host "1. 运行命令:Get-ItemProperty `"$updaterPath`""
+ Write-Host "2. 确认 IsReadOnly 属性为 True"
+ Write-Host "3. 运行命令:icacls `"$updaterPath`""
+ Write-Host "4. 确认只有读取权限"
+ Write-Host ""
+ Write-Host "$YELLOW[提示]$NC 完成后请重启 Cursor"
+ }
+
+ try {
+ # 检查cursor-updater是否存在
+ if (Test-Path $updaterPath) {
+ # 如果是文件,说明已经创建了阻止更新
+ if ((Get-Item $updaterPath) -is [System.IO.FileInfo]) {
+ Write-Host "$GREEN[信息]$NC 已创建阻止更新文件,无需再次阻止"
+ return
+ }
+ # 如果是目录,尝试删除
+ else {
+ try {
+ Remove-Item -Path $updaterPath -Force -Recurse -ErrorAction Stop
+ Write-Host "$GREEN[信息]$NC 成功删除 cursor-updater 目录"
+ }
+ catch {
+ Write-Host "$RED[错误]$NC 删除 cursor-updater 目录失败"
+ Show-ManualGuide
+ return
+ }
+ }
+ }
+
+ # 创建阻止文件
+ try {
+ New-Item -Path $updaterPath -ItemType File -Force -ErrorAction Stop | Out-Null
+ Write-Host "$GREEN[信息]$NC 成功创建阻止文件"
+ }
+ catch {
+ Write-Host "$RED[错误]$NC 创建阻止文件失败"
+ Show-ManualGuide
+ return
+ }
+
+ # 设置文件权限
+ try {
+ # 设置只读属性
+ Set-ItemProperty -Path $updaterPath -Name IsReadOnly -Value $true -ErrorAction Stop
+
+ # 使用 icacls 设置权限
+ $result = Start-Process "icacls.exe" -ArgumentList "`"$updaterPath`" /inheritance:r /grant:r `"$($env:USERNAME):(R)`"" -Wait -NoNewWindow -PassThru
+ if ($result.ExitCode -ne 0) {
+ throw "icacls 命令失败"
+ }
+
+ Write-Host "$GREEN[信息]$NC 成功设置文件权限"
+ }
+ catch {
+ Write-Host "$RED[错误]$NC 设置文件权限失败"
+ Show-ManualGuide
+ return
+ }
+
+ # 验证设置
+ try {
+ $fileInfo = Get-ItemProperty $updaterPath
+ if (-not $fileInfo.IsReadOnly) {
+ Write-Host "$RED[错误]$NC 验证失败:文件权限设置可能未生效"
+ Show-ManualGuide
+ return
+ }
+ }
+ catch {
+ Write-Host "$RED[错误]$NC 验证设置失败"
+ Show-ManualGuide
+ return
+ }
+
+ Write-Host "$GREEN[信息]$NC 成功禁用自动更新"
+ }
+ catch {
+ Write-Host "$RED[错误]$NC 发生未知错误: $_"
+ Show-ManualGuide
+ }
+ }
+ else {
+ Write-Host "$GREEN[信息]$NC 保持默认设置,不进行更改"
+ }
+
+ # 保留有效的注册表更新
+ Update-MachineGuid
+
+} catch {
+ Write-Host "$RED[错误]$NC 主要操作失败: $_"
+ Write-Host "$YELLOW[尝试]$NC 使用备选方法..."
+
+ try {
+ # 备选方法:使用 Add-Content
+ $tempFile = [System.IO.Path]::GetTempFileName()
+ $config | ConvertTo-Json | Set-Content -Path $tempFile -Encoding UTF8
+ Copy-Item -Path $tempFile -Destination $STORAGE_FILE -Force
+ Remove-Item -Path $tempFile
+ Write-Host "$GREEN[信息]$NC 使用备选方法成功写入配置"
+ } catch {
+ Write-Host "$RED[错误]$NC 所有尝试都失败了"
+ Write-Host "错误详情: $_"
+ Write-Host "目标文件: $STORAGE_FILE"
+ Write-Host "请确保您有足够的权限访问该文件"
+ Read-Host "按回车键退出"
+ exit 1
+ }
+}
+
+Write-Host ""
+Read-Host "按回车键退出"
+exit 0
+
+# 在文件写入部分修改
+function Write-ConfigFile {
+ param($config, $filePath)
+
+ try {
+ # 使用 UTF8 无 BOM 编码
+ $utf8NoBom = New-Object System.Text.UTF8Encoding $false
+ $jsonContent = $config | ConvertTo-Json -Depth 10
+
+ # 统一使用 LF 换行符
+ $jsonContent = $jsonContent.Replace("`r`n", "`n")
+
+ [System.IO.File]::WriteAllText(
+ [System.IO.Path]::GetFullPath($filePath),
+ $jsonContent,
+ $utf8NoBom
+ )
+
+ Write-Host "$GREEN[信息]$NC 成功写入配置文件(UTF8 无 BOM)"
+ }
+ catch {
+ throw "写入配置文件失败: $_"
+ }
+}
+
+function Compare-Version {
+ param (
+ [string]$version1,
+ [string]$version2
+ )
+
+ try {
+ $v1 = [version]($version1 -replace '[^\d\.].*$')
+ $v2 = [version]($version2 -replace '[^\d\.].*$')
+ return $v1.CompareTo($v2)
+ }
+ catch {
+ Write-Host "$RED[错误]$NC 版本比较失败: $_"
+ return 0
+ }
+}
+
+# 在主流程开始时添加版本检查
+Write-Host "$GREEN[信息]$NC 正在检查 Cursor 版本..."
+$cursorVersion = Get-CursorVersion
+
+if ($cursorVersion) {
+ $compareResult = Compare-Version $cursorVersion "0.45.0"
+ if ($compareResult -ge 0) {
+ Write-Host "$RED[错误]$NC 当前版本 ($cursorVersion) 暂不支持"
+ Write-Host "$YELLOW[建议]$NC 请使用 v0.44.11 及以下版本"
+ Write-Host "$YELLOW[建议]$NC 可以从以下地址下载支持的版本:"
+ Write-Host "Windows: https://download.todesktop.com/230313mzl4w4u92/Cursor%20Setup%200.44.11%20-%20Build%20250103fqxdt5u9z-x64.exe"
+ Write-Host "Mac ARM64: https://dl.todesktop.com/230313mzl4w4u92/versions/0.44.11/mac/zip/arm64"
+ Read-Host "按回车键退出"
+ exit 1
+ }
+ else {
+ Write-Host "$GREEN[信息]$NC 当前版本 ($cursorVersion) 支持重置功能"
+ }
+}
+else {
+ Write-Host "$YELLOW[警告]$NC 无法检测版本,将继续执行..."
+}
\ No newline at end of file
diff --git a/db_interactive.py b/db_interactive.py
new file mode 100644
index 0000000..c4f2306
--- /dev/null
+++ b/db_interactive.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sqlite3
+import os
+
+def main():
+ print("SQLite3 数据库查看器")
+ print("-" * 40)
+
+ # 获取数据库路径
+ db_path = input("请输入数据库文件的完整路径: ").strip()
+
+ if not os.path.exists(db_path):
+ print(f"错误: 文件 '{db_path}' 不存在!")
+ return
+
+ try:
+ # 连接到数据库
+ print(f"连接到数据库 '{db_path}'...")
+ conn = sqlite3.connect(db_path)
+ cursor = conn.cursor()
+
+ while True:
+ print("\n" + "-" * 40)
+ print("可用操作:")
+ print("1. 显示所有表")
+ print("2. 查看表结构")
+ print("3. 查看表数据")
+ print("4. 执行自定义SQL查询")
+ print("5. 退出")
+
+ choice = input("\n请选择操作 (1-5): ").strip()
+
+ if choice == "1":
+ # 显示所有表
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
+ tables = cursor.fetchall()
+
+ if not tables:
+ print("数据库中没有表")
+ else:
+ print(f"\n数据库中的表 ({len(tables)}):")
+ for i, table in enumerate(tables):
+ print(f"{i+1}. {table[0]}")
+
+ elif choice == "2":
+ # 查看表结构
+ table_name = input("请输入表名: ").strip()
+ cursor.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';")
+ if not cursor.fetchone():
+ print(f"表 '{table_name}' 不存在!")
+ continue
+
+ cursor.execute(f"PRAGMA table_info({table_name});")
+ columns = cursor.fetchall()
+ print(f"\n表 '{table_name}' 的结构:")
+ print("-" * 40)
+ print(f"{'序号':5} {'列名':20} {'类型':15} {'是否可空':10} {'默认值':15}")
+ print("-" * 70)
+ for col in columns:
+ col_id, name, type_, notnull, default_val, pk = col
+ print(f"{col_id:5} {name:20} {type_:15} {'否' if notnull else '是':10} {str(default_val)[:15]:15}")
+
+ elif choice == "3":
+ # 查看表数据
+ table_name = input("请输入表名: ").strip()
+ cursor.execute(f"SELECT name FROM sqlite_master WHERE type='table' AND name='{table_name}';")
+ if not cursor.fetchone():
+ print(f"表 '{table_name}' 不存在!")
+ continue
+
+ limit = input("显示多少条记录 (默认10): ").strip() or "10"
+ try:
+ limit = int(limit)
+ except ValueError:
+ print("请输入有效数字!")
+ continue
+
+ cursor.execute(f"SELECT COUNT(*) FROM {table_name};")
+ total = cursor.fetchone()[0]
+ print(f"\n表 '{table_name}' 共有 {total} 条记录,显示前 {limit} 条:")
+
+ if total == 0:
+ print("表中没有数据")
+ continue
+
+ # 获取列名
+ cursor.execute(f"PRAGMA table_info({table_name});")
+ columns = cursor.fetchall()
+ col_names = [col[1] for col in columns]
+
+ # 显示数据
+ cursor.execute(f"SELECT * FROM {table_name} LIMIT {limit};")
+ rows = cursor.fetchall()
+
+ # 打印列名
+ header = " | ".join(col_names)
+ print("\n" + header)
+ print("-" * len(header))
+
+ # 打印数据
+ for row in rows:
+ row_str = []
+ for item in row:
+ item_str = str(item)
+ if len(item_str) > 20:
+ item_str = item_str[:17] + "..."
+ row_str.append(item_str)
+ print(" | ".join(row_str))
+
+ elif choice == "4":
+ # 执行自定义SQL
+ sql = input("请输入SQL查询语句: ").strip()
+ if not sql:
+ continue
+
+ try:
+ cursor.execute(sql)
+ if sql.lower().startswith(("select", "pragma")):
+ rows = cursor.fetchall()
+ if not rows:
+ print("查询返回0条结果")
+ continue
+
+ # 获取列名
+ col_names = [description[0] for description in cursor.description]
+
+ # 打印列名
+ header = " | ".join(col_names)
+ print("\n" + header)
+ print("-" * len(header))
+
+ # 最多显示50条
+ display_rows = rows[:50]
+ for row in display_rows:
+ row_str = []
+ for item in row:
+ item_str = str(item)
+ if len(item_str) > 20:
+ item_str = item_str[:17] + "..."
+ row_str.append(item_str)
+ print(" | ".join(row_str))
+
+ if len(rows) > 50:
+ print(f"...(还有 {len(rows)-50} 条记录)")
+
+ print(f"\n总共 {len(rows)} 条记录")
+ else:
+ conn.commit()
+ print("查询执行成功")
+ except sqlite3.Error as e:
+ print(f"SQL错误: {e}")
+
+ elif choice == "5":
+ # 退出
+ break
+
+ else:
+ print("无效选择,请输入1-5之间的数字")
+
+ conn.close()
+ print("\n已关闭数据库连接。再见!")
+
+ except sqlite3.Error as e:
+ print(f"SQLite 错误: {e}")
+ except Exception as e:
+ print(f"发生错误: {e}")
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/db_viewer.py b/db_viewer.py
new file mode 100644
index 0000000..62c51f9
--- /dev/null
+++ b/db_viewer.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sqlite3
+import sys
+import os
+
+def print_separator(char='-', length=80):
+ """打印分隔线"""
+ print(char * length)
+
+def show_database_info(db_path):
+ """显示数据库信息"""
+ if not os.path.exists(db_path):
+ print(f"错误:数据库文件 '{db_path}' 不存在")
+ return
+
+ try:
+ # 连接到数据库
+ conn = sqlite3.connect(db_path)
+ cursor = conn.cursor()
+
+ # 获取所有表名
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
+ tables = cursor.fetchall()
+
+ if not tables:
+ print("数据库中没有表")
+ conn.close()
+ return
+
+ print(f"在数据库 '{db_path}' 中找到 {len(tables)} 个表:")
+ print_separator()
+
+ # 显示每个表的结构和数据
+ for table in tables:
+ table_name = table[0]
+ print(f"\n表名: {table_name}")
+ print_separator()
+
+ # 获取表结构
+ cursor.execute(f"PRAGMA table_info({table_name});")
+ columns = cursor.fetchall()
+
+ # 打印列名
+ col_names = [col[1] for col in columns]
+ col_types = [col[2] for col in columns]
+ print("表结构:")
+ for i, (name, type_) in enumerate(zip(col_names, col_types)):
+ print(f" {i+1}. {name} ({type_})")
+
+ # 获取记录数
+ cursor.execute(f"SELECT COUNT(*) FROM {table_name};")
+ count = cursor.fetchone()[0]
+ print(f"\n记录数: {count}")
+
+ # 获取表数据 (最多显示10条)
+ if count > 0:
+ cursor.execute(f"SELECT * FROM {table_name} LIMIT 10;")
+ rows = cursor.fetchall()
+
+ print("\n数据预览 (最多10条):")
+ print_separator()
+
+ # 打印列名
+ for col in col_names:
+ print(f"{col:15}", end="")
+ print("\n" + "-" * (15 * len(col_names)))
+
+ # 打印数据
+ for row in rows:
+ for item in row:
+ # 对值进行截断,防止过长的数据破坏格式
+ item_str = str(item)
+ if len(item_str) > 13:
+ item_str = item_str[:10] + "..."
+ print(f"{item_str:15}", end="")
+ print()
+
+ if count > 10:
+ print(f"...(还有 {count-10} 条记录)")
+
+ print_separator("=")
+
+ conn.close()
+ print("\n查询完成!")
+
+ except sqlite3.Error as e:
+ print(f"SQLite 错误: {e}")
+ except Exception as e:
+ print(f"发生错误: {e}")
+
+def show_usage():
+ """显示使用帮助"""
+ print(f"使用方法: python {os.path.basename(__file__)} <数据库文件路径>")
+ print("示例: python db_viewer.py C:\\path\\to\\your\\database.db")
+
+if __name__ == "__main__":
+ if len(sys.argv) != 2:
+ show_usage()
+ else:
+ db_path = sys.argv[1]
+ show_database_info(db_path)
\ No newline at end of file
diff --git a/gui/components/__pycache__/widgets.cpython-312.pyc b/gui/components/__pycache__/widgets.cpython-312.pyc
index f288555..2fa552e 100644
Binary files a/gui/components/__pycache__/widgets.cpython-312.pyc and b/gui/components/__pycache__/widgets.cpython-312.pyc differ
diff --git a/gui/components/__pycache__/workers.cpython-312.pyc b/gui/components/__pycache__/workers.cpython-312.pyc
index 67f2b9c..f7b5b1a 100644
Binary files a/gui/components/__pycache__/workers.cpython-312.pyc and b/gui/components/__pycache__/workers.cpython-312.pyc differ
diff --git a/gui/components/widgets.py b/gui/components/widgets.py
index 662d4bc..7ef6fb5 100644
--- a/gui/components/widgets.py
+++ b/gui/components/widgets.py
@@ -8,6 +8,10 @@ from PyQt5.QtCore import (
QPoint, QRect, QSize, pyqtProperty
)
from PyQt5.QtGui import QPainter, QColor, QPen
+import os
+from pathlib import Path
+from urllib.parse import quote
+import requests
class ActivationStatusWidget(QFrame):
"""激活状态显示组件"""
@@ -111,11 +115,18 @@ class ActivationWidget(QFrame):
layout.addWidget(header_frame)
# 添加提示文本
+ # info_text = QLabel(
+ # "获取会员激活码,请通过以下方式:\n\n"
+ # "官方自助网站:cursorpro.com.cn\n"
+ # "代理听泉助手vx联系:behikcigar\n"
+ # "天猫商店:https://e.tb.cn/h.TC2gtKSiccfl5MD?tk=GZvHenPgE4o CZ193\n\n"
+ # "诚挚祝愿,欢迎加盟合作!"
+ # )
info_text = QLabel(
"获取会员激活码,请通过以下方式:\n\n"
"官方自助网站:cursorpro.com.cn\n"
- "微信客服号:behikcigar\n"
- "商店铺:xxx\n\n"
+ "代理听泉助手vx联系:behikcigar\n"
+ "购买链接:https://www.houfaka.com/links/3BD4C127\n\n"
"诚挚祝愿,欢迎加盟合作!"
)
info_text.setStyleSheet("""
@@ -153,7 +164,7 @@ class ActivationWidget(QFrame):
# 复制微信按钮
copy_wx_btn = QPushButton("复制微信")
copy_wx_btn.setCursor(Qt.PointingHandCursor)
- copy_wx_btn.clicked.connect(lambda: self._copy_to_clipboard("bshkcigar", "已复制微信号"))
+ copy_wx_btn.clicked.connect(lambda: self._copy_to_clipboard("behikcigar", "已复制微信号"))
copy_wx_btn.setStyleSheet("""
QPushButton {
background-color: #198754;
@@ -169,13 +180,31 @@ class ActivationWidget(QFrame):
""")
btn_layout.addWidget(copy_wx_btn)
- # 确定按钮
- ok_btn = QPushButton("确定")
- ok_btn.setCursor(Qt.PointingHandCursor)
- ok_btn.clicked.connect(msg.accept)
- ok_btn.setStyleSheet("""
+ # 复制淘宝店铺按钮
+ # copy_tb_btn = QPushButton("复制天猫店铺")
+ # copy_tb_btn.setCursor(Qt.PointingHandCursor)
+ # copy_tb_btn.clicked.connect(lambda: self._copy_to_clipboard("https://e.tb.cn/h.TC2gtKSiccfl5MD?tk=GZvHenPgE4o CZ193", "已复制淘宝店铺"))
+ # copy_tb_btn.setStyleSheet("""
+ # QPushButton {
+ # background-color: #ff5100;
+ # color: white;
+ # border: none;
+ # padding: 6px 16px;
+ # border-radius: 3px;
+ # font-size: 13px;
+ # }
+ # QPushButton:hover {
+ # background-color: #c42b1c;
+ # }
+ # """)
+ # btn_layout.addWidget(copy_tb_btn)
+
+ copy_tb_btn = QPushButton("复制购买链接")
+ copy_tb_btn.setCursor(Qt.PointingHandCursor)
+ copy_tb_btn.clicked.connect(lambda: self._copy_to_clipboard("https://www.houfaka.com/links/3BD4C127", "已复制"))
+ copy_tb_btn.setStyleSheet("""
QPushButton {
- background-color: #6c757d;
+ background-color: #ff5100;
color: white;
border: none;
padding: 6px 16px;
@@ -183,10 +212,29 @@ class ActivationWidget(QFrame):
font-size: 13px;
}
QPushButton:hover {
- background-color: #5c636a;
+ background-color: #c42b1c;
}
""")
- btn_layout.addWidget(ok_btn)
+ btn_layout.addWidget(copy_tb_btn)
+
+ # 确定按钮
+ # ok_btn = QPushButton("确定")
+ # ok_btn.setCursor(Qt.PointingHandCursor)
+ # ok_btn.clicked.connect(msg.accept)
+ # ok_btn.setStyleSheet("""
+ # QPushButton {
+ # background-color: #6c757d;
+ # color: white;
+ # border: none;
+ # padding: 6px 16px;
+ # border-radius: 3px;
+ # font-size: 13px;
+ # }
+ # QPushButton:hover {
+ # background-color: #5c636a;
+ # }
+ # """)
+ # btn_layout.addWidget(ok_btn)
layout.addLayout(btn_layout)
@@ -214,7 +262,7 @@ class ActivationWidget(QFrame):
# 标题
title = QLabel("激活(听泉)会员,多个激活码可叠加整体时长")
- title.setStyleSheet("color: #28a745; font-size: 14px; font-weight: bold; border: none;") # 绿色标题
+ title.setStyleSheet("color: #28a745; font-size: 14px; line-height: 15px;min-height: 15px; font-weight: bold; border: none;") # 绿色标题
layout.addWidget(title)
# 激活码输入区域
@@ -227,6 +275,8 @@ class ActivationWidget(QFrame):
background-color: #f8f9fa;
border: 1px solid #ced4da;
border-radius: 4px;
+ min-height: 20px;
+ line-height: 20px;
padding: 8px;
color: #495057;
}
@@ -242,6 +292,8 @@ class ActivationWidget(QFrame):
color: white;
border: none;
padding: 8px 22px;
+ min-height: 20px;
+ line-height: 20px;
border-radius: 4px;
font-size: 13px;
}
@@ -371,7 +423,7 @@ class MemberStatusWidget(QFrame):
职责:
1. 显示会员状态信息
- 2. 显示设备信息
+ 2. 显示公告信息
3. 根据不同状态显示不同样式
属性:
@@ -381,11 +433,23 @@ class MemberStatusWidget(QFrame):
- 会员状态(正常/未激活)
- 到期时间
- 剩余天数
- - 设备信息(系统、设备名、IP、地区)
+ - 公告区域(从API获取,每2分钟更新一次)
"""
def __init__(self, parent=None):
super().__init__(parent)
self.setup_ui()
+
+ # 保存最后一次状态更新的数据
+ self.last_status = {}
+
+ # 创建定时器,每2分钟更新一次公告
+ self.announcement_timer = QTimer(self)
+ self.announcement_timer.setInterval(120000) # 2分钟 = 120000毫秒
+ self.announcement_timer.timeout.connect(self.refresh_announcement)
+ self.announcement_timer.start()
+
+ # 初始化后,延迟一秒检查并显示重要公告
+ QTimer.singleShot(1000, self.check_important_announcement)
def setup_ui(self):
layout = QVBoxLayout(self)
@@ -397,7 +461,7 @@ class MemberStatusWidget(QFrame):
container_layout = QVBoxLayout(container)
container_layout.setSpacing(5) # 减小组件间距
container_layout.setContentsMargins(0,0,0,0) # 减小内边距
- container.setFixedHeight(220) # 根据需要调整这个值
+ container.setFixedHeight(240) # 根据需要调整这个值
# 状态信息
self.status_text = QTextEdit()
@@ -455,15 +519,157 @@ class MemberStatusWidget(QFrame):
""")
layout.addWidget(container)
+
+ def check_important_announcement(self):
+ """检查并显示重要公告(level=5)"""
+ announcement_data = self.get_announcement(return_full_data=True)
+ if announcement_data and announcement_data.get("level") == 5:
+ self.show_announcement_dialog(announcement_data.get("txt", ""))
+
+ def show_announcement_dialog(self, content):
+ """显示公告弹窗"""
+ msg = QDialog(self)
+ msg.setWindowTitle("重要公告")
+ msg.setMinimumWidth(400)
+ msg.setWindowFlags(msg.windowFlags() & ~Qt.WindowContextHelpButtonHint)
+
+ # 创建布局
+ layout = QVBoxLayout()
+ layout.setSpacing(12)
+ layout.setContentsMargins(20, 15, 20, 15)
+
+ # 创建顶部框
+ header_frame = QFrame()
+ header_frame.setStyleSheet("""
+ QFrame {
+ background-color: #e6f7ff;
+ border: 1px solid #91d5ff;
+ border-radius: 4px;
+ }
+ QLabel {
+ background: transparent;
+ border: none;
+ }
+ """)
+ header_layout = QHBoxLayout(header_frame)
+ header_layout.setContentsMargins(12, 10, 12, 10)
+ header_layout.setSpacing(10)
+
+ icon_label = QLabel()
+ icon_label.setPixmap(self.style().standardIcon(QStyle.SP_MessageBoxInformation).pixmap(20, 20))
+ header_layout.addWidget(icon_label)
+
+ text_label = QLabel("重要公告")
+ text_label.setStyleSheet("font-size: 14px; font-weight: bold; color: #1890ff; background: transparent;")
+ header_layout.addWidget(text_label)
+ header_layout.addStretch()
+
+ layout.addWidget(header_frame)
+
+ # 公告内容
+ content_text = QTextEdit()
+ content_text.setReadOnly(True)
+ content_text.setHtml(content)
+ content_text.setStyleSheet("""
+ QTextEdit {
+ border: 1px solid #e8e8e8;
+ border-radius: 4px;
+ padding: 10px;
+ background-color: #fafafa;
+ min-height: 120px;
+ }
+ """)
+ layout.addWidget(content_text)
+
+ # 确认按钮
+ btn_layout = QHBoxLayout()
+ btn_layout.addStretch()
+
+ ok_btn = QPushButton("我知道了")
+ ok_btn.setCursor(Qt.PointingHandCursor)
+ ok_btn.clicked.connect(msg.accept)
+ ok_btn.setStyleSheet("""
+ QPushButton {
+ background-color: #1890ff;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 4px;
+ font-size: 13px;
+ }
+ QPushButton:hover {
+ background-color: #40a9ff;
+ }
+ """)
+ btn_layout.addWidget(ok_btn)
+
+ layout.addLayout(btn_layout)
+
+ # 设置对话框样式和布局
+ msg.setStyleSheet("""
+ QDialog {
+ background-color: white;
+ }
+ """)
+ msg.setLayout(layout)
+
+ # 显示对话框
+ msg.exec_()
+
+ def refresh_announcement(self):
+ """定时刷新公告内容"""
+ if self.last_status:
+ # 使用最后一次的状态数据更新界面,只刷新公告部分
+ self.update_status(self.last_status, refresh_announcement_only=True)
+
+ def get_announcement(self, return_full_data=False):
+ """从API获取公告内容
+
+ Args:
+ return_full_data: 是否返回完整的公告数据字典,包含level等信息
+ """
+ try:
+ response = requests.get("http://api.cursorpro.com.cn/admin/api.version/ad", timeout=5)
+ if response.status_code == 200:
+ try:
+ # 解析JSON响应
+ json_data = response.json()
+ # 检查code是否为0(成功)
+ if json_data.get("code") == 0:
+ # 获取公告数据
+ announcement_data = json_data.get("data", {})
+
+ # 根据参数决定返回完整数据还是仅文本
+ if return_full_data:
+ return announcement_data
+ else:
+ return announcement_data.get("txt", "")
+ else:
+ # 如果code不是0,返回错误信息
+ error_msg = f"获取公告失败: {json_data.get('msg', '未知错误')}"
+ return {} if return_full_data else error_msg
+ except ValueError:
+ # JSON解析错误
+ error_msg = "解析公告数据失败"
+ return {} if return_full_data else error_msg
+ else:
+ error_msg = f"获取公告失败,状态码: {response.status_code}"
+ return {} if return_full_data else error_msg
+ except Exception as e:
+ error_msg = f"获取公告失败: {str(e)}"
+ return {} if return_full_data else error_msg
- def update_status(self, status: Dict):
+ def update_status(self, status: Dict, refresh_announcement_only=False):
"""更新状态显示"""
try:
+ # 保存最后一次状态数据用于刷新公告
+ if not refresh_announcement_only:
+ self.last_status = status.copy()
+
# 获取状态信息
is_activated = status.get("is_activated", False)
expire_time = status.get("expire_time", "未知")
days_left = status.get("days_left", 0)
- device_info = status.get("device_info", {})
# 构建状态文本
status_text = []
@@ -479,15 +685,14 @@ class MemberStatusWidget(QFrame):
days_color = "#10B981" if days_left > 30 else "#F59E0B" if days_left > 7 else "#EF4444"
status_text.append(f'剩余天数:{days_left}天')
-
-
status_text.append("
")
- # 设备信息
- status_text.append("\n设备信息:")
- status_text.append(f"系统:{device_info.get('os', 'Windows')}")
- status_text.append(f"设备名:{device_info.get('device_name', '未知')}")
- status_text.append(f"IP地址:{device_info.get('ip', '未知')}")
- status_text.append(f"地区:{device_info.get('location', '未知')}")
+
+ # 公告区域
+ announcement = self.get_announcement()
+ status_text.append('