Compare commits

...

14 Commits

Author SHA1 Message Date
yeongpin
ff358588bb feat: Refactor Project Structure and Add Configuration Management
- Add `config.py` for centralized configuration management
- Add `utils.py` for cross-platform utility functions
- Remove `browser.py` and `control.py` to simplify project structure
- Update version to 1.7.06
- Modify build specification to reflect new file structure
- Update localization files with new update confirmation messages
- Enhance configuration loading and path detection across different platforms
2025-03-11 11:49:17 +08:00
yeongpin
6ca80ccb10 Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-03-11 10:12:57 +08:00
yeongpin
2fca5218fb remove md 2025-03-11 10:12:54 +08:00
Pin Studios
71ecf5a201 Update issue templates 2025-03-11 10:12:41 +08:00
Pin Studios
4570b174ab Update issue templates 2025-03-10 18:36:36 +08:00
yeongpin
f708ce443b fix: Correct Syntax in Linux Installation Script
- Fix syntax error in install script's conditional statements
- Remove unnecessary braces and correct shell script syntax
- Ensure proper handling of Linux architecture detection
2025-03-10 17:54:09 +08:00
yeongpin
4f6f3fe814 fix: Improve Cursor Version Check and Configuration Handling
- Enhance version checking in `reset_machine_manual.py` with more robust error handling
- Add detailed error messages and logging for version detection
- Update configuration paths in `new_signup.py` to include Cursor application path
- Add configuration initialization in `main.py`
- Update localization files with new error and version-related messages
2025-03-10 17:41:07 +08:00
yeongpin
54ecf2d752 feat: Add Linux ARM64 Support and Update Build Workflow
- Extend GitHub Actions workflow to build Linux x64 and ARM64 executables
- Update install script to detect Linux architecture (x64 or ARM64)
- Modify release process to include both Linux architecture artifacts
- Rename Linux build job to clarify x64 architecture
2025-03-10 16:28:26 +08:00
yeongpin
1f1231d1a9 remove br in readme 2025-03-10 14:43:35 +08:00
yeongpin
d10999a517 Update Readme Doc 2025-03-10 14:43:09 +08:00
yeongpin
fb4be6334a docs: Add Detailed Comments and Localization for Configuration Settings
- Add bilingual comments (English and Chinese) for OSPaths section
- Provide clear descriptions for timing-related configuration parameters
- Enhance readability of README.md configuration settings
- Improve documentation for storage, SQLite, and machine ID paths
2025-03-10 13:42:18 +08:00
Pin Studios
786eba5371 Update README.md 2025-03-10 13:41:05 +08:00
yeongpin
41ddbf519e hotfix: Optimize Verification Code Handling and Timing Configuration
- Refactor verification code retrieval with dynamic wait time generation
- Use `get_random_wait_time()` for more flexible retry intervals
- Update version to 1.7.04 across project files
- Minor improvements to timing and retry logic in signup process
2025-03-10 13:27:41 +08:00
yeongpin
ffd48201fd hotfix: Improve Signup Flow and Timing Configuration
- Add comprehensive timing configuration for signup process
- Refactor random wait time generation with more flexible config options
- Update form filling and verification code handling with configurable wait times
- Enhance localization support for new timing-related messages
- Update version to 1.7.03 across project files
2025-03-10 12:24:43 +08:00
20 changed files with 664 additions and 572 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.7.02 version=1.7.06
VERSION=1.7.02 VERSION=1.7.06

View File

@@ -6,7 +6,7 @@ on:
version: version:
description: 'Version number (e.g. 1.0.9)' description: 'Version number (e.g. 1.0.9)'
required: true required: true
default: '1.7.02' default: '1.7.06'
permissions: permissions:
contents: write contents: write
@@ -98,7 +98,7 @@ jobs:
name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64 name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
build-linux: build-linux-x64:
needs: create-tag needs: create-tag
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
@@ -120,20 +120,55 @@ jobs:
pip install pyinstaller pip install pyinstaller
pip install -r requirements.txt pip install -r requirements.txt
- name: Build Linux executable - name: Build Linux x64 executable
env: env:
VERSION: ${{ env.VERSION }} VERSION: ${{ env.VERSION }}
run: | run: |
pyinstaller build.spec pyinstaller build.spec
mv "dist/CursorFreeVIP_${{ env.VERSION }}_linux" "dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64"
echo "Contents of dist directory:" echo "Contents of dist directory:"
ls -la dist/ ls -la dist/
- name: Upload Linux artifact - name: Upload Linux x64 artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: CursorFreeVIP_${{ env.VERSION }}_linux name: CursorFreeVIP_${{ env.VERSION }}_linux_x64
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64
build-linux-arm64:
needs: create-tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: arm64
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
- name: Build in ARM64 Docker container
run: |
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.9-slim bash -c "
apt-get update && apt-get install -y build-essential
pip install --upgrade pip
pip install pyinstaller
pip install -r requirements.txt
python -m PyInstaller build.spec
mv /app/dist/CursorFreeVIP_${{ env.VERSION }}_linux /app/dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
"
echo "Contents of dist directory:"
ls -la dist/
- name: Upload Linux ARM64 artifact
uses: actions/upload-artifact@v4
with:
name: CursorFreeVIP_${{ env.VERSION }}_linux_arm64
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
build-macos-intel: build-macos-intel:
needs: create-tag needs: create-tag
@@ -171,9 +206,8 @@ jobs:
name: CursorFreeVIP_${{ env.VERSION }}_mac_intel name: CursorFreeVIP_${{ env.VERSION }}_mac_intel
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
create-release: create-release:
needs: [build-windows, build-macos-arm64, build-linux, build-macos-intel] needs: [build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
@@ -201,7 +235,8 @@ jobs:
files: | files: |
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP_${{ env.VERSION }}_linux artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_x64/CursorFreeVIP_${{ env.VERSION }}_linux_x64
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_arm64/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel
draft: false draft: false
prerelease: false prerelease: false

View File

@@ -1,5 +1,30 @@
# Change Log # Change Log
## v1.7.06
1. Add: Update Confirm | 增加更新確認
2. Add: Update Skipped | 增加更新跳過
3. Add: Invalid Choice | 增加無效選擇
4. Fix: Cursor Path | 修復Cursor路徑
5. Fix: Path Encoding | 修復路徑編碼
6. Fix: Getting Verification Code | 修復獲取驗證碼
7. Fix: Setting Password | 修復設置密碼
8. Fix: Disable Auto Update | 修復禁用自動更新
9. Add Config.py | 增加Config.py
10. Add utils.py | 增加utils.py
11. Rebuild some logic | 重新構建一些邏輯
## v1.7.05
1. Fix: Cursor Version Check | 修復Cursor版本檢查
2. Fix: Small Problem | 修復一些小問題
## v1.7.04
1. Hotfix: Small Problem | 修復一些小問題
## v1.7.03
1. Hotfix: Small Problem | 修復一些小問題
## v1.7.02 ## v1.7.02
1. Fix: Cursor Path | 修復Cursor路徑 1. Fix: Cursor Path | 修復Cursor路徑
2. Add: Config File | 增加配置文件 2. Add: Config File | 增加配置文件

View File

@@ -12,7 +12,7 @@
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
</p> </p>
<h4>Support Latest 0.46.9 Version | 支持最新0.46.9本</h4> <h4>Support Latest 0.46.10 Version | 支持最新0.46.10版本</h4>
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration. This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
@@ -88,6 +88,8 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
📝 Config | 文件配置 📝 Config | 文件配置
`Win / Macos / Linux Path | 路徑 [Documents/.cursor-free-vip/config.ini]` `Win / Macos / Linux Path | 路徑 [Documents/.cursor-free-vip/config.ini]`
<details>
<summary><b>⭐ Config | 文件配置</b></summary>
``` ```
[Chrome] [Chrome]
@@ -101,10 +103,44 @@ handle_turnstile_time = 2
handle_turnstile_random_time = 1-3 handle_turnstile_random_time = 1-3
[OSPaths] [OSPaths]
# Storage Path | 存儲路徑
storage_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/storage.json storage_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/storage.json
# SQLite Path | SQLite路徑
sqlite_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/state.vscdb sqlite_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/state.vscdb
# Machine ID Path | 機器ID路徑
machine_id_path = /Users/username/Library/Application Support/Cursor/machineId machine_id_path = /Users/username/Library/Application Support/Cursor/machineId
[Timing]
# Min Random Time | 最小隨機時間
min_random_time = 0.1
# Max Random Time | 最大隨機時間
max_random_time = 0.8
# Page Load Wait | 頁面加載等待時間
page_load_wait = 0.1-0.8
# Input Wait | 輸入等待時間
input_wait = 0.3-0.8
# Submit Wait | 提交等待時間
submit_wait = 0.5-1.5
# Verification Code Input | 驗證碼輸入等待時間
verification_code_input = 0.1-0.3
# Verification Success Wait | 驗證成功等待時間
verification_success_wait = 2-3
# Verification Retry Wait | 驗證重試等待時間
verification_retry_wait = 2-3
# Email Check Initial Wait | 郵件檢查初始等待時間
email_check_initial_wait = 4-6
# Email Refresh Wait | 郵件刷新等待時間
email_refresh_wait = 2-4
# Settings Page Load Wait | 設置頁面加載等待時間
settings_page_load_wait = 1-2
# Failed Retry Time | 失敗重試時間
failed_retry_time = 0.5-1
# Retry Interval | 重試間隔
retry_interval = 8-12
# Max Timeout | 最大超時時間
max_timeout = 160
``` ```
</details>
* Use administrator to run the script <br>請使用管理員身份運行腳本 * Use administrator to run the script <br>請使用管理員身份運行腳本

View File

@@ -1,95 +0,0 @@
from DrissionPage import ChromiumOptions, ChromiumPage
import sys
import os
import logging
import random
class BrowserManager:
def __init__(self, noheader=False):
self.browser = None
self.noheader = noheader
def init_browser(self):
"""初始化浏览器"""
co = self._get_browser_options()
# 如果设置了 noheader添加相应的参数
if self.noheader:
co.set_argument('--headless=new')
self.browser = ChromiumPage(co)
return self.browser
def _get_browser_options(self):
"""获取浏览器配置"""
co = ChromiumOptions()
try:
extension_path = self._get_extension_path()
co.add_extension(extension_path)
co.set_argument("--allow-extensions-in-incognito")
extension_block_path = self.get_extension_block()
co.add_extension(extension_block_path)
co.set_argument("--allow-extensions-in-incognito")
except FileNotFoundError as e:
logging.warning(f"警告: {e}")
# 设置更真实的用户代理
co.set_user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
# 基本设置
co.set_pref("credentials_enable_service", False)
# 随机端口
co.auto_port()
# 系统特定设置
if sys.platform == "darwin": # macOS
co.set_argument("--disable-gpu")
co.set_argument("--no-sandbox")
elif sys.platform == "win32": # Windows
co.set_argument("--disable-software-rasterizer")
# 设置窗口大小
window_width = random.randint(1024, 1920)
window_height = random.randint(768, 1080)
co.set_argument(f"--window-size={window_width},{window_height}")
return co
def _get_extension_path(self):
"""获取插件路径"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "turnstilePatch")
if hasattr(sys, "_MEIPASS"):
extension_path = os.path.join(sys._MEIPASS, "turnstilePatch")
if not os.path.exists(extension_path):
raise FileNotFoundError(f"插件不存在: {extension_path}")
return extension_path
def get_extension_block(self):
"""获取插件路径"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "PBlock")
if hasattr(sys, "_MEIPASS"):
extension_path = os.path.join(sys._MEIPASS, "PBlock")
if not os.path.exists(extension_path):
raise FileNotFoundError(f"插件不存在: {extension_path}")
return extension_path
def quit(self):
"""关闭浏览器"""
if self.browser:
try:
self.browser.quit()
except:
pass

View File

@@ -29,15 +29,19 @@ a = Analysis(
('cursor_auth.py', '.'), ('cursor_auth.py', '.'),
('reset_machine_manual.py', '.'), ('reset_machine_manual.py', '.'),
('cursor_register.py', '.'), ('cursor_register.py', '.'),
('browser.py', '.'), ('new_signup.py', '.'),
('control.py', '.'), ('new_tempemail.py', '.'),
('quit_cursor.py', '.'),
('cursor_register_manual.py', '.'),
('.env', '.') ('.env', '.')
], ],
hiddenimports=[ hiddenimports=[
'cursor_auth', 'cursor_auth',
'reset_machine_manual', 'reset_machine_manual',
'browser', 'new_signup',
'control' 'new_tempemail',
'quit_cursor',
'cursor_register_manual'
], ],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},

117
config.py Normal file
View File

@@ -0,0 +1,117 @@
import os
import sys
import configparser
from colorama import Fore, Style
from utils import get_user_documents_path, get_default_chrome_path, get_linux_cursor_path
def setup_config(translator=None):
"""Setup configuration file and return config object"""
try:
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
os.makedirs(config_dir, exist_ok=True)
config = configparser.ConfigParser()
# 默認配置
default_config = {
'Chrome': {
'chromepath': get_default_chrome_path()
},
'Turnstile': {
'handle_turnstile_time': '2',
'handle_turnstile_random_time': '1-3'
},
'Timing': {
'min_random_time': '0.1',
'max_random_time': '0.8',
'page_load_wait': '0.1-0.8',
'input_wait': '0.3-0.8',
'submit_wait': '0.5-1.5',
'verification_code_input': '0.1-0.3',
'verification_success_wait': '2-3',
'verification_retry_wait': '2-3',
'email_check_initial_wait': '4-6',
'email_refresh_wait': '2-4',
'settings_page_load_wait': '1-2',
'failed_retry_time': '0.5-1',
'retry_interval': '8-12',
'max_timeout': '160'
}
}
# 添加系統特定路徑配置
if sys.platform == "win32":
appdata = os.getenv("APPDATA")
localappdata = os.getenv("LOCALAPPDATA", "")
default_config['WindowsPaths'] = {
'storage_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "storage.json"),
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
'machine_id_path': os.path.join(appdata, "Cursor", "machineId"),
'cursor_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app"),
'updater_path': os.path.join(localappdata, "cursor-updater")
}
elif sys.platform == "darwin":
default_config['MacPaths'] = {
'storage_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"),
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app",
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater")
}
elif sys.platform == "linux":
sudo_user = os.environ.get('SUDO_USER')
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
default_config['LinuxPaths'] = {
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/.config/Cursor/machineId"),
'cursor_path': get_linux_cursor_path(),
'updater_path': os.path.expanduser("~/.config/cursor-updater")
}
# 讀取現有配置並合併
if os.path.exists(config_file):
config.read(config_file, encoding='utf-8')
config_modified = False
for section, options in default_config.items():
if not config.has_section(section):
config.add_section(section)
config_modified = True
for option, value in options.items():
if not config.has_option(section, option):
config.set(section, option, str(value))
config_modified = True
if translator:
print(f"{Fore.YELLOW} {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
if config_modified:
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN}{translator.get('register.config_updated')}{Style.RESET_ALL}")
else:
for section, options in default_config.items():
config.add_section(section)
for option, value in options.items():
config.set(section, option, str(value))
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN}{translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
return config
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ Error setting up config: {e}{Style.RESET_ALL}")
return None
def get_config(translator=None):
"""Get existing config or create new one"""
return setup_config(translator)

View File

@@ -1,181 +0,0 @@
import time
import random
import os
from colorama import Fore, Style, init
# 初始化colorama
init()
# 定义emoji常量
EMOJI = {
'MAIL': '📧',
'REFRESH': '🔄',
'SUCCESS': '',
'ERROR': '',
'INFO': '',
'CODE': '📱'
}
class BrowserControl:
def __init__(self, browser, translator=None):
self.browser = browser
self.translator = translator # 保存translator
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
self.current_tab = None # 当前标签页
self.signup_tab = None # 注册标签页
self.email_tab = None # 邮箱标签页
def create_new_tab(self):
"""创建新标签页"""
try:
# 保存当前标签页
self.current_tab = self.browser
# 创建新的浏览器实例
from browser import BrowserManager
browser_manager = BrowserManager()
new_browser = browser_manager.init_browser()
# 保存新标签页
self.signup_tab = new_browser
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.create_new_tab_success')}{Style.RESET_ALL}")
return new_browser
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.create_new_tab_failed', error=str(e))}{Style.RESET_ALL}")
return None
def fill_verification_code(self, code):
"""填写验证码"""
try:
if not code or len(code) != 6:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.verification_code_format_error')}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.fill_verification_code')}...{Style.RESET_ALL}")
# 记住当前标签页(邮箱页面)
email_tab = self.browser
# 切换回注册页面标签
self.switch_to_tab(self.signup_tab)
time.sleep(1)
# 输入验证码
for digit in code:
self.browser.actions.input(digit)
time.sleep(random.uniform(0.1, 0.3))
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.verification_code_filled')}{Style.RESET_ALL}")
# 等待页面加载和登录完成
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.wait_for_login')}...{Style.RESET_ALL}")
time.sleep(5)
# 先访问登录页面确保登录状态
login_url = "https://authenticator.cursor.sh"
self.browser.get(login_url)
time.sleep(3) # 增加等待时间
# 获取cookies第一次尝试
token = self.get_cursor_session_token()
if not token:
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.get_token_failed')}...{Style.RESET_ALL}")
time.sleep(3)
token = self.get_cursor_session_token()
if token:
self.save_token_to_file(token)
# 获取到token后再访问设置页面
settings_url = "https://www.cursor.com/settings"
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.get_account_info')}...{Style.RESET_ALL}")
self.browser.get(settings_url)
time.sleep(2)
# 获取账户额度信息
try:
usage_selector = (
"css:div.col-span-2 > div > div > div > div > "
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
)
usage_ele = self.browser.ele(usage_selector)
if usage_ele:
usage_info = usage_ele.text
total_usage = usage_info.split("/")[-1].strip()
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('control.account_usage_limit')}: {total_usage}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_account_usage_failed', error=str(e))}{Style.RESET_ALL}")
# 切换回邮箱页面
self.switch_to_tab(email_tab)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.fill_verification_code_failed', error=str(e))}{Style.RESET_ALL}")
return False
def check_and_click_turnstile(self):
"""检查并点击 Turnstile 验证框"""
try:
# 等待验证框出现
time.sleep(1)
# 查找验证框
verify_checkbox = self.browser.ele('xpath://label[contains(@class, "cb-lb")]//input[@type="checkbox"]')
if verify_checkbox:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.find_turnstile_verification_box')}...{Style.RESET_ALL}")
verify_checkbox.click()
time.sleep(2) # 等待验证完成
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.clicked_turnstile_verification_box')}{Style.RESET_ALL}")
return True
return False
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.check_and_click_turnstile_failed', error=str(e))}{Style.RESET_ALL}")
return False
def get_cursor_session_token(self, max_attempts=3, retry_interval=2):
"""获取Cursor会话token"""
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.get_cursor_session_token')}...{Style.RESET_ALL}")
attempts = 0
while attempts < max_attempts:
try:
# 直接从浏览器对象获取cookies
all_cookies = self.browser.get_cookies()
# 遍历查找目标cookie
for cookie in all_cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
token = cookie["value"].split("%3A%3A")[1]
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.get_cursor_session_token_success')}: {token}{Style.RESET_ALL}")
return token
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.get_cursor_session_token_failed', attempts=attempts, retry_interval=retry_interval)}...{Style.RESET_ALL}")
time.sleep(retry_interval)
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.reach_max_attempts', max_attempts=max_attempts)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_cookie_failed', error=str(e))}{Style.RESET_ALL}")
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.will_retry_in', retry_interval=retry_interval)}...{Style.RESET_ALL}")
time.sleep(retry_interval)
return None
def save_token_to_file(self, token):
"""保存token到文件"""
try:
with open('cursor_tokens.txt', 'a', encoding='utf-8') as f:
f.write(f"Token: {token}\n")
f.write("-" * 50 + "\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.token_saved_to_file')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.save_token_failed', error=str(e))}{Style.RESET_ALL}")

View File

@@ -2,6 +2,7 @@ import sqlite3
import os import os
import sys import sys
from colorama import Fore, Style, init from colorama import Fore, Style, init
from config import get_config
# 初始化colorama # 初始化colorama
init() init()
@@ -21,21 +22,40 @@ EMOJI = {
class CursorAuth: class CursorAuth:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
# 判断操作系统
if sys.platform == "win32": # Windows # 获取配置
self.db_path = os.path.join( config = get_config(translator)
os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb" if not config:
) print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.config_error') if self.translator else 'Failed to load configuration'}{Style.RESET_ALL}")
elif sys.platform == 'linux': sys.exit(1)
self.db_path = os.path.expanduser(
"~/.config/Cursor/User/globalStorage/state.vscdb" # 根据操作系统获取路径
) try:
elif sys.platform == 'darwin': # macOS if sys.platform == "win32": # Windows
self.db_path = os.path.expanduser( if not config.has_section('WindowsPaths'):
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb" raise ValueError("Windows paths not configured")
) self.db_path = config.get('WindowsPaths', 'sqlite_path')
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform')}{Style.RESET_ALL}") elif sys.platform == 'linux': # Linux
if not config.has_section('LinuxPaths'):
raise ValueError("Linux paths not configured")
self.db_path = config.get('LinuxPaths', 'sqlite_path')
elif sys.platform == 'darwin': # macOS
if not config.has_section('MacPaths'):
raise ValueError("macOS paths not configured")
self.db_path = config.get('MacPaths', 'sqlite_path')
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform') if self.translator else 'Unsupported platform'}{Style.RESET_ALL}")
sys.exit(1)
# 验证路径是否存在
if not os.path.exists(os.path.dirname(self.db_path)):
raise FileNotFoundError(f"Database directory not found: {os.path.dirname(self.db_path)}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.path_error', error=str(e)) if self.translator else f'Error getting database path: {str(e)}'}{Style.RESET_ALL}")
sys.exit(1) sys.exit(1)
# 检查数据库文件是否存在 # 检查数据库文件是否存在

View File

@@ -2,8 +2,6 @@ import os
from colorama import Fore, Style, init from colorama import Fore, Style, init
import time import time
import random import random
from browser import BrowserManager
from control import BrowserControl
from cursor_auth import CursorAuth from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter from reset_machine_manual import MachineIDResetter
@@ -35,7 +33,6 @@ class CursorRegistration:
self.translator = translator self.translator = translator
# Set to display mode # Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False' os.environ['BROWSER_HEADLESS'] = 'False'
self.browser_manager = BrowserManager()
self.browser = None self.browser = None
self.controller = None self.controller = None
self.mail_url = "https://yopmail.com/zh/email-generator" self.mail_url = "https://yopmail.com/zh/email-generator"
@@ -160,7 +157,7 @@ class CursorRegistration:
if usage_ele: if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip() total_usage = usage_ele.text.split("/")[-1].strip()
print(f"Total Usage: {total_usage}\n") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('register.total_usage', usage=total_usage)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30 max_attempts = 30
retry_interval = 2 retry_interval = 2

View File

@@ -2,8 +2,6 @@ import os
from colorama import Fore, Style, init from colorama import Fore, Style, init
import time import time
import random import random
from browser import BrowserManager
from control import BrowserControl
from cursor_auth import CursorAuth from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter from reset_machine_manual import MachineIDResetter
@@ -35,7 +33,6 @@ class CursorRegistration:
self.translator = translator self.translator = translator
# Set to display mode # Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False' os.environ['BROWSER_HEADLESS'] = 'False'
self.browser_manager = BrowserManager()
self.browser = None self.browser = None
self.controller = None self.controller = None
self.sign_up_url = "https://authenticator.cursor.sh/sign-up" self.sign_up_url = "https://authenticator.cursor.sh/sign-up"

View File

@@ -4,6 +4,7 @@ import platform
import shutil import shutil
from colorama import Fore, Style, init from colorama import Fore, Style, init
import subprocess import subprocess
from config import get_config
# Initialize colorama # Initialize colorama
init() init()
@@ -24,11 +25,24 @@ class AutoUpdateDisabler:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
self.system = platform.system() self.system = platform.system()
self.updater_paths = {
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"), # 从配置文件获取路径
"Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"), config = get_config(translator)
"Linux": os.path.expanduser("~/.config/cursor-updater") if config:
} if self.system == "Windows":
self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
elif self.system == "Darwin":
self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
elif self.system == "Linux":
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
else:
# 如果配置加载失败,使用默认路径
self.updater_paths = {
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"),
"Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"),
"Linux": os.path.expanduser("~/.config/cursor-updater")
}
self.updater_path = self.updater_paths.get(self.system)
def _kill_cursor_processes(self): def _kill_cursor_processes(self):
"""End all Cursor processes""" """End all Cursor processes"""
@@ -50,7 +64,7 @@ class AutoUpdateDisabler:
def _remove_updater_directory(self): def _remove_updater_directory(self):
"""Delete updater directory""" """Delete updater directory"""
try: try:
updater_path = self.updater_paths.get(self.system) updater_path = self.updater_path
if not updater_path: if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}") raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
@@ -72,7 +86,7 @@ class AutoUpdateDisabler:
def _create_blocking_file(self): def _create_blocking_file(self):
"""Create blocking file""" """Create blocking file"""
try: try:
updater_path = self.updater_paths.get(self.system) updater_path = self.updater_path
if not updater_path: if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}") raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")

View File

@@ -68,7 +68,24 @@
"version_less_than_0_45": "Cursor Version < 0.45.0, Skip Patching getMachineId", "version_less_than_0_45": "Cursor Version < 0.45.0, Skip Patching getMachineId",
"detecting_version": "Detecting Cursor Version", "detecting_version": "Detecting Cursor Version",
"patching_getmachineid": "Patching getMachineId", "patching_getmachineid": "Patching getMachineId",
"version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId" "version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId",
"permission_denied": "Permission Denied: {error}",
"backup_created": "Backup Created",
"update_success": "Update Success",
"update_failed": "Update Failed: {error}",
"windows_machine_guid_updated": "Windows Machine GUID Updated Successfully",
"reading_package_json": "Reading package.json {path}",
"invalid_json_object": "Invalid JSON Object",
"no_version_field": "No Version Field Found in package.json",
"version_field_empty": "Version Field is Empty",
"invalid_version_format": "Invalid Version Format: {version}",
"found_version": "Found Version: {version}",
"version_parse_error": "Version Parse Error: {error}",
"package_not_found": "Package.json Not Found: {path}",
"check_version_failed": "Check Version Failed: {error}",
"stack_trace": "Stack Trace",
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0"
}, },
"register": { "register": {
"title": "Cursor Registration Tool", "title": "Cursor Registration Tool",
@@ -141,7 +158,11 @@
"verification_failed": "Verification Failed", "verification_failed": "Verification Failed",
"verification_error": "Verification Error: {error}", "verification_error": "Verification Error: {error}",
"config_option_added": "Config Option Added: {option}", "config_option_added": "Config Option Added: {option}",
"config_updated": "Config Updated" "config_updated": "Config Updated",
"password_submitted": "Password Submitted",
"total_usage": "Total Usage: {usage}",
"setting_on_password": "Setting Password",
"getting_code": "Getting Verification Code, Will Try in 60s"
}, },
"auth": { "auth": {
"title": "Cursor Auth Manager", "title": "Cursor Auth Manager",
@@ -251,6 +272,9 @@
"updating": "Updating to the latest version. The program will restart automatically.", "updating": "Updating to the latest version. The program will restart automatically.",
"up_to_date": "You are using the latest version.", "up_to_date": "You are using the latest version.",
"check_failed": "Failed to check for updates: {error}", "check_failed": "Failed to check for updates: {error}",
"continue_anyway": "Continuing with current version..." "continue_anyway": "Continuing with current version...",
"update_confirm": "Do you want to update to the latest version? (Y/n)",
"update_skipped": "Skipping update.",
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'."
} }
} }

View File

@@ -68,7 +68,23 @@
"version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补", "version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补",
"detecting_version": "检测Cursor版本", "detecting_version": "检测Cursor版本",
"patching_getmachineid": "修补getMachineId", "patching_getmachineid": "修补getMachineId",
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修补getMachineId" "version_greater_than_0_45": "Cursor版本 >= 0.45.0修补getMachineId",
"permission_denied": "权限拒绝: {error}",
"backup_created": "备份已创建",
"update_success": "更新成功",
"update_failed": "更新失败: {error}",
"windows_machine_guid_updated": "Windows机器GUID更新成功",
"reading_package_json": "读取package.json {path}",
"invalid_json_object": "JSON对象无效",
"no_version_field": "package.json中没有版本字段",
"version_field_empty": "版本字段为空",
"invalid_version_format": "版本格式无效: {version}",
"found_version": "找到版本: {version}",
"version_parse_error": "版本解析错误: {error}",
"package_not_found": "package.json未找到: {path}",
"check_version_failed": "检查版本失败: {error}",
"stack_trace": "堆栈跟踪",
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
}, },
"register": { "register": {
"title": "Cursor 注册工具", "title": "Cursor 注册工具",
@@ -141,7 +157,11 @@
"verification_failed": "验证失败", "verification_failed": "验证失败",
"verification_error": "验证错误: {error}", "verification_error": "验证错误: {error}",
"config_option_added": "配置项已添加: {option}", "config_option_added": "配置项已添加: {option}",
"config_updated": "配置已更新" "config_updated": "配置已更新",
"password_submitted": "密码已提交",
"total_usage": "总使用量: {usage}",
"setting_on_password": "设置密码",
"getting_code": "获取验证码将在60秒内尝试..."
}, },
"auth": { "auth": {
"title": "Cursor 认证管理器", "title": "Cursor 认证管理器",
@@ -248,6 +268,9 @@
"updating": "正在更新到最新版本。程序将自动重启。", "updating": "正在更新到最新版本。程序将自动重启。",
"up_to_date": "您使用的是最新版本。", "up_to_date": "您使用的是最新版本。",
"check_failed": "检查更新失败: {error}", "check_failed": "检查更新失败: {error}",
"continue_anyway": "继续使用当前版本..." "continue_anyway": "继续使用当前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳过更新。",
"invalid_choice": "选择无效。请输入 'Y' 或 'n'."
} }
} }

View File

@@ -68,8 +68,25 @@
"version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补", "version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补",
"detecting_version": "檢測Cursor版本", "detecting_version": "檢測Cursor版本",
"patching_getmachineid": "修補getMachineId", "patching_getmachineid": "修補getMachineId",
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修補getMachineId" "version_greater_than_0_45": "Cursor版本 >= 0.45.0修補getMachineId",
"permission_denied": "權限拒絕: {error}",
"backup_created": "備份已創建",
"update_success": "更新成功",
"update_failed": "更新失敗: {error}",
"windows_machine_guid_updated": "Windows機器GUID更新成功",
"reading_package_json": "讀取package.json {path}",
"invalid_json_object": "JSON對象無效",
"no_version_field": "package.json中沒有版本字段",
"version_field_empty": "版本字段為空",
"invalid_version_format": "版本格式無效: {version}",
"found_version": "找到版本: {version}",
"version_parse_error": "版本解析錯誤: {error}",
"package_not_found": "package.json未找到: {path}",
"check_version_failed": "檢查版本失敗: {error}",
"stack_trace": "堆疊跟踪",
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
}, },
"register": { "register": {
"title": "Cursor 註冊工具", "title": "Cursor 註冊工具",
"start": "正在啟動註冊流程...", "start": "正在啟動註冊流程...",
@@ -122,7 +139,11 @@
"verification_failed": "驗證失敗", "verification_failed": "驗證失敗",
"verification_error": "驗證錯誤: {error}", "verification_error": "驗證錯誤: {error}",
"config_option_added": "配置項已添加: {option}", "config_option_added": "配置項已添加: {option}",
"config_updated": "配置已更新" "config_updated": "配置已更新",
"password_submitted": "密碼已提交",
"total_usage": "總使用量: {usage}",
"setting_on_password": "設置密碼",
"getting_code": "正在獲取驗證碼將在60秒內嘗試..."
}, },
"auth": { "auth": {
"title": "Cursor 認證管理器", "title": "Cursor 認證管理器",
@@ -229,6 +250,9 @@
"updating": "正在更新到最新版本。程序將自動重啟。", "updating": "正在更新到最新版本。程序將自動重啟。",
"up_to_date": "您使用的是最新版本。", "up_to_date": "您使用的是最新版本。",
"check_failed": "檢查更新失敗: {error}", "check_failed": "檢查更新失敗: {error}",
"continue_anyway": "繼續使用當前版本..." "continue_anyway": "繼續使用當前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳過更新。",
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'."
} }
} }

19
main.py
View File

@@ -9,6 +9,7 @@ import locale
import platform import platform
import requests import requests
import subprocess import subprocess
from config import get_config
# 只在 Windows 系统上导入 windll # 只在 Windows 系统上导入 windll
if platform.system() == 'Windows': if platform.system() == 'Windows':
@@ -238,6 +239,17 @@ def check_latest_version():
if latest_version != version: if latest_version != version:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.new_version_available', current=version, latest=latest_version)}{Style.RESET_ALL}") print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.new_version_available', current=version, latest=latest_version)}{Style.RESET_ALL}")
# 詢問用戶是否要更新
while True:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('updater.update_confirm', choices='Y/n')}: {Style.RESET_ALL}").lower()
if choice in ['', 'y', 'yes']:
break
elif choice in ['n', 'no']:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.update_skipped')}{Style.RESET_ALL}")
return
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
try: try:
# Execute update command based on platform # Execute update command based on platform
if platform.system() == 'Windows': if platform.system() == 'Windows':
@@ -285,6 +297,13 @@ def check_latest_version():
def main(): def main():
print_logo() print_logo()
# 初始化配置
config = get_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
return
check_latest_version() # Add version check before showing menu check_latest_version() # Add version check before showing menu
print_menu() print_menu()

View File

@@ -7,6 +7,7 @@ from colorama import Fore, Style
import configparser import configparser
from pathlib import Path from pathlib import Path
import sys import sys
from config import get_config
# 在文件开头添加全局变量 # 在文件开头添加全局变量
_translator = None _translator = None
@@ -37,37 +38,51 @@ def signal_handler(signum, frame):
cleanup_chrome_processes(_translator) cleanup_chrome_processes(_translator)
os._exit(0) os._exit(0)
def simulate_human_input(page, url, translator=None): def simulate_human_input(page, url, config, translator=None):
"""访问网址""" """访问网址"""
if translator: if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}") print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
else:
print("正在访问网址...")
# 先访问空白页面 # 先访问空白页面
page.get('about:blank') page.get('about:blank')
time.sleep(random.uniform(1.0, 2.0)) time.sleep(get_random_wait_time(config, 'page_load_wait'))
# 访问目标页面 # 访问目标页面
page.get(url) page.get(url)
time.sleep(random.uniform(2.0, 3.0)) # 等待页面加载 time.sleep(get_random_wait_time(config, 'page_load_wait'))
def fill_signup_form(page, first_name, last_name, email, translator=None): def fill_signup_form(page, first_name, last_name, email, config, translator=None):
"""填写注册表单""" """填写注册表单"""
try: try:
if translator: if translator:
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}") print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}")
else: else:
print("\n正在填写注册表单...") print("\n正在填写注册表单...")
# 填写名字
first_name_input = page.ele("@name=first_name")
if first_name_input:
first_name_input.input(first_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 填写姓氏
last_name_input = page.ele("@name=last_name")
if last_name_input:
last_name_input.input(last_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 填写邮箱
email_input = page.ele("@name=email")
if email_input:
email_input.input(email)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 点击提交按钮
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
# 构建带参数的URL
encoded_email = email.replace('@', '%40')
signup_url = f"https://authenticator.cursor.sh/sign-up/password?first_name={first_name}&last_name={last_name}&email={encoded_email}&redirect_uri=https%3A%2F%2Fcursor.com%2Fapi%2Fauth%2Fcallback"
# 直接访问URL
page.get(signup_url)
time.sleep(random.uniform(2.0, 3.0))
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.form_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.form_success')}{Style.RESET_ALL}")
else: else:
@@ -117,113 +132,41 @@ def get_user_documents_path():
return os.path.join("/home", sudo_user, "Documents") return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents") return os.path.join(os.path.expanduser("~"), "Documents")
def parse_random_time(time_str): def get_random_wait_time(config, timing_type='page_load_wait'):
"""解析随机时间范围配置""" """
Get random wait time from config
Args:
config: ConfigParser object
timing_type: Type of timing to get (page_load_wait, input_wait, submit_wait)
Returns:
float: Random wait time or fixed time
"""
try: try:
if '-' in time_str: if not config.has_section('Timing'):
min_time, max_time = map(float, time_str.split('-')) return random.uniform(0.1, 0.8) # 默认值
elif ',' in time_str:
min_time, max_time = map(float, time_str.split(','))
else:
min_time = max_time = float(time_str)
return min_time, max_time
except:
return 1, 3 # 默认值
def setup_config(translator=None):
"""Setup configuration file and return config object"""
try:
# Set configuration file path
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
# Create config directory (if it doesn't exist)
os.makedirs(config_dir, exist_ok=True)
# Read or create configuration file
config = configparser.ConfigParser()
# 默认配置
default_config = {
'Chrome': {
'chromepath': get_default_chrome_path()
},
'Turnstile': {
'handle_turnstile_time': '2',
'handle_turnstile_random_time': '1-3'
}
}
# Add OS-specific path configurations
if sys.platform == "win32":
appdata = os.getenv("APPDATA")
default_config['WindowsPaths'] = {
'storage_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "storage.json"),
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
'machine_id_path': os.path.join(os.getenv("APPDATA"), "Cursor", "machineId")
}
elif sys.platform == "darwin":
default_config['MacPaths'] = {
'storage_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId")
}
elif sys.platform == "linux":
sudo_user = os.environ.get('SUDO_USER')
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
default_config['LinuxPaths'] = {
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/.config/Cursor/machineId")
}
if os.path.exists(config_file):
config.read(config_file)
config_modified = False
# 检查并添加缺失的配置项
for section, options in default_config.items():
if not config.has_section(section):
config.add_section(section)
config_modified = True
for option, value in options.items():
if not config.has_option(section, option):
config.set(section, option, value)
config_modified = True
if translator:
print(f"{Fore.YELLOW} {translator.get('register.config_option_added', option=f'{section}.{option}') if translator else f'添加配置项: {section}.{option}'}{Style.RESET_ALL}")
# 如果有新增配置项,保存文件
if config_modified:
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN}{translator.get('register.config_updated') if translator else '配置文件已更新'}{Style.RESET_ALL}")
else:
# 创建新配置文件
config = configparser.ConfigParser()
for section, options in default_config.items():
config.add_section(section)
for option, value in options.items():
config.set(section, option, value)
with open(config_file, 'w', encoding='utf-8') as f: if timing_type == 'random':
config.write(f) min_time = float(config.get('Timing', 'min_random_time', fallback='0.1'))
if translator: max_time = float(config.get('Timing', 'max_random_time', fallback='0.8'))
print(f"{Fore.GREEN}{translator.get('register.config_created') if translator else '已创建配置文件'}: {config_file}{Style.RESET_ALL}") return random.uniform(min_time, max_time)
return config time_value = config.get('Timing', timing_type, fallback='0.1-0.8')
except Exception as e: # 检查是否为固定时间值
if translator: if '-' not in time_value and ',' not in time_value:
print(f"{Fore.RED}{translator.get('register.config_setup_error', error=str(e)) if translator else f'配置设置出错: {str(e)}'}{Style.RESET_ALL}") return float(time_value) # 返回固定时间
raise
# 处理范围时间
min_time, max_time = map(float, time_value.split('-' if '-' in time_value else ','))
return random.uniform(min_time, max_time)
except:
return random.uniform(0.1, 0.8) # 出错时返回默认值
def setup_driver(translator=None): def setup_driver(translator=None):
"""Setup browser driver""" """Setup browser driver"""
try: try:
# 获取配置 # 获取配置
config = setup_config(translator) config = get_config(translator)
# Get Chrome path # Get Chrome path
chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path()) chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path())
@@ -289,7 +232,12 @@ def handle_turnstile(page, config, translator=None):
# from config # from config
turnstile_time = float(config.get('Turnstile', 'handle_turnstile_time', fallback='2')) turnstile_time = float(config.get('Turnstile', 'handle_turnstile_time', fallback='2'))
random_time_str = config.get('Turnstile', 'handle_turnstile_random_time', fallback='1-3') random_time_str = config.get('Turnstile', 'handle_turnstile_random_time', fallback='1-3')
min_random_time, max_random_time = parse_random_time(random_time_str)
# 解析随机时间范围
try:
min_time, max_time = map(float, random_time_str.split('-'))
except:
min_time, max_time = 1, 3 # 默认值
max_retries = 2 max_retries = 2
retry_count = 0 retry_count = 0
@@ -322,7 +270,7 @@ def handle_turnstile(page, config, translator=None):
print("检测到验证框...") print("检测到验证框...")
# from config # from config
time.sleep(random.uniform(min_random_time, max_random_time)) time.sleep(random.uniform(min_time, max_time))
challenge_check.click() challenge_check.click()
time.sleep(turnstile_time) # from config time.sleep(turnstile_time) # from config
@@ -348,7 +296,7 @@ def handle_turnstile(page, config, translator=None):
print("验证通过!") print("验证通过!")
return True return True
time.sleep(random.uniform(min_random_time, max_random_time)) time.sleep(random.uniform(min_time, max_time))
if translator: if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}") print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
@@ -393,57 +341,39 @@ def generate_password(length=12):
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*" chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length)) return ''.join(random.choices(chars, k=length))
def fill_password(page, password: str, translator=None) -> bool: def fill_password(page, password: str, config, translator=None):
""" """
填写密码表单 填写密码表单
""" """
try: try:
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else '设置密码'}{Style.RESET_ALL}") print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else '设置密码'}{Style.RESET_ALL}")
# 等待密码框出现并尝试多次 # 填写密码
max_retries = 5 password_input = page.ele("@name=password")
for i in range(max_retries): print(f"{Fore.CYAN}🔑 {translator.get('register.setting_on_password')}: {password}{Style.RESET_ALL}")
# 检查是否出现错误信息 if password_input:
if page.ele("This email is not available."): password_input.input(password)
print(f"{Fore.RED}{translator.get('register.email_used') if translator else '注册失败:邮箱已被使用'}{Style.RESET_ALL}")
return False
# 查找密码输入框 # 点击提交按钮
password_input = page.ele("@name=password") submit_button = page.ele("@type=submit")
if password_input: if submit_button:
# 清除可能存在的旧值并输入新密码 submit_button.click()
password_input.click() time.sleep(get_random_wait_time(config, 'submit_wait'))
time.sleep(random.uniform(0.5, 1))
password_input.input(password)
time.sleep(random.uniform(1, 2))
# 查找并点击提交按钮
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else '密码已提交'}{Style.RESET_ALL}")
time.sleep(random.uniform(2, 3))
return True
else:
print(f"{Fore.YELLOW}⚠️ {translator.get('register.retry_submit') if translator else '未找到提交按钮,重试中...'}{Style.RESET_ALL}")
# 如果没找到密码框,等待后重试 print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else '密码已提交'}{Style.RESET_ALL}")
time.sleep(2)
if i < max_retries - 1: # 不是最后一次尝试时才打印 return True
print(f"{Fore.YELLOW}⚠️ {translator.get('register.retry_password', attempt=i+1) if translator else f'{i+1} 次尝试设置密码...'}{Style.RESET_ALL}")
print(f"{Fore.RED}{translator.get('register.password_set_failed') if translator else '密码设置失败:超过重试次数'}{Style.RESET_ALL}")
return False
except Exception as e: except Exception as e:
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'设置密码时出错: {str(e)}'}{Style.RESET_ALL}") print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'设置密码时出错: {str(e)}'}{Style.RESET_ALL}")
return False return False
def handle_verification_code(browser_tab, email_tab, controller, email, password, translator=None): def handle_verification_code(browser_tab, email_tab, controller, config, translator=None):
"""处理验证码""" """处理验证码"""
try: try:
if translator: if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
# 检查是否使用手动输入验证码 # 检查是否使用手动输入验证码
if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式 if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式
@@ -452,33 +382,33 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 在注册页面填写验证码 # 在注册页面填写验证码
for i, digit in enumerate(verification_code): for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit) browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3)) time.sleep(get_random_wait_time(config, 'verification_code_input'))
print(f"{translator.get('register.verification_success')}") print(f"{translator.get('register.verification_success')}")
time.sleep(3) time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证 # 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator): if handle_turnstile(browser_tab, config, translator):
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(2) time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 访问设置页面 # 访问设置页面
print(f"{Fore.CYAN} {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}") print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings") browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载 time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab return True, browser_tab
return False, None return False, None
# 自动获取验证码逻辑 # 自动获取验证码逻辑
elif email_tab: elif email_tab:
print(f"{translator.get('register.waiting_for_verification_code')}") print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
time.sleep(5) # 等待验证码邮件 time.sleep(get_random_wait_time(config, 'email_check_initial_wait'))
# 使用已有的 email_tab 刷新邮箱 # 使用已有的 email_tab 刷新邮箱
email_tab.refresh_inbox() email_tab.refresh_inbox()
time.sleep(3) time.sleep(get_random_wait_time(config, 'email_refresh_wait'))
# 检查邮箱是否有验证码邮件 # 检查邮箱是否有验证码邮件
if email_tab.check_for_cursor_email(): if email_tab.check_for_cursor_email():
@@ -487,22 +417,23 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 在注册页面填写验证码 # 在注册页面填写验证码
for i, digit in enumerate(verification_code): for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit) browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3)) time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(3) time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证 # 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator): if handle_turnstile(browser_tab, config, translator):
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(2) time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 访问设置页面 # 访问设置页面
if translator: if translator:
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}") print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings") browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载 time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab return True, browser_tab
else: else:
@@ -515,9 +446,9 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 获取验证码,设置超时 # 获取验证码,设置超时
verification_code = None verification_code = None
max_attempts = 20 max_attempts = 20
retry_interval = 10 retry_interval = get_random_wait_time(config, 'retry_interval') # 使用 get_random_wait_time
start_time = time.time() start_time = time.time()
timeout = 160 timeout = float(config.get('Timing', 'max_timeout', fallback='160')) # 這個可以保持不變因為是固定值
if translator: if translator:
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}") print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
@@ -541,29 +472,29 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 刷新邮箱 # 刷新邮箱
email_tab.refresh_inbox() email_tab.refresh_inbox()
time.sleep(retry_interval) time.sleep(retry_interval) # 使用 get_random_wait_time
if verification_code: if verification_code:
# 在注册页面填写验证码 # 在注册页面填写验证码
for i, digit in enumerate(verification_code): for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit) browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3)) time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(3) time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证 # 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator): if handle_turnstile(browser_tab, config, translator):
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(2) time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 直接访问设置页面 # 直接访问设置页面
if translator: if translator:
print(f"{Fore.CYAN}{translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}") print(f"{Fore.CYAN}{translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings") browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载 time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
# 直接返回成功,让 cursor_register.py 处理账户信息获取 # 直接返回成功,让 cursor_register.py 处理账户信息获取
return True, browser_tab return True, browser_tab
@@ -646,14 +577,12 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
# 访问注册页面 # 访问注册页面
url = "https://authenticator.cursor.sh/sign-up" url = "https://authenticator.cursor.sh/sign-up"
if translator:
print(f"\n{Fore.CYAN}{translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
# 访问页面 # 访问页面
simulate_human_input(page, url, translator) simulate_human_input(page, url, config, translator)
if translator: if translator:
print(f"{Fore.CYAN}{translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}") print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}")
time.sleep(5) time.sleep(get_random_wait_time(config, 'page_load_wait'))
# 如果没有提供账号信息,则生成随机信息 # 如果没有提供账号信息,则生成随机信息
if not all([email, password, first_name, last_name]): if not all([email, password, first_name, last_name]):
@@ -670,36 +599,35 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
f.write(f"{'='*50}\n") f.write(f"{'='*50}\n")
# 填写表单 # 填写表单
if fill_signup_form(page, first_name, last_name, email, translator): if fill_signup_form(page, first_name, last_name, email, config, translator):
if translator: if translator:
print(f"\n{Fore.GREEN}{translator.get('register.form_submitted')}{Style.RESET_ALL}") print(f"\n{Fore.GREEN}{translator.get('register.form_submitted')}{Style.RESET_ALL}")
# 处理第一次 Turnstile 验证 # 处理第一次 Turnstile 验证
if handle_turnstile(page, config, translator): if handle_turnstile(page, config, translator):
if translator: if translator:
print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}") print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
# 填写密码 # 填写密码
if fill_password(page, password, translator): if fill_password(page, password, config, translator):
if translator: if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}")
time.sleep(2)
# 处理第二次 Turnstile 验证 # 处理第二次 Turnstile 验证
if handle_turnstile(page, config, translator): if handle_turnstile(page, config, translator):
if translator: if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
if handle_verification_code(page, email_tab, controller, email, password, translator): if handle_verification_code(page, email_tab, controller, config, translator):
success = True success = True
return True, page # 返回浏览器实例 return True, page
else: else:
print(f"\n{Fore.RED} {translator.get('register.verification_code_processing_failed') if translator else '验证码处理失败'}{Style.RESET_ALL}") print(f"\n{Fore.RED} {translator.get('register.verification_code_processing_failed') if translator else '验证码处理失败'}{Style.RESET_ALL}")
else: else:
print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}") print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
else: else:
print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}") print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
else: else:
print(f"\n{Fore.RED} {translator.get('register.first_verification_failed') if translator else '第一次验证失败'}{Style.RESET_ALL}") print(f"\n{Fore.RED} {translator.get('register.first_verification_failed') if translator else '第一次验证失败'}{Style.RESET_ALL}")
return False, None return False, None

View File

@@ -12,6 +12,8 @@ from colorama import Fore, Style, init
from typing import Tuple from typing import Tuple
import configparser import configparser
from new_signup import get_user_documents_path from new_signup import get_user_documents_path
import traceback
from config import get_config
# Initialize colorama # Initialize colorama
init() init()
@@ -29,42 +31,45 @@ EMOJI = {
def get_cursor_paths(translator=None) -> Tuple[str, str]: def get_cursor_paths(translator=None) -> Tuple[str, str]:
""" Get Cursor related paths""" """ Get Cursor related paths"""
system = platform.system() system = platform.system()
paths_map = { # 讀取配置文件
"Darwin": { config = configparser.ConfigParser()
"base": "/Applications/Cursor.app/Contents/Resources/app", config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
"package": "package.json", config_file = os.path.join(config_dir, "config.ini")
"main": "out/main.js",
}, if not os.path.exists(config_file):
"Windows": { raise OSError(translator.get('reset.config_not_found') if translator else "找不到配置文件")
"base": os.path.join(
os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app" config.read(config_file, encoding='utf-8') # 指定編碼
),
"package": "package.json", # 根據系統獲取路徑
"main": "out/main.js", if system == "Darwin":
}, section = 'MacPaths'
"Linux": { elif system == "Windows":
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"], section = 'WindowsPaths'
"package": "package.json", elif system == "Linux":
"main": "out/main.js", section = 'LinuxPaths'
}, else:
}
if system not in paths_map:
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}") raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
if system == "Linux": if not config.has_section(section) or not config.has_option(section, 'cursor_path'):
for base in paths_map["Linux"]["bases"]: raise OSError(translator.get('reset.path_not_configured') if translator else "未配置 Cursor 路徑")
pkg_path = os.path.join(base, paths_map["Linux"]["package"])
if os.path.exists(pkg_path): base_path = config.get(section, 'cursor_path')
return (pkg_path, os.path.join(base, paths_map["Linux"]["main"]))
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径") if not os.path.exists(base_path):
raise OSError(translator.get('reset.path_not_found', path=base_path) if translator else f"找不到 Cursor 路徑: {base_path}")
base_path = paths_map[system]["base"]
return ( pkg_path = os.path.join(base_path, "package.json")
os.path.join(base_path, paths_map[system]["package"]), main_path = os.path.join(base_path, "out/main.js")
os.path.join(base_path, paths_map[system]["main"]),
) # 檢查文件是否存在
if not os.path.exists(pkg_path):
raise OSError(translator.get('reset.package_not_found', path=pkg_path) if translator else f"找不到 package.json: {pkg_path}")
if not os.path.exists(main_path):
raise OSError(translator.get('reset.main_not_found', path=main_path) if translator else f"找不到 main.js: {main_path}")
return (pkg_path, main_path)
def get_cursor_machine_id_path(translator=None) -> str: def get_cursor_machine_id_path(translator=None) -> str:
""" """
@@ -176,11 +181,60 @@ def check_cursor_version(translator) -> bool:
"""Check Cursor version""" """Check Cursor version"""
try: try:
pkg_path, _ = get_cursor_paths(translator) pkg_path, _ = get_cursor_paths(translator)
with open(pkg_path, "r", encoding="utf-8") as f: print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.reading_package_json', path=pkg_path)}{Style.RESET_ALL}")
version = json.load(f)["version"]
return version_check(version, min_version="0.45.0", translator=translator) try:
with open(pkg_path, "r", encoding="utf-8") as f:
data = json.load(f)
except UnicodeDecodeError:
# 如果 UTF-8 讀取失敗,嘗試其他編碼
with open(pkg_path, "r", encoding="latin-1") as f:
data = json.load(f)
if not isinstance(data, dict):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
return False
if "version" not in data:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_version_field')}{Style.RESET_ALL}")
return False
version = str(data["version"]).strip()
if not version:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_field_empty')}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.found_version', version=version)}{Style.RESET_ALL}")
# 檢查版本格式
if not re.match(r"^\d+\.\d+\.\d+$", version):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
return False
# 比較版本
try:
current = tuple(map(int, version.split(".")))
min_ver = (0, 45, 0) # 直接使用元組而不是字符串
if current >= min_ver:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.version_check_passed', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.version_too_low', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
return False
except ValueError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_parse_error', error=str(e))}{Style.RESET_ALL}")
return False
except FileNotFoundError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.package_not_found', path=pkg_path)}{Style.RESET_ALL}")
return False
except json.JSONDecodeError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
return False
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.stack_trace')}: {traceback.format_exc()}{Style.RESET_ALL}")
return False return False
def modify_workbench_js(file_path: str, translator=None) -> bool: def modify_workbench_js(file_path: str, translator=None) -> bool:
@@ -355,7 +409,7 @@ class MachineIDResetter:
if not os.path.exists(config_file): if not os.path.exists(config_file):
raise FileNotFoundError(f"Config file not found: {config_file}") raise FileNotFoundError(f"Config file not found: {config_file}")
config.read(config_file) config.read(config_file, encoding='utf-8')
# Check operating system # Check operating system
if sys.platform == "win32": # Windows if sys.platform == "win32": # Windows
@@ -576,12 +630,12 @@ class MachineIDResetter:
### Remove In v1.7.02 ### Remove In v1.7.02
# Check Cursor version and perform corresponding actions # Check Cursor version and perform corresponding actions
# greater_than_0_45 = check_cursor_version(self.translator) greater_than_0_45 = check_cursor_version(self.translator)
# if greater_than_0_45: if greater_than_0_45:
# print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.detecting_version')} >= 0.45.0{self.translator.get('reset.patching_getmachineid')}{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.detecting_version')} >= 0.45.0{self.translator.get('reset.patching_getmachineid')}{Style.RESET_ALL}")
# patch_cursor_get_machine_id(self.translator) patch_cursor_get_machine_id(self.translator)
# else: else:
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.version_less_than_0_45')}{Style.RESET_ALL}") print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.version_less_than_0_45')}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.success')}{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}{self.translator.get('reset.new_id')}:{Style.RESET_ALL}") print(f"\n{Fore.CYAN}{self.translator.get('reset.new_id')}:{Style.RESET_ALL}")
@@ -637,7 +691,9 @@ class MachineIDResetter:
return False return False
def run(translator=None): def run(translator=None):
"""Convenient function for directly calling the reset function""" config = get_config(translator)
if not config:
return False
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")

View File

@@ -63,8 +63,15 @@ detect_os() {
echo -e "${CYAN} Detected macOS Intel architecture${NC}" echo -e "${CYAN} Detected macOS Intel architecture${NC}"
fi fi
elif [[ "$(uname)" == "Linux" ]]; then elif [[ "$(uname)" == "Linux" ]]; then
OS="linux" # Detect Linux architecture
echo -e "${CYAN} Detected Linux system${NC}" ARCH=$(uname -m)
if [[ "$ARCH" == "aarch64" || "$ARCH" == "arm64" ]]; then
OS="linux_arm64"
echo -e "${CYAN} Detected Linux ARM64 architecture${NC}"
else
OS="linux_x64"
echo -e "${CYAN} Detected Linux x64 architecture${NC}"
fi
else else
# Assume Windows # Assume Windows
OS="windows" OS="windows"
@@ -123,6 +130,16 @@ install_cursor_free_vip() {
download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}" download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
echo -e "${CYAN} New download link: ${download_url}${NC}" echo -e "${CYAN} New download link: ${download_url}${NC}"
if ! curl --output /dev/null --silent --head --fail "$download_url"; then
echo -e "${RED}❌ New download link does not exist${NC}"
exit 1
fi
elif [[ "$OS" == "linux_x64" || "$OS" == "linux_arm64" ]]; then
OS="linux"
binary_name="CursorFreeVIP_${VERSION}_${OS}"
download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
echo -e "${CYAN} New download link: ${download_url}${NC}"
if ! curl --output /dev/null --silent --head --fail "$download_url"; then if ! curl --output /dev/null --silent --head --fail "$download_url"; then
echo -e "${RED}❌ New download link does not exist${NC}" echo -e "${RED}❌ New download link does not exist${NC}"
exit 1 exit 1

32
utils.py Normal file
View File

@@ -0,0 +1,32 @@
import os
import sys
import platform
def get_user_documents_path():
"""Get user documents path"""
if platform.system() == "Windows":
return os.path.expanduser("~\\Documents")
else:
return os.path.expanduser("~/Documents")
def get_default_chrome_path():
"""Get default Chrome path"""
if sys.platform == "win32":
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
elif sys.platform == "darwin":
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
else:
return "/usr/bin/google-chrome"
def get_linux_cursor_path():
"""Get Linux Cursor path"""
possible_paths = [
"/opt/Cursor/resources/app",
"/usr/share/cursor/resources/app",
"/opt/cursor-bin/resources/app",
"/usr/lib/cursor/resources/app",
os.path.expanduser("~/.local/share/cursor/resources/app")
]
# 返回第一个存在的路径,如果都不存在则返回第一个路径
return next((path for path in possible_paths if os.path.exists(path)), possible_paths[0])