Compare commits

...

67 Commits

Author SHA1 Message Date
yeongpin
cd4f36725c refactor: Optimize Signup Flow and Error Handling
- Implement direct URL-based signup with pre-filled parameters
- Enhance password setting process with improved error detection
- Add more robust retry mechanism for password input
- Improve error handling for email availability and submission
- Refactor signup and password setting methods for better reliability
2025-03-10 11:44:37 +08:00
yeongpin
dccb524bd7 Update Readme add some doc 2025-03-10 11:21:42 +08:00
yeongpin
90e9a5b287 feat: Enhance Configuration Management for Cursor Paths
- Add OS-specific path configurations for Cursor storage, SQLite, and machine ID files
- Integrate configuration file management in `new_signup.py` and `reset_machine_manual.py`
- Remove Workbench Cursor path and main.js modification steps
- Improve cross-platform path detection and configuration handling
- Update version to 1.7.02
2025-03-10 11:18:21 +08:00
Pin Studios
66a67fce8b Update README.md 2025-03-07 11:20:04 +08:00
Pin Studios
0981f00b9c Update build.yml 2025-03-07 11:19:49 +08:00
yeongpin
02851c9a09 feat: Implement Configuration Management and Enhance Browser Setup
- Add `setup_config` function to manage configuration file across platforms
- Extract configuration-related code from `setup_driver` into a separate function
- Implement dynamic Chrome path detection for Windows, macOS, and Linux
- Add configurable Turnstile verification settings
- Update README.md with configuration file details
- Enhance localization support for configuration-related messages
- Improve code maintainability and platform compatibility
2025-03-07 11:18:43 +08:00
yeongpin
6312d66813 chore: Bump version to 1.6.03
- Update version in .env file
- Update CHANGELOG.md with version 1.6.03 details
- Minor hotfix and maintenance release
2025-03-06 18:37:04 +08:00
yeongpin
1b1a21f3d7 feat: Add Machine ID File Update Functionality
- Implement `update_machine_id_file` method in `MachineIDResetter` class
- Add new `get_cursor_machine_id_path` function to detect machineId file path across platforms
- Enhance machine ID reset process with file-level machine ID update
- Implement backup mechanism for existing machineId file
- Add cross-platform support for Linux, macOS, and Windows
- Improve error handling and logging with colorful console output
2025-03-06 18:36:00 +08:00
yeongpin
c94bd605e5 fix: Update GitHub Link in Workbench JS Modification
- Correct GitHub repository link to point to specific project repository
- Modify onClick handler to open the correct GitHub page for the project
2025-03-06 18:14:34 +08:00
yeongpin
9f6eee77e0 feat: Add Workbench JS Modification and Cursor Path Detection
- Update `MachineIDResetter` to include workbench.desktop.main.js modification step
- Enhance platform support for Linux, macOS, and Windows
- Update CHANGELOG.md to version 1.6.02
2025-03-06 18:11:40 +08:00
Pin Studios
2bdcc2f633 chore: Bump version to 1.6.01 2025-03-06 12:19:26 +08:00
Pin Studios
4aabe2e403 chore: Bump version to 1.5.04 and improve system compatibility
- Update version in .env file to 1.5.04
- Add Mac-specific run_venv script to .gitignore
- Enhance Cursor Auth platform detection with more precise sys.platform checks
- Add maximum retry mechanism for email creation
- Improve error handling and platform support in cursor_auth.py
2025-03-06 12:18:55 +08:00
yeongpin
005aa2cd95 feat: Improve Version Update Mechanism with Robust Error Handling
- Enhance GitHub API version check with proper headers and timeout
- Add comprehensive error handling for version retrieval
- Implement more reliable update script download and execution
- Improve cross-platform update process with better error reporting
- Add fallback mechanisms for network and update failures
2025-03-06 11:18:21 +08:00
yeongpin
14f6dfc29d fix: Improve Browser Startup and Error Handling
- Add `--no-sandbox` flag to resolve browser startup issues
- Enhance error handling in temp email creation
- Update localization files with new email-related messages
- Improve translation support for email creation process
2025-03-06 11:01:34 +08:00
Pin Studios
2e9bd269ad Update README.md 2025-03-03 15:24:36 +08:00
Pin Studios
f9b7e23253 Update README.md 2025-03-03 15:19:44 +08:00
yeongpin
0d979f7543 chore: Bump default version to 1.5.03 in build workflow 2025-03-03 11:59:15 +08:00
yeongpin
cce3025f7f feat: Enhance Name Generation and Improve Account Registration Process
- Implement realistic name generation with predefined name lists
- Modify first name letter for uniqueness
- Add more descriptive console output with emojis and translations
- Update localization files with new registration-related keys
- Optimize random name generation in registration modules
2025-03-03 11:58:28 +08:00
Pin Studios
fe3e27561b Update .env 2025-03-03 11:17:35 +08:00
yeongpin
bf2bea71eb Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-03-03 11:16:45 +08:00
yeongpin
77a61647dd docs: Update CHANGELOG with version 1.5.01 release notes 2025-03-03 11:10:10 +08:00
yeongpin
813dd4431e feat: Add version update checker with GitHub release support 2025-03-03 11:09:11 +08:00
Pin Studios
d7116b8cf3 Update .env 2025-03-02 13:55:25 +08:00
Pin Studios
f5a7acc4e3 Merge pull request #122 from bingoohuang/main
print account info as early as possible
2025-03-02 13:54:58 +08:00
bingoohuang
479933844a print account info as early as possible 2025-02-28 21:20:12 +08:00
yeongpin
93046d7f03 Update README.md with improved image styling and new screenshot 2025-02-27 10:44:18 +08:00
Pin Studios
e7ca31b710 Update build.yml 2025-02-27 10:33:53 +08:00
Pin Studios
87b99b0d16 Update CHANGELOG.md 2025-02-27 10:29:10 +08:00
Pin Studios
e983b6f560 Update .env 2025-02-27 10:27:18 +08:00
Pin Studios
21ca7ab24f Merge pull request #111 from ahmed98Osama/feature/return-to-menu
Return to main menu after each operation
2025-02-27 10:27:00 +08:00
Ahmed Osama
e7468644a4 feat: return to main menu after each operation instead of exiting 2025-02-26 19:07:14 +02:00
Pin Studios
2bca7d7d14 Update install.sh 2025-02-26 22:34:38 +08:00
Pin Studios
040c5f5836 Update install.ps1 2025-02-26 22:30:56 +08:00
Pin Studios
4a86bbeeb4 Update block_domain.txt 2025-02-26 22:01:53 +08:00
Pin Studios
68b7888a83 Merge pull request #101 from aliensb/main
add support for linux
2025-02-25 23:56:29 +08:00
yeongpin
78dee025a7 feat: Enhance Temporary Email Service with Domain Blocking and Improved Error Handling
- Add domain blocking mechanism to filter out problematic email domains
- Implement dynamic domain list retrieval from external source
- Improve error handling and logging in email creation process
- Add fallback service switching when domains are blocked
- Update localization files with new error and blocking-related messages
- Increment version to 1.4.06
2025-02-25 23:53:55 +08:00
Pin Studios
17c2b4b243 Create block_domain.txt 2025-02-25 23:30:53 +08:00
tom
2acb271f19 add support for linux 2025-02-25 16:52:32 +08:00
Pin Studios
b688b67c26 Update .gitignore 2025-02-25 12:23:45 +08:00
yeongpin
3543615a69 fix: Improve Windows Language Detection and Platform Compatibility
- Correct Windows language detection method in Translator class
- Update platform system check for Windows-specific imports
- Refactor language detection to use platform-specific checks
- Ensure more robust cross-platform language detection logic
2025-02-25 12:16:55 +08:00
yeongpin
24b0a5c09e refactor: Streamline System Language Detection Logic
- Consolidate language detection methods into a single unified approach
- Improve cross-platform language detection for Windows and Unix-like systems
- Simplify error handling and add more robust language code mapping
- Remove platform-specific detection methods in favor of a more generic approach
- Enhance language fallback mechanism with better default handling
2025-02-25 12:14:54 +08:00
yeongpin
5bbba05f69 feat: Improve System Language Detection in Translator Class
- Refactor language detection method with separate methods for Windows, macOS, and Linux
- Add more robust language detection for different platforms
- Improve error handling and fallback mechanisms
- Ensure consistent language code formatting (zh_CN, zh_TW)
- Update Windows language detection using windll with additional error checks
2025-02-25 12:09:13 +08:00
yeongpin
72c95e4b4f refactor: Simplify OS Detection and Download Process
- Streamline OS detection logic by removing detailed architecture checks
- Reduce complexity in download URL generation
- Remove verbose download verification steps
- Simplify error handling during binary download
2025-02-25 12:00:59 +08:00
yeongpin
8afd5df4ea fix: Correct Syntax Error in Installation Script Conditional Logic
- Fix incorrect brace placement in install_cursor_free_vip function
- Remove unnecessary closing brace that was causing syntax error
- Ensure proper flow control in installation script
2025-02-25 11:55:38 +08:00
yeongpin
a7a97b5621 feat: Enhance OS Detection and Download Resilience in Installation Script
- Add Windows system detection
- Implement fallback mechanism for macOS binary downloads
- Improve download link verification with HEAD request
- Add graceful handling for missing architecture-specific binaries
- Provide more informative error messages during download process
2025-02-25 11:54:45 +08:00
yeongpin
e2e2ebc12e feat: Enhance Download Verification in Installation Script
- Add verbose curl download logging
- Implement file size check for downloaded binaries
- Provide detailed error messages for download failures
- Improve download error handling and diagnostics
2025-02-25 11:53:37 +08:00
yeongpin
d131bccac0 feat: Improve macOS Architecture Detection in Installation Script
- Add detailed macOS architecture detection (ARM64 and Intel)
- Enhance system type logging with informative messages
- Provide more precise OS and architecture identification
2025-02-25 11:50:39 +08:00
yeongpin
d852bcff50 feat: Enhance Multilingual Support and System Language Detection
- Add automatic system language detection for Windows and Unix-like systems
- Update localization files with new translation keys
- Improve language handling in various modules
- Translate more UI messages to English
- Add GitHub link to logo display
- Bump version to 1.4.04
2025-02-25 10:46:36 +08:00
Pin Studios
4c91525082 Merge pull request #94 from UntaDotMy/main
feat: Add support for Cursor v0.46.X and improve email service
2025-02-25 10:35:34 +08:00
BilaUnta
b565232ba6 bump version 2025-02-25 09:26:10 +08:00
BilaUnta
5725a2b2f7 improve for 1.4.03 2025-02-25 09:22:22 +08:00
yeongpin
6ec6f8bdbf Add license section to README.md 2025-02-24 12:11:20 +08:00
yeongpin
5c1bcff55a Update Readme 2025-02-24 11:43:54 +08:00
yeongpin
025c4e2ef3 Update readme 2025-02-24 11:30:34 +08:00
yeongpin
6ac1294bee Add Disable Cursor Auto Update Feature 2025-02-24 11:18:57 +08:00
Pin Studios
fb443592d3 Update README.md 2025-02-21 18:02:22 +08:00
Pin Studios
c30a62b072 Update README.md 2025-02-17 16:30:55 +08:00
Pin Studios
8bc509cccf Update README.md 2025-02-17 16:29:16 +08:00
yeongpin
9f814708d1 Update CHANGELOG.md for v1.3.02 release 2025-02-13 15:33:09 +08:00
Pin Studios
1889f9827a Update README.md 2025-02-13 15:28:00 +08:00
yeongpin
57d2d40e67 update coffee 2025-02-13 15:24:40 +08:00
yeongpin
04fa6ee935 update 2025-02-13 15:07:29 +08:00
yeongpin
ed1e0f787e fixed some env 2025-02-13 15:05:45 +08:00
yeongpin
240716e45f fixed small update 2025-02-13 15:05:20 +08:00
yeongpin
35bbe6c93c update cl 2025-02-13 13:25:32 +08:00
yeongpin
fed50a31cc Add manual Cursor registration with manual email input 2025-02-13 13:24:32 +08:00
yeongpin
57ea4dd25a update changelog 2025-02-13 12:16:54 +08:00
29 changed files with 2428 additions and 760 deletions

4
.env
View File

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

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.0.9-dev' default: '1.7.02'
permissions: permissions:
contents: write contents: write
@@ -63,7 +63,7 @@ jobs:
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: CursorFreeVIP_${{ env.VERSION }}_windows.exe name: CursorFreeVIP_${{ env.VERSION }}_windows.exe
path: dist/* path: dist/CursorFreeVIP_${{ env.VERSION }}_windows.exe
build-macos-arm64: build-macos-arm64:
needs: create-tag needs: create-tag
@@ -90,12 +90,13 @@ jobs:
- name: Build MacOS ARM executable - name: Build MacOS ARM executable
run: | run: |
pyinstaller build.spec pyinstaller build.spec
mv "dist/CursorFreeVIP_${{ env.VERSION }}_mac" "dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64"
- name: Upload MacOS ARM artifact - name: Upload MacOS ARM artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64 name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64
path: dist/* path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
build-linux: build-linux:
needs: create-tag needs: create-tag
@@ -120,14 +121,18 @@ jobs:
pip install -r requirements.txt pip install -r requirements.txt
- name: Build Linux executable - name: Build Linux executable
env:
VERSION: ${{ env.VERSION }}
run: | run: |
pyinstaller build.spec pyinstaller build.spec
echo "Contents of dist directory:"
ls -la dist/
- name: Upload Linux artifact - name: Upload Linux artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: CursorFreeVIP_${{ env.VERSION }}_linux name: CursorFreeVIP_${{ env.VERSION }}_linux
path: dist/* path: dist/CursorFreeVIP_${{ env.VERSION }}_linux
build-macos-intel: build-macos-intel:
@@ -155,14 +160,16 @@ jobs:
- name: Build MacOS Intel executable - name: Build MacOS Intel executable
env: env:
TARGET_ARCH: 'x86_64' TARGET_ARCH: 'x86_64'
VERSION: ${{ env.VERSION }}
run: | run: |
arch -x86_64 python3 -m PyInstaller build.spec arch -x86_64 python3 -m PyInstaller build.spec
mv "dist/CursorFreeVIP_${{ env.VERSION }}_mac" "dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel"
- name: Upload MacOS Intel artifact - name: Upload MacOS Intel artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: CursorFreeVIP_${{ env.VERSION }}_mac_intel name: CursorFreeVIP_${{ env.VERSION }}_mac_intel
path: dist/* path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
create-release: create-release:
@@ -182,25 +189,21 @@ jobs:
- name: Prepare release files - name: Prepare release files
run: | run: |
cd artifacts cd artifacts
# 重命名文件为最终的可执行文件名 echo "Contents of artifacts directory:"
mv CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP.exe CursorFreeVIP_${{ env.VERSION }}_windows.exe
mv CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_mac_arm64
mv CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_linux
mv CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_mac_intel
# 删除空目录
rm -rf */
ls -la ls -la
echo "Contents of subdirectories:"
ls -la */
- name: Create Release - name: Create Release
uses: softprops/action-gh-release@v1 uses: softprops/action-gh-release@v1
with: with:
tag_name: v${{ env.VERSION }} tag_name: v${{ env.VERSION }}
files: | files: |
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux artifacts/CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP_${{ env.VERSION }}_linux
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel
draft: false draft: false
prerelease: false prerelease: false
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

51
.gitignore vendored Normal file
View File

@@ -0,0 +1,51 @@
__pycache__
server/
venv/
check_license.py
cursor_modifier.py
reset_machine.py
Run_Venv.bat
token_monitor.py
get_mac.py
.gitignore
build.bat
build.mac.command
build.py
build.sh
ENV/
test.py
install.bat
run.bat
temp_account_info.txt
.env copy
# PyInstaller
build/
dist/
*.spec2
credentials.txt
cursor_accounts.txt
recaptcha.py
install_requirements.bat
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Project specific
*.log
*.db
*.sqlite3
# Mac
run_venv.mac.command

View File

@@ -1,5 +1,111 @@
# Change Log # Change Log
## v1.7.02
1. Fix: Cursor Path | 修復Cursor路徑
2. Add: Config File | 增加配置文件
3. Remove: Workbench Cursor Path | 移除Workbench Cursor路徑
4. Remove: Cursor Main JS | 移除Cursor main.js
## v1.7.01
- Refactoring: Extract configuration-related code from the `setup_driver` function to an independent `setup_config` function
- Optimization: Improve code maintainability and make configuration management and browser settings more clear
- Improvement: The creation and update logic of the configuration file is clearer and more independent
## v1.6.03
1. Hotfix: Small Problem | 修復一些問題
## v1.6.02
1. Hotfix: Small Problem | 修復一些問題
2. Add: Test some Bypass Code | 測試一些繞過代碼
## v1.6.01
1. Fix: Cursor Auth | 修復Cursor Auth
2. Add: Create Account Maximum Retry | 增加創建賬號最大重試次數
3. Fix: Cursor Auth Error | 修復Cursor Auth錯誤
4. Fix: Update Curl Faild | 修復更新Curl失敗
## v1.5.03
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題
2. Small Fix: Error Handling | 小修錯誤處理
3. Small Fix: Translation | 小修翻譯
4. Small Fix: Performance | 小修性能
## v1.5.02
1. Add: Generate Random Name Alias | 增加生成隨機真實姓名
2. Add: Realistic Name Input | 增加真實姓名輸入
3. Optimize: Error Handling | 優化錯誤處理
4. Optimize: Translation | 優化翻譯
5. Optimize: Performance | 優化性能
## v1.5.01
1. Add: Check Latest Version | 增加檢查最新版本
2. Add: Update Command | 增加更新命令
## v1.4.08
1. Add: Print Some Account Info | 增加打印一些賬號信息
## v1.4.07
1. Add Removed break statements after each operation | 修改結束event後的break暫停應用
2. Added print_menu() calls to show the menu again | 添加print_menu調用以再次顯示菜單
3. Updated error handling to show menu instead of exiting | 更新錯誤處理以顯示菜單而不是退出
## v1.4.06
1. Add: Blocked Domains Loaded | 增加被屏蔽的域名加載
2. Fix: Cleanup Error | 修復清理進程時出錯
3. Fix: Blocked Domains Loaded Error | 修復被屏蔽的域名加載錯誤
4. Fix: Available Domains Loaded Error | 修復可用域名加載錯誤
5. Fix: Domains Filtered Error | 修復過濾後剩餘域名錯誤
6. Fix: Domains Excluded Error | 修復排除域名錯誤
## v1.4.05
1. Fix: macOS Language Detection | 修復macOS語言檢測
## v1.4.04
1. Change Some Language Info to English | 更改一些語言信息為英文
2. Add Auto Detect System Language | 增加自動檢測系統語言
3. Fixed Some Issues | 修復一些問題
## v1.4.03
1. Switch to API-based Registration System | 改用API註冊系統替代瀏覽器操作
2. Add Support for Latest Cursor Version | 增加支持最新版本Cursor
3. Enhance Translation System | 優化多語言翻譯系統
4. Add Database Connection Status Messages | 增加數據庫連接狀態提示
5. Improve Error Handling for Database Operations | 改進數據庫操作的錯誤處理
6. Add New API Integration | 新增API集成
7. Optimize Performance and Stability | 優化性能和穩定性
## v1.4.01
1. Add Disable Cursor Auto Upgrade | 增加禁用Cursor自動升級
## v1.3.02
1. Add Buy Me a Coffee | 增加請我喝杯咖啡
2. Add PayPal | 增加PayPal
3. Very Small Fix | 非常小的修復
4. Fix main.py option number | 修復main.py選項數量
## v1.3.01
1. Add Manual Email Input | 增加手動輸入郵箱地址
2. Add Manual Code Input | 增加手動輸入驗證碼
3. Fix Cursor Options | 修復Cursor選項
## v1.2.02
1. Add PBlock | 增加PBlock
2. Remove uBlock0.chromium | 移除uBlock0.chromium
3. Optimize the logic of the script | 優化腳本邏輯
4. Optimize Size | 優化大小
## v1.2.01 ## v1.2.01
@@ -142,4 +248,4 @@
<p align="center"> <p align="center">
<img src="./images/pro_2025-01-11_16-24-03.png" alt="Cursor Pro Logo" width="400"/><br> <img src="./images/pro_2025-01-11_16-24-03.png" alt="Cursor Pro Logo" width="400"/><br>
</p> </p>

View File

@@ -1,7 +1,7 @@
# ➤ Cursor Free VIP # ➤ Cursor Free VIP
<div align="center"> <div align="center">
<p align="center"> <p align="center">
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200"/> <img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
</p> </p>
<p align="center"> <p align="center">
@@ -9,16 +9,17 @@
[![Release](https://img.shields.io/github/v/release/yeongpin/cursor-free-vip?style=flat-square&logo=github&color=blue)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![Release](https://img.shields.io/github/v/release/yeongpin/cursor-free-vip?style=flat-square&logo=github&color=blue)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
[![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/License-CC_BY--NC--ND_4.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-nd/4.0/) [![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/License-CC_BY--NC--ND_4.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-nd/4.0/)
[![Stars](https://img.shields.io/github/stars/yeongpin/cursor-free-vip?style=flat-square&logo=github)](https://github.com/yeongpin/cursor-free-vip/stargazers) [![Stars](https://img.shields.io/github/stars/yeongpin/cursor-free-vip?style=flat-square&logo=github)](https://github.com/yeongpin/cursor-free-vip/stargazers)
[![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.45.11 Version | 支持最新0.45.11版本</h4> <h4>Support Latest 0.46.9 Version | 支持最新0.46.9本</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.
這是一個自動化工具,自動註冊除了Google驗證碼),支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。 這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。
<p align="center"> <p align="center">
<img src="./images/new107_2025-01-15_13-53-56.png" alt="new" width="400"/><br> <img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br>
</p> </p>
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/) ##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
@@ -85,6 +86,28 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
## ❗ Note | 注意事項 ## ❗ Note | 注意事項
📝 Config | 文件配置
`Win / Macos / Linux Path | 路徑 [Documents/.cursor-free-vip/config.ini]`
```
[Chrome]
# Default Google Chrome Path | 默認Google Chrome 遊覽器路徑
chromepath = C:\Program Files\Google/Chrome/Application/chrome.exe
[Turnstile]
# Handle Tuenstile Wait Time | 等待人機驗證時間
handle_turnstile_time = 2
# Handle Tuenstile Wait Random Time (must merge 1-3 or 1,3) | 等待人機驗證隨機時間(必須是 1-3 或者 1,3 這樣的組合)
handle_turnstile_random_time = 1-3
[OSPaths]
storage_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/storage.json
sqlite_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/state.vscdb
machine_id_path = /Users/username/Library/Application Support/Cursor/machineId
```
* Use administrator to run the script <br>請使用管理員身份運行腳本
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br> * Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
* This tool is only for learning and research purposes <br>此工具僅供學習和研究使用<br> * This tool is only for learning and research purposes <br>此工具僅供學習和研究使用<br>
@@ -95,9 +118,9 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
## 🚨 Common Issues | 常見問題 ## 🚨 Common Issues | 常見問題
|如果遇到權限問題,請確保:|If you encounter permission issues, please ensure:| |如果遇到權限問題,請確保:| 此腳本以管理員身份運行 |
|:---:|:---:| |:---:|:---:|
| 此腳本以管理員身份運行 | This script is run with administrator privileges | |If you encounter permission issues, please ensure: | This script is run with administrator privileges |
@@ -106,11 +129,46 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
歡迎提交 Issue 和 Pull Request 歡迎提交 Issue 和 Pull Request
<a href="https://github.com/yeongpin/cursor-free-vip/graphs/contributors">
<img src="https://contrib.rocks/image?repo=yeongpin/cursor-free-vip" />
</a>
<br /><br />
## 📩 Disclaimer | 免責聲明 ## 📩 Disclaimer | 免責聲明
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br> 本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br>
源代碼靈感來之 | Original code inspiration from [Here](https://github.com/hmhm2022/gpt-cursor-auto)
This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne by the user. This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne by the user.
## 💰 Buy Me a Coffee | 請我喝杯咖啡
<div align="center">
<table>
<tr>
<td>
<img src="./images/provi-code.jpg" alt="buy_me_a_coffee" width="280"/><br>
</td>
<td>
<img src="./images/paypal.png" alt="buy_me_a_coffee" width="280"/><br>
</td>
</tr>
</table>
</div>
## ⭐ Star History | 星星數
<div align="center">
[![Star History Chart](https://api.star-history.com/svg?repos=yeongpin/cursor-free-vip&type=Date)](https://star-history.com/#yeongpin/cursor-free-vip&Date)
</div>
## 📝 License | 授權
本項目採用 [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) 授權。
Please refer to the [LICENSE](LICENSE.md) file for details.

3
block_domain.txt Normal file
View File

@@ -0,0 +1,3 @@
oakon.com
famamail.com
2925.com

View File

@@ -2,28 +2,28 @@
chcp 65001 > nul chcp 65001 > nul
cls cls
:: 檢查是否以管理員權限運行 :: Check if running with administrator privileges
net session >nul 2>&1 net session >nul 2>&1
if %errorLevel% == 0 ( if %errorLevel% == 0 (
:: 如果是管理員權限,只創建虛擬環境後就降權運行 :: If running with administrator privileges, create virtual environment and then run with normal user privileges
if not exist venv ( if not exist venv (
echo 正在創建虛擬環境... echo 正在創建虛擬環境...
python -m venv venv python -m venv venv
) )
:: 降權運行剩餘的步驟 :: Run remaining steps with normal user privileges
echo 以普通用戶權限繼續... echo 以普通用戶權限繼續...
powershell -Command "Start-Process -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0 run' -Verb RunAs:NO" powershell -Command "Start-Process -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0 run' -Verb RunAs:NO"
exit /b exit /b
) else ( ) else (
:: 檢查是否是第二階段運行 :: Check if running in second stage
if "%1"=="run" ( if "%1"=="run" (
goto RUN_BUILD goto RUN_BUILD
) else ( ) else (
:: 如果是普通權限且需要創建虛擬環境,請求管理員權限 :: If running with normal privileges and creating virtual environment is required, request administrator privileges
if not exist venv ( if not exist venv (
echo ⚠️ 需要管理員權限來創建虛擬環境 echo ⚠️ Requires administrator privileges to create virtual environment
echo 正在請求管理員權限... echo Requesting administrator privileges...
powershell -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0'" powershell -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0'"
exit /b exit /b
) else ( ) else (
@@ -33,30 +33,30 @@ if %errorLevel% == 0 (
) )
:RUN_BUILD :RUN_BUILD
echo 啟動虛擬環境... echo Starting virtual environment...
call venv\Scripts\activate.bat call venv\Scripts\activate.bat
if errorlevel 1 ( if errorlevel 1 (
echo啟動虛擬環境失敗 echoFailed to start virtual environment
pause pause
exit /b 1 exit /b 1
) )
:: 檢查並安裝缺失的依賴 :: Check and install missing dependencies
echo 檢查依賴... echo Checking dependencies...
for /f "tokens=1" %%i in (requirements.txt) do ( for /f "tokens=1" %%i in (requirements.txt) do (
pip show %%i >nul 2>&1 || ( pip show %%i >nul 2>&1 || (
echo 安裝 %%i... echo Installing %%i...
pip install %%i pip install %%i
) )
) )
echo 開始構建... echo Starting build...
python build.py python build.py
if errorlevel 1 ( if errorlevel 1 (
echo構建失敗 echoBuild failed
pause pause
exit /b 1 exit /b 1
) )
echo完成! echoCompleted!
pause pause

View File

@@ -8,7 +8,7 @@ import shutil
from logo import print_logo from logo import print_logo
from dotenv import load_dotenv from dotenv import load_dotenv
# 忽略特定警告 # Ignore specific warnings
warnings.filterwarnings("ignore", category=SyntaxWarning) warnings.filterwarnings("ignore", category=SyntaxWarning)
class LoadingAnimation: class LoadingAnimation:
@@ -50,21 +50,21 @@ def simulate_progress(message, duration=1.0, steps=20):
progress_bar(i, steps, prefix="Progress:", length=40) progress_bar(i, steps, prefix="Progress:", length=40)
def build(): def build():
# 清理屏幕 # Clean screen
os.system("cls" if platform.system().lower() == "windows" else "clear") os.system("cls" if platform.system().lower() == "windows" else "clear")
# 顯示 logo # Display logo
print_logo() print_logo()
# 清理 PyInstaller 緩存 # Clean PyInstaller cache
print("\033[93m🧹 清理構建緩存...\033[0m") print("\033[93m🧹 Cleaning build cache...\033[0m")
if os.path.exists('build'): if os.path.exists('build'):
shutil.rmtree('build') shutil.rmtree('build')
# 重新加載環境變量以確保獲取最新版本 # Reload environment variables to ensure getting the latest version
load_dotenv(override=True) load_dotenv(override=True)
version = os.getenv('VERSION', '1.0.0') version = os.getenv('VERSION', '1.0.0')
print(f"\033[93m📦 正在構建版本: v{version}\033[0m") print(f"\033[93m📦 Building version: v{version}\033[0m")
try: try:
simulate_progress("Preparing build environment...", 0.5) simulate_progress("Preparing build environment...", 0.5)
@@ -72,7 +72,7 @@ def build():
loading = LoadingAnimation() loading = LoadingAnimation()
loading.start("Building in progress") loading.start("Building in progress")
# 根据系统类型设置输出名称 # Set output name based on system type
system = platform.system().lower() system = platform.system().lower()
if system == "windows": if system == "windows":
os_type = "windows" os_type = "windows"
@@ -86,7 +86,7 @@ def build():
output_name = f"CursorFreeVIP_{version}_{os_type}" output_name = f"CursorFreeVIP_{version}_{os_type}"
# 构建命令 # Build command
build_command = f'pyinstaller --clean --noconfirm build.spec' build_command = f'pyinstaller --clean --noconfirm build.spec'
output_path = os.path.join('dist', f'{output_name}{ext}') output_path = os.path.join('dist', f'{output_name}{ext}')
@@ -95,16 +95,16 @@ def build():
loading.stop() loading.stop()
if os.path.exists(output_path): if os.path.exists(output_path):
print(f"\n\033[92m✅ 構建完成!") print(f"\n\033[92m✅ Build completed!")
print(f"📦 可執行文件位於: {output_path}\033[0m") print(f"📦 Executable file located: {output_path}\033[0m")
else: else:
print("\n\033[91m❌ 構建失敗:未找到輸出文件\033[0m") print("\n\033[91m❌ Build failed: Output file not found\033[0m")
return False return False
except Exception as e: except Exception as e:
if loading: if loading:
loading.stop() loading.stop()
print(f"\n\033[91m❌ 構建過程出錯: {str(e)}\033[0m") print(f"\n\033[91m❌ Build process error: {str(e)}\033[0m")
return False return False
return True return True

View File

@@ -8,7 +8,7 @@ NC='\033[0m' # No Color
# 检查并安装必要的依赖 # 检查并安装必要的依赖
check_dependencies() { check_dependencies() {
echo -e "${YELLOW}检查系统依赖...${NC}" echo -e "${YELLOW}Checking system dependencies...${NC}"
# 检查是否为 Ubuntu/Debian # 检查是否为 Ubuntu/Debian
if [ -f /etc/debian_version ]; then if [ -f /etc/debian_version ]; then
@@ -16,42 +16,42 @@ check_dependencies() {
PACKAGES="python3 python3-pip python3-venv" PACKAGES="python3 python3-pip python3-venv"
for pkg in $PACKAGES; do for pkg in $PACKAGES; do
if ! dpkg -l | grep -q "^ii $pkg "; then if ! dpkg -l | grep -q "^ii $pkg "; then
echo -e "${YELLOW}安装 $pkg...${NC}" echo -e "${YELLOW}Installing $pkg...${NC}"
sudo apt-get update sudo apt-get update
sudo apt-get install -y $pkg sudo apt-get install -y $pkg
fi fi
done done
else else
echo -e "${RED}不支持的系统,请手动安装 python3, pip3 python3-venv${NC}" echo -e "${RED}Unsupported system, please install python3, pip3 and python3-venv manually${NC}"
exit 1 exit 1
fi fi
} }
# 创建并激活虚拟环境 # 创建并激活虚拟环境
setup_venv() { setup_venv() {
echo -e "${GREEN}正在创建虚拟环境...${NC}" echo -e "${GREEN}Creating virtual environment...${NC}"
python3 -m venv venv python3 -m venv venv
echo -e "${GREEN}启动虚拟环境...${NC}" echo -e "${GREEN}Starting virtual environment...${NC}"
. ./venv/bin/activate || source ./venv/bin/activate . ./venv/bin/activate || source ./venv/bin/activate
} }
# 安装依赖 # 安装依赖
install_dependencies() { install_dependencies() {
echo -e "${GREEN}安装依赖...${NC}" echo -e "${GREEN}Installing dependencies...${NC}"
python3 -m pip install --upgrade pip python3 -m pip install --upgrade pip
pip3 install -r requirements.txt pip3 install -r requirements.txt
} }
# 构建程序 # 构建程序
build_program() { build_program() {
echo -e "${GREEN}开始构建...${NC}" echo -e "${GREEN}Starting build...${NC}"
python3 build.py python3 build.py
} }
# 清理 # 清理
cleanup() { cleanup() {
echo -e "${GREEN}清理虚拟环境...${NC}" echo -e "${GREEN}Cleaning virtual environment...${NC}"
deactivate 2>/dev/null || true deactivate 2>/dev/null || true
rm -rf venv rm -rf venv
} }
@@ -73,8 +73,8 @@ main() {
# 清理 # 清理
cleanup cleanup
echo -e "${GREEN}完成!${NC}" echo -e "${GREEN}Completed!${NC}"
echo "按任意键退出..." echo "Press any key to exit..."
# 使用兼容的方式读取输入 # 使用兼容的方式读取输入
if [ "$(uname)" = "Linux" ]; then if [ "$(uname)" = "Linux" ]; then
read dummy read dummy

View File

@@ -45,65 +45,6 @@ class BrowserControl:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.create_new_tab_failed', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.create_new_tab_failed', error=str(e))}{Style.RESET_ALL}")
return None return None
def switch_to_tab(self, browser):
"""切换到指定浏览器窗口"""
try:
self.browser = browser
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.switch_tab_success')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.switch_tab_failed', error=str(e))}{Style.RESET_ALL}")
return False
def get_current_tab(self):
"""获取当前标签页"""
return self.browser
def wait_for_page_load(self, seconds=2):
"""等待页面加载"""
time.sleep(seconds)
def navigate_to(self, url):
"""导航到指定URL"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.navigate_to', url=url)}...{Style.RESET_ALL}")
self.browser.get(url)
self.wait_for_page_load()
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.browser_error', error=str(e))}{Style.RESET_ALL}")
return False
def get_verification_code(self):
"""从邮件中获取验证码"""
try:
# 尝试所有可能的样式组合
selectors = [
# 新样式
'xpath://div[contains(@style, "font-family:-apple-system") and contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "color:#202020")]',
# 带行高的样式
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "line-height:30px")]',
# rgba 颜色样式
'xpath://div[contains(@style, "font-size: 28px") and contains(@style, "letter-spacing: 2px") and contains(@style, "color: rgba(32, 32, 32, 1)")]',
# 宽松样式
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]'
]
# 依次尝试每个选择器
for selector in selectors:
code_div = self.browser.ele(selector)
if code_div:
verification_code = code_div.text.strip()
if verification_code.isdigit() and len(verification_code) == 6:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.found_verification_code')}: {verification_code}{Style.RESET_ALL}")
return verification_code
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.no_valid_verification_code')}{Style.RESET_ALL}")
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_verification_code_error', error=str(e))}{Style.RESET_ALL}")
return None
def fill_verification_code(self, code): def fill_verification_code(self, code):
"""填写验证码""" """填写验证码"""

View File

@@ -22,14 +22,38 @@ class CursorAuth:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
# 判断操作系统 # 判断操作系统
if os.name == "nt": # Windows if sys.platform == "win32": # Windows
self.db_path = os.path.join( self.db_path = os.path.join(
os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb" os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb"
) )
else: # macOS elif sys.platform == 'linux':
self.db_path = os.path.expanduser(
"~/.config/Cursor/User/globalStorage/state.vscdb"
)
elif sys.platform == 'darwin': # macOS
self.db_path = os.path.expanduser( self.db_path = os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb" "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
) )
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform')}{Style.RESET_ALL}")
sys.exit(1)
# 检查数据库文件是否存在
if not os.path.exists(self.db_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_not_found', path=self.db_path)}{Style.RESET_ALL}")
return
# 检查文件权限
if not os.access(self.db_path, os.R_OK | os.W_OK):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_permission_error')}{Style.RESET_ALL}")
return
try:
self.conn = sqlite3.connect(self.db_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
except sqlite3.Error as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_connection_error', error=str(e))}{Style.RESET_ALL}")
return
def update_auth(self, email=None, access_token=None, refresh_token=None): def update_auth(self, email=None, access_token=None, refresh_token=None):
conn = None conn = None
@@ -66,13 +90,16 @@ class CursorAuth:
# 设置要更新的键值对 # 设置要更新的键值对
updates = [] updates = []
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
if email is not None: if email is not None:
updates.append(("cursorAuth/cachedEmail", email)) updates.append(("cursorAuth/cachedEmail", email))
if access_token is not None: if access_token is not None:
updates.append(("cursorAuth/accessToken", access_token)) updates.append(("cursorAuth/accessToken", access_token))
if refresh_token is not None: if refresh_token is not None:
updates.append(("cursorAuth/refreshToken", refresh_token)) updates.append(("cursorAuth/refreshToken", refresh_token))
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
# 使用事务来确保数据完整性 # 使用事务来确保数据完整性
cursor.execute("BEGIN TRANSACTION") cursor.execute("BEGIN TRANSACTION")
@@ -110,5 +137,3 @@ class CursorAuth:
if conn: if conn:
conn.close() conn.close()
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}") print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")

View File

@@ -10,10 +10,10 @@ from reset_machine_manual import MachineIDResetter
os.environ["PYTHONVERBOSE"] = "0" os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0" os.environ["PYINSTALLER_VERBOSE"] = "0"
# 初始化colorama # Initialize colorama
init() init()
# 定义emoji常量 # Define emoji constants
EMOJI = { EMOJI = {
'START': '🚀', 'START': '🚀',
'FORM': '📝', 'FORM': '📝',
@@ -33,7 +33,7 @@ EMOJI = {
class CursorRegistration: class CursorRegistration:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
# 设置为显示模式 # Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False' os.environ['BROWSER_HEADLESS'] = 'False'
self.browser_manager = BrowserManager() self.browser_manager = BrowserManager()
self.browser = None self.browser = None
@@ -47,39 +47,49 @@ class CursorRegistration:
# 账号信息 # 账号信息
self.password = self._generate_password() self.password = self._generate_password()
self.first_name = self._generate_name() # Generate first name and last name separately
self.last_name = self._generate_name() first_name = random.choice([
"James", "John", "Robert", "Michael", "William", "David", "Joseph", "Thomas",
"Emma", "Olivia", "Ava", "Isabella", "Sophia", "Mia", "Charlotte", "Amelia",
"Liam", "Noah", "Oliver", "Elijah", "Lucas", "Mason", "Logan", "Alexander"
])
self.last_name = random.choice([
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Anderson", "Wilson", "Taylor", "Thomas", "Moore", "Martin", "Jackson", "Lee",
"Thompson", "White", "Harris", "Clark", "Lewis", "Walker", "Hall", "Young"
])
# Modify first letter of first name
new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
self.first_name = new_first_letter + first_name[1:]
print(f"\n{Fore.CYAN}{EMOJI['PASSWORD']} {self.translator.get('register.password')}: {self.password} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.first_name')}: {self.first_name} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.last_name')}: {self.last_name} {Style.RESET_ALL}")
def _generate_password(self, length=12): def _generate_password(self, length=12):
"""Generate Random Password""" """Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*" chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length)) return ''.join(random.choices(chars, k=length))
def _generate_name(self, length=6):
"""Generate Random Name"""
first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
rest_letters = ''.join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length-1))
return first_letter + rest_letters
def setup_email(self): def setup_email(self):
"""设置邮箱""" """Setup Email"""
try: try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}")
# 使用 new_tempemail 创建临时邮箱,传入 translator # Create a temporary email using new_tempemail, passing translator
from new_tempemail import NewTempEmail from new_tempemail import NewTempEmail
self.temp_email = NewTempEmail(self.translator) # 传入 translator self.temp_email = NewTempEmail(self.translator) # Pass translator
# 创建临时邮箱 # Create a temporary email
email_address = self.temp_email.create_email() email_address = self.temp_email.create_email()
if not email_address: if not email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_create_failed')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_create_failed')}{Style.RESET_ALL}")
return False return False
# 保存邮箱地址和浏览器实例 # Save email address
self.email_address = email_address self.email_address = email_address
self.email_tab = self.temp_email # 传递 NewTempEmail 实例而不是 page self.email_tab = self.temp_email # Pass NewTempEmail instance
self.controller = BrowserControl(self.temp_email.page, self.translator)
return True return True
@@ -93,10 +103,10 @@ class CursorRegistration:
try: try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
# 直接使用 new_signup.py 进行注册 # Directly use new_signup.py to sign up
from new_signup import main as new_signup_main from new_signup import main as new_signup_main
# 执行新的注册流程,传入 translator # Execute the new registration process, passing translator
result, browser_tab = new_signup_main( result, browser_tab = new_signup_main(
email=self.email_address, email=self.email_address,
password=self.password, password=self.password,
@@ -108,11 +118,11 @@ class CursorRegistration:
) )
if result: if result:
# 使用返回的浏览器实例获取账户信息 # Use the returned browser instance to get account information
self.signup_tab = browser_tab # 保存浏览器实例 self.signup_tab = browser_tab # Save browser instance
success = self._get_account_info() success = self._get_account_info()
# 获取信息后关闭浏览器 # Close browser after getting information
if browser_tab: if browser_tab:
try: try:
browser_tab.quit() browser_tab.quit()
@@ -127,7 +137,7 @@ class CursorRegistration:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
return False return False
finally: finally:
# 确保在任何情况下都关闭浏览器 # Ensure browser is closed in any case
if browser_tab: if browser_tab:
try: try:
browser_tab.quit() browser_tab.quit()
@@ -135,7 +145,7 @@ class CursorRegistration:
pass pass
def _get_account_info(self): def _get_account_info(self):
"""获取账户信息和 Token""" """Get Account Information and Token"""
try: try:
self.signup_tab.get(self.settings_url) self.signup_tab.get(self.settings_url)
time.sleep(2) time.sleep(2)
@@ -150,6 +160,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['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
@@ -186,7 +197,7 @@ class CursorRegistration:
return False return False
def _save_account_info(self, token, total_usage): def _save_account_info(self, token, total_usage):
"""保存账户信息到文件""" """Save Account Information to File"""
try: try:
# 先更新认证信息 # 先更新认证信息
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
@@ -201,7 +212,7 @@ class CursorRegistration:
if not resetter.reset_machine_ids(): # 直接调用reset_machine_ids方法 if not resetter.reset_machine_ids(): # 直接调用reset_machine_ids方法
raise Exception("Failed to reset machine ID") raise Exception("Failed to reset machine ID")
# 保存账户信息到文件 # Save account information to file
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f: with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n") f.write(f"\n{'='*50}\n")
f.write(f"Email: {self.email_address}\n") f.write(f"Email: {self.email_address}\n")
@@ -218,7 +229,7 @@ class CursorRegistration:
return False return False
def start(self): def start(self):
"""启动注册流程""" """Start Registration Process"""
try: try:
if self.setup_email(): if self.setup_email():
if self.register_cursor(): if self.register_cursor():
@@ -226,7 +237,7 @@ class CursorRegistration:
return True return True
return False return False
finally: finally:
# 关闭邮箱标签页 # Close email tab
if hasattr(self, 'temp_email'): if hasattr(self, 'temp_email'):
try: try:
self.temp_email.close() self.temp_email.close()
@@ -234,7 +245,7 @@ class CursorRegistration:
pass pass
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None): def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
"""更新Cursor的认证信息的便捷函数""" """Update Cursor Auth Info"""
auth_manager = CursorAuth(translator=self.translator) auth_manager = CursorAuth(translator=self.translator)
return auth_manager.update_auth(email, access_token, refresh_token) return auth_manager.update_auth(email, access_token, refresh_token)

273
cursor_register_manual.py Normal file
View File

@@ -0,0 +1,273 @@
import os
from colorama import Fore, Style, init
import time
import random
from browser import BrowserManager
from control import BrowserControl
from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter
os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0"
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'FORM': '📝',
'VERIFY': '🔄',
'PASSWORD': '🔑',
'CODE': '📱',
'DONE': '',
'ERROR': '',
'WAIT': '',
'SUCCESS': '',
'MAIL': '📧',
'KEY': '🔐',
'UPDATE': '🔄',
'INFO': ''
}
class CursorRegistration:
def __init__(self, translator=None):
self.translator = translator
# Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser_manager = BrowserManager()
self.browser = None
self.controller = None
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
self.settings_url = "https://www.cursor.com/settings"
self.email_address = None
self.signup_tab = None
self.email_tab = None
# Generate account information
self.password = self._generate_password()
# Generate first name and last name separately
first_name = random.choice([
"James", "John", "Robert", "Michael", "William", "David", "Joseph", "Thomas",
"Emma", "Olivia", "Ava", "Isabella", "Sophia", "Mia", "Charlotte", "Amelia",
"Liam", "Noah", "Oliver", "Elijah", "Lucas", "Mason", "Logan", "Alexander"
])
self.last_name = random.choice([
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Anderson", "Wilson", "Taylor", "Thomas", "Moore", "Martin", "Jackson", "Lee",
"Thompson", "White", "Harris", "Clark", "Lewis", "Walker", "Hall", "Young"
])
# Modify first letter of first name
new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
self.first_name = new_first_letter + first_name[1:]
print(f"\n{Fore.CYAN}{EMOJI['PASSWORD']} {self.translator.get('register.password')}: {self.password} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.first_name')}: {self.first_name} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.last_name')}: {self.last_name} {Style.RESET_ALL}")
def _generate_password(self, length=12):
"""Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def setup_email(self):
"""Setup Email"""
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.manual_email_input') if self.translator else 'Please enter your email address:'}")
self.email_address = input().strip()
if '@' not in self.email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}\n{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_setup_failed', error=str(e))}{Style.RESET_ALL}")
return False
def get_verification_code(self):
"""Manually Get Verification Code"""
try:
print(f"{Fore.CYAN}{EMOJI['CODE']} {self.translator.get('register.manual_code_input') if self.translator else 'Please enter the verification code:'}")
code = input().strip()
if not code.isdigit() or len(code) != 6:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_code') if self.translator else '无效的验证码'}{Style.RESET_ALL}")
return None
return code
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.code_input_failed', error=str(e))}{Style.RESET_ALL}")
return None
def register_cursor(self):
"""Register Cursor"""
browser_tab = None
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
# Use new_signup.py directly for registration
from new_signup import main as new_signup_main
# Execute new registration process, passing translator
result, browser_tab = new_signup_main(
email=self.email_address,
password=self.password,
first_name=self.first_name,
last_name=self.last_name,
email_tab=None, # No email tab needed
controller=self, # Pass self instead of self.controller
translator=self.translator
)
if result:
# Use the returned browser instance to get account information
self.signup_tab = browser_tab # Save browser instance
success = self._get_account_info()
# Close browser after getting information
if browser_tab:
try:
browser_tab.quit()
except:
pass
return success
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
return False
finally:
# Ensure browser is closed in any case
if browser_tab:
try:
browser_tab.quit()
except:
pass
def _get_account_info(self):
"""Get Account Information and Token"""
try:
self.signup_tab.get(self.settings_url)
time.sleep(2)
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.signup_tab.ele(usage_selector)
total_usage = "未知"
if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip()
print(f"Total Usage: {total_usage}\n")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30
retry_interval = 2
attempts = 0
while attempts < max_attempts:
try:
cookies = self.signup_tab.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
token = cookie["value"].split("%3A%3A")[1]
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
self._save_account_info(token, total_usage)
return True
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_max_attempts', max=max_attempts)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_failed', error=str(e))}{Style.RESET_ALL}")
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.account_error', error=str(e))}{Style.RESET_ALL}")
return False
def _save_account_info(self, token, total_usage):
"""Save Account Information to File"""
try:
# Update authentication information first
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}")
# Reset machine ID
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
resetter = MachineIDResetter(self.translator) # Create instance with translator
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
raise Exception("Failed to reset machine ID")
# Save account information to file
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {self.email_address}\n")
f.write(f"Password: {self.password}\n")
f.write(f"Token: {token}\n")
f.write(f"Usage Limit: {total_usage}\n")
f.write(f"{'='*50}\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}")
return False
def start(self):
"""Start Registration Process"""
try:
if self.setup_email():
if self.register_cursor():
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}")
return True
return False
finally:
# Close email tab
if hasattr(self, 'temp_email'):
try:
self.temp_email.close()
except:
pass
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
"""Convenient function to update Cursor authentication information"""
auth_manager = CursorAuth(translator=self.translator)
return auth_manager.update_auth(email, access_token, refresh_token)
def main(translator=None):
"""Main function to be called from main.py"""
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['START']} {translator.get('register.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
registration = CursorRegistration(translator)
registration.start()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('register.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
main(main_translator)

135
disable_auto_update.py Normal file
View File

@@ -0,0 +1,135 @@
import os
import sys
import platform
import shutil
from colorama import Fore, Style, init
import subprocess
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
"PROCESS": "🔄",
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"FOLDER": "📁",
"FILE": "📄",
"STOP": "🛑",
"CHECK": "✔️"
}
class AutoUpdateDisabler:
def __init__(self, translator=None):
self.translator = translator
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"),
"Linux": os.path.expanduser("~/.config/cursor-updater")
}
def _kill_cursor_processes(self):
"""End all Cursor processes"""
try:
print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('update.killing_processes') if self.translator else '正在结束 Cursor 进程...'}{Style.RESET_ALL}")
if self.system == "Windows":
subprocess.run(['taskkill', '/F', '/IM', 'Cursor.exe', '/T'], capture_output=True)
else:
subprocess.run(['pkill', '-f', 'Cursor'], capture_output=True)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.processes_killed') if self.translator else 'Cursor 进程已结束'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.kill_process_failed', error=str(e)) if self.translator else f'结束进程失败: {e}'}{Style.RESET_ALL}")
return False
def _remove_updater_directory(self):
"""Delete updater directory"""
try:
updater_path = self.updater_paths.get(self.system)
if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
print(f"{Fore.CYAN}{EMOJI['FOLDER']} {self.translator.get('update.removing_directory') if self.translator else '正在删除更新程序目录...'}{Style.RESET_ALL}")
if os.path.exists(updater_path):
if os.path.isdir(updater_path):
shutil.rmtree(updater_path)
else:
os.remove(updater_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}")
return False
def _create_blocking_file(self):
"""Create blocking file"""
try:
updater_path = self.updater_paths.get(self.system)
if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}")
# Create empty file
open(updater_path, 'w').close()
# Set read-only attribute
if self.system == "Windows":
os.system(f'attrib +r "{updater_path}"')
else:
os.chmod(updater_path, 0o444) # Set to read-only
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.create_block_file_failed', error=str(e)) if self.translator else f'创建阻止文件失败: {e}'}{Style.RESET_ALL}")
return False
def disable_auto_update(self):
"""Disable auto update"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('update.start_disable') if self.translator else '开始禁用自动更新...'}{Style.RESET_ALL}")
# 1. End processes
if not self._kill_cursor_processes():
return False
# 2. Delete directory
if not self._remove_updater_directory():
return False
# 3. Create blocking file
if not self._create_blocking_file():
return False
print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.disable_failed', error=str(e)) if self.translator else f'禁用自动更新失败: {e}'}{Style.RESET_ALL}")
return False
def run(translator=None):
"""Convenient function for directly calling the disable function"""
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['STOP']} {translator.get('update.title') if translator else 'Disable Cursor Auto Update'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
disabler = AutoUpdateDisabler(translator)
disabler.disable_auto_update()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('update.press_enter') if translator else 'Press Enter to Continue...'}")
if __name__ == "__main__":
from main import translator as main_translator
run(main_translator)

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
images/paypal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
images/provi-code.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

View File

@@ -2,15 +2,17 @@
"menu": { "menu": {
"title": "Available Options", "title": "Available Options",
"exit": "Exit Program", "exit": "Exit Program",
"reset": "Reset Machine Manual", "reset": "Reset Machine ID",
"register": "Register Cursor", "register": "Register New Cursor Account",
"quit": "Quit Cursor", "register_manual": "Register Cursor with Custom Email",
"select_language": "Select Language", "quit": "Close Cursor Application",
"input_choice": "Enter your choice ({choices})", "select_language": "Change Language",
"invalid_choice": "Invalid choice. Please try again", "input_choice": "Please enter your choice ({choices})",
"program_terminated": "Program terminated by user", "invalid_choice": "Invalid selection. Please enter a number from {choices}",
"error_occurred": "An error occurred: {error}", "program_terminated": "Program was terminated by user",
"press_enter": "Press Enter to Exit" "error_occurred": "An error occurred: {error}. Please try again",
"press_enter": "Press Enter to Exit",
"disable_auto_update": "Disable Cursor Auto-Update"
}, },
"languages": { "languages": {
"en": "English", "en": "English",
@@ -62,25 +64,29 @@
"patch_completed": "Patching getMachineId Completed", "patch_completed": "Patching getMachineId Completed",
"patch_failed": "Patching getMachineId Failed: {error}", "patch_failed": "Patching getMachineId Failed: {error}",
"version_check_passed": "Cursor Version Check Passed", "version_check_passed": "Cursor Version Check Passed",
"file_modified": "File Modified" "file_modified": "File Modified",
"version_less_than_0_45": "Cursor Version < 0.45.0, Skip Patching getMachineId",
"detecting_version": "Detecting Cursor Version",
"patching_getmachineid": "Patching getMachineId",
"version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId"
}, },
"register": { "register": {
"title": "Cursor Registration Tool", "title": "Cursor Registration Tool",
"start": "Starting Registration Process", "start": "Starting registration process...",
"handling_turnstile": "Handling Turnstile", "handling_turnstile": "Processing security verification...",
"retry_verification": "Retry Verification", "retry_verification": "Retrying verification...",
"detect_turnstile": "Detect Turnstile", "detect_turnstile": "Checking security verification...",
"verification_success": "Verification Success", "verification_success": "Security verification successful",
"starting_browser": "Starting Browser", "starting_browser": "Opening browser...",
"form_success": "Form Success", "form_success": "Form submitted successfully",
"browser_started": "Browser Started", "browser_started": "Browser opened successfully",
"waiting_for_second_verification": "Waiting for Second Verification", "waiting_for_second_verification": "Waiting for email verification...",
"waiting_for_verification_code": "Waiting for Verification Code", "waiting_for_verification_code": "Waiting for verification code...",
"password_success": "Password Set Successfully", "password_success": "Password set successfully",
"password_error": "Password Set Failed: {error}", "password_error": "Could not set password: {error}. Please try again",
"waiting_for_page_load": "Waiting for Page Load", "waiting_for_page_load": "Loading page...",
"first_verification_passed": "First Verification Passed", "first_verification_passed": "Initial verification successful",
"mailbox": "Successfully Entered Mailbox", "mailbox": "Successfully accessed email inbox",
"register_start": "Start Register", "register_start": "Start Register",
"form_submitted": "Form Submitted, Start Verification...", "form_submitted": "Form Submitted, Start Verification...",
"filling_form": "Fill Form", "filling_form": "Fill Form",
@@ -123,7 +129,19 @@
"get_email_address": "Get Email Address", "get_email_address": "Get Email Address",
"update_cursor_auth_info": "Update Cursor Auth Info", "update_cursor_auth_info": "Update Cursor Auth Info",
"register_process_error": "Register Process Error: {error}", "register_process_error": "Register Process Error: {error}",
"setting_password": "Setting Password" "setting_password": "Setting Password",
"manual_code_input": "Manual Code Input",
"manual_email_input": "Manual Email Input",
"password": "Password",
"first_name": "First Name",
"last_name": "Last Name",
"exit_signal": "Exit Signal",
"email_address": "Email Address",
"config_created": "Config Created",
"verification_failed": "Verification Failed",
"verification_error": "Verification Error: {error}",
"config_option_added": "Config Option Added: {option}",
"config_updated": "Config Updated"
}, },
"auth": { "auth": {
"title": "Cursor Auth Manager", "title": "Cursor Auth Manager",
@@ -141,7 +159,10 @@
"database_connection_closed": "Database Connection Closed", "database_connection_closed": "Database Connection Closed",
"database_updated_successfully": "Database Updated Successfully", "database_updated_successfully": "Database Updated Successfully",
"connected_to_database": "Connected to Database", "connected_to_database": "Connected to Database",
"updating_pair": "Updating Key-Value Pair" "updating_pair": "Updating Key-Value Pair",
"db_not_found": "Database file not found at: {path}",
"db_permission_error": "Cannot access database file. Please check permissions",
"db_connection_error": "Failed to connect to database: {error}"
}, },
"control": { "control": {
"generate_email": "Generating New Email", "generate_email": "Generating New Email",
@@ -178,7 +199,7 @@
}, },
"email": { "email": {
"starting_browser": "Starting Browser", "starting_browser": "Starting Browser",
"visiting_site": "Visiting smailpro.com", "visiting_site": "Visiting mail.tm",
"create_success": "Email Created Successfully", "create_success": "Email Created Successfully",
"create_failed": "Failed to Create Email", "create_failed": "Failed to Create Email",
"create_error": "Email Creation Error: {error}", "create_error": "Email Creation Error: {error}",
@@ -192,6 +213,44 @@
"verification_code_found": "Verification Code Found", "verification_code_found": "Verification Code Found",
"verification_code_not_found": "Verification Code Not Found", "verification_code_not_found": "Verification Code Not Found",
"verification_code_error": "Verification Code Error: {error}", "verification_code_error": "Verification Code Error: {error}",
"address": "Email Address" "address": "Email Address",
"all_domains_blocked": "All Domains Blocked, Switching Service",
"no_available_domains_after_filtering": "No Available Domains After Filtering",
"switching_service": "Switching to {service} Service",
"domains_list_error": "Failed to Get Domains List: {error}",
"failed_to_get_available_domains": "Failed to Get Available Domains",
"domains_excluded": "Domains Excluded: {domains}",
"failed_to_create_account": "Failed to Create Account",
"account_creation_error": "Account Creation Error: {error}",
"blocked_domains": "Blocked Domains: {domains}",
"blocked_domains_loaded": "Blocked Domains Loaded: {count}",
"blocked_domains_loaded_error": "Blocked Domains Loaded Error: {error}",
"blocked_domains_loaded_success": "Blocked Domains Loaded Successfully",
"blocked_domains_loaded_timeout": "Blocked Domains Loaded Timeout: {timeout}s",
"blocked_domains_loaded_timeout_error": "Blocked Domains Loaded Timeout Error: {error}",
"available_domains_loaded": "Available Domains Loaded: {count}",
"domains_filtered": "Domains Filtered: {count}",
"trying_to_create_email": "Trying to create email: {email}"
},
"update": {
"title": "Disable Cursor Auto Update",
"disable_success": "Auto Update Disabled Successfully",
"disable_failed": "Disable Auto Update Failed: {error}",
"press_enter": "Press Enter to Exit",
"start_disable": "Start Disabling Auto Update",
"killing_processes": "Killing Processes",
"processes_killed": "Processes Killed",
"removing_directory": "Removing Directory",
"directory_removed": "Directory Removed",
"creating_block_file": "Creating Block File",
"block_file_created": "Block File Created"
},
"updater": {
"checking": "Checking for updates...",
"new_version_available": "New version available! (Current: {current}, Latest: {latest})",
"updating": "Updating to the latest version. The program will restart automatically.",
"up_to_date": "You are using the latest version.",
"check_failed": "Failed to check for updates: {error}",
"continue_anyway": "Continuing with current version..."
} }
} }

View File

@@ -3,14 +3,16 @@
"title": "可用选项", "title": "可用选项",
"exit": "退出程序", "exit": "退出程序",
"reset": "重置机器标识", "reset": "重置机器标识",
"register": "注册 Cursor", "register": "注册 Cursor 账号",
"quit": "退出 Cursor", "register_manual": "使用自定义邮箱注册",
"select_language": "选择语言", "quit": "关闭 Cursor 应用",
"input_choice": "输入选择 ({choices})", "select_language": "更改语言",
"invalid_choice": "无效选择,请重试", "input_choice": "请输入您的选择 ({choices})",
"program_terminated": "程序被用户终止", "invalid_choice": "选择无效,请输入 {choices} 范围内的数字",
"error_occurred": "发生错误: {error}", "program_terminated": "程序已被用户终止",
"press_enter": "按回车键退出" "error_occurred": "发生错误:{error},请重试",
"press_enter": "按回车键退出",
"disable_auto_update": "禁用 Cursor 自动更新"
}, },
"languages": { "languages": {
"en": "English", "en": "English",
@@ -62,29 +64,33 @@
"patch_completed": "getMachineId修补完成", "patch_completed": "getMachineId修补完成",
"patch_failed": "getMachineId修补失败: {error}", "patch_failed": "getMachineId修补失败: {error}",
"version_check_passed": "Cursor版本检查通过", "version_check_passed": "Cursor版本检查通过",
"file_modified": "文件已修改" "file_modified": "文件已修改",
"version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补",
"detecting_version": "检测Cursor版本",
"patching_getmachineid": "修补getMachineId",
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修补getMachineId"
}, },
"register": { "register": {
"title": "Cursor 注册工具", "title": "Cursor 注册工具",
"start": "开始注册流程", "start": "正在启动注册流程...",
"browser_started": "浏览器已启动", "browser_started": "浏览器已成功打开",
"password_success": "密码设置成", "password_success": "密码设置成",
"password_error": "密码设置失败: {error}", "password_error": "无法设置密码:{error},请重试",
"waiting_for_page_load": "等待页面加载", "waiting_for_page_load": "页面加载中...",
"mailbox": "成功进入邮箱", "mailbox": "成功访问邮箱",
"waiting_for_second_verification": "等待第二阶段验证", "waiting_for_second_verification": "等待邮箱验证...",
"waiting_for_verification_code": "等待验证码", "waiting_for_verification_code": "等待验证码...",
"first_verification_passed": "第一阶段验证通过", "first_verification_passed": "初始验证通过",
"register_start": "开始注册流程", "register_start": "开始注册流程",
"form_submitted": "表单已提交,开始验证...", "form_submitted": "表单已提交,开始验证...",
"filling_form": "填写注册信息", "filling_form": "填写注册信息",
"visiting_url": "访问URL", "visiting_url": "访问URL",
"basic_info": "基本信息提交完成", "basic_info": "基本信息提交完成",
"handling_turnstile": "处理 Turnstile 验证", "handling_turnstile": "正在处理安全验证...",
"retry_verification": "重试验证", "retry_verification": "正在重试验证...",
"detect_turnstile": "检测 Turnstile 验证", "detect_turnstile": "正在检查安全验证...",
"verification_success": "验证成功", "verification_success": "安全验证通过",
"starting_browser": "启动浏览器", "starting_browser": "正在打开浏览器...",
"form_success": "表单提交成功", "form_success": "表单提交成功",
"handle_turnstile": "处理 Turnstile 验证", "handle_turnstile": "处理 Turnstile 验证",
"no_turnstile": "未检测到 Turnstile 验证", "no_turnstile": "未检测到 Turnstile 验证",
@@ -123,7 +129,19 @@
"get_email_address": "获取邮箱地址", "get_email_address": "获取邮箱地址",
"register_process_error": "注册流程错误: {error}", "register_process_error": "注册流程错误: {error}",
"update_cursor_auth_info": "更新Cursor认证信息", "update_cursor_auth_info": "更新Cursor认证信息",
"setting_password": "设置密码" "setting_password": "设置密码",
"manual_code_input": "手动输入验证码",
"manual_email_input": "手动输入邮箱",
"password": "密码",
"first_name": "名字",
"last_name": "姓氏",
"exit_signal": "退出信号",
"email_address": "邮箱地址",
"config_created": "配置已创建",
"verification_failed": "验证失败",
"verification_error": "验证错误: {error}",
"config_option_added": "配置项已添加: {option}",
"config_updated": "配置已更新"
}, },
"auth": { "auth": {
"title": "Cursor 认证管理器", "title": "Cursor 认证管理器",
@@ -140,7 +158,10 @@
"connected_to_database": "已连接到数据库", "connected_to_database": "已连接到数据库",
"database_updated_successfully": "数据库更新成功", "database_updated_successfully": "数据库更新成功",
"database_connection_closed": "数据库连接已关闭", "database_connection_closed": "数据库连接已关闭",
"updating_pair": "更新键值对" "updating_pair": "更新键值对",
"db_not_found": "未找到数据库文件:{path}",
"db_permission_error": "无法访问数据库文件,请检查权限",
"db_connection_error": "连接数据库失败:{error}"
}, },
"control": { "control": {
"generate_email": "生成新邮箱", "generate_email": "生成新邮箱",
@@ -175,7 +196,7 @@
}, },
"email": { "email": {
"starting_browser": "启动浏览器", "starting_browser": "启动浏览器",
"visiting_site": "访问 smailpro.com", "visiting_site": "访问 mail.tm",
"create_success": "邮箱创建成功", "create_success": "邮箱创建成功",
"create_failed": "邮箱创建失败", "create_failed": "邮箱创建失败",
"create_error": "邮箱创建错误: {error}", "create_error": "邮箱创建错误: {error}",
@@ -189,6 +210,44 @@
"verification_code_found": "找到验证码", "verification_code_found": "找到验证码",
"verification_code_not_found": "未找到验证码", "verification_code_not_found": "未找到验证码",
"verification_code_error": "验证码错误: {error}", "verification_code_error": "验证码错误: {error}",
"address": "邮箱地址" "address": "邮箱地址",
"all_domains_blocked": "所有域名都被屏蔽了,切换服务",
"no_available_domains_after_filtering": "过滤后没有可用域名",
"switching_service": "切换到 {service} 服务",
"domains_list_error": "获取域名列表失败: {error}",
"failed_to_get_available_domains": "获取可用域名失败",
"blocked_domains_loaded": "加载了 {count} 个被屏蔽的域名",
"domains_excluded": "排除了 {domains} 个被屏蔽的域名",
"failed_to_create_account": "创建账户失败",
"account_creation_error": "账户创建错误: {error}",
"blocked_domains": "被屏蔽的域名: {domains}",
"blocked_domains_loaded_error": "加载被屏蔽的域名失败: {error}",
"blocked_domains_loaded_success": "加载被屏蔽的域名成功",
"blocked_domains_loaded_timeout": "加载被屏蔽的域名超时: {timeout}秒",
"blocked_domains_loaded_timeout_error": "加载被屏蔽的域名超时错误: {error}",
"available_domains_loaded": "获取到 {count} 个可用域名",
"domains_filtered": "过滤后剩餘 {count} 個可用域名",
"trying_to_create_email": "尝试创建邮箱: {email}"
},
"update": {
"title": "禁用 Cursor 自动更新",
"disable_success": "自动更新禁用成功",
"disable_failed": "禁用自动更新失败: {error}",
"press_enter": "按回车键退出",
"start_disable": "开始禁用自动更新",
"killing_processes": "杀死进程",
"processes_killed": "进程已杀死",
"removing_directory": "删除目录",
"directory_removed": "目录已删除",
"creating_block_file": "创建阻止文件",
"block_file_created": "阻止文件已创建"
},
"updater": {
"checking": "检查更新...",
"new_version_available": "有新版本可用! (当前版本: {current}, 最新版本: {latest})",
"updating": "正在更新到最新版本。程序将自动重启。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "检查更新失败: {error}",
"continue_anyway": "继续使用当前版本..."
} }
} }

View File

@@ -1,16 +1,18 @@
{ {
"menu": { "menu": {
"title": "可用選項", "title": "可用選項",
"exit": "退出程", "exit": "退出程",
"reset": "重置機器識", "reset": "重置機器識別碼",
"register": "註冊 Cursor", "register": "註冊 Cursor 帳號",
"quit": "退出 Cursor", "register_manual": "使用自訂郵箱註冊",
"select_language": "選擇語言", "quit": "關閉 Cursor 應用程式",
"input_choice": "輸入選擇 ({choices})", "select_language": "變更語言",
"invalid_choice": "無效選擇,請重試", "input_choice": "請輸入您的選擇 ({choices})",
"program_terminated": "程序被用戶終止", "invalid_choice": "選擇無效,請輸入 {choices} 範圍內的數字",
"error_occurred": "發生錯誤: {error}", "program_terminated": "程式已被使用者終止",
"press_enter": "按回車鍵退出" "error_occurred": "發生錯誤:{error},請重試",
"press_enter": "按返回鍵退出",
"disable_auto_update": "停用 Cursor 自動更新"
}, },
"languages": { "languages": {
"en": "English", "en": "English",
@@ -62,53 +64,38 @@
"patch_completed": "getMachineId修補完成", "patch_completed": "getMachineId修補完成",
"patch_failed": "getMachineId修補失敗: {error}", "patch_failed": "getMachineId修補失敗: {error}",
"version_check_passed": "Cursor版本檢查通過", "version_check_passed": "Cursor版本檢查通過",
"file_modified": "文件已修改" "file_modified": "文件已修改",
"version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补",
"detecting_version": "檢測Cursor版本",
"patching_getmachineid": "修補getMachineId",
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修補getMachineId"
}, },
"register": { "register": {
"title": "Cursor 註冊工具", "title": "Cursor 註冊工具",
"start": "開始註冊流程", "start": "正在啟動註冊流程...",
"mailbox": "成功進入郵箱", "handling_turnstile": "正在處理安全驗證...",
"browser_started": "瀏覽器已啟動", "retry_verification": "正在重試驗證...",
"waiting_for_page_load": "等待頁面加載", "detect_turnstile": "正在檢查安全驗證...",
"password_success": "密碼設置完成", "verification_success": "安全驗證通過",
"password_error": "密碼設置失敗: {error}", "starting_browser": "正在開啟瀏覽器...",
"form_success": "表單提交成功",
"browser_started": "瀏覽器已成功開啟",
"waiting_for_second_verification": "等待郵箱驗證...",
"waiting_for_verification_code": "等待驗證碼...",
"password_success": "密碼設定成功",
"password_error": "無法設定密碼:{error},請重試",
"waiting_for_page_load": "頁面載入中...",
"first_verification_passed": "初始驗證通過",
"mailbox": "已成功存取郵箱",
"visiting_url": "訪問URL", "visiting_url": "訪問URL",
"first_verification_passed": "第一階段驗證通過",
"register_start": "開始註冊流程", "register_start": "開始註冊流程",
"form_submitted": "表單已提交,開始驗證...", "form_submitted": "表單已提交,開始驗證...",
"waiting_for_second_verification": "等待第二階段驗證",
"filling_form": "填寫註冊信息", "filling_form": "填寫註冊信息",
"basic_info": "基本信息提交完成", "basic_info": "基本信息提交完成",
"handle_turnstile": "處理 Turnstile 驗證", "handle_turnstile": "處理 Turnstile 驗證",
"no_turnstile": "未檢測到 Turnstile 驗證", "no_turnstile": "未檢測到 Turnstile 驗證",
"turnstile_passed": "驗證通過", "turnstile_passed": "驗證通過",
"verification_start": "開始獲取驗證碼", "verification_start": "開始獲取驗證碼",
"waiting_for_verification_code": "等待驗證碼",
"handling_turnstile": "處理 Turnstile 驗證",
"retry_verification": "重試驗證",
"detect_turnstile": "檢測 Turnstile 驗證",
"verification_success": "驗證成功",
"starting_browser": "啟動瀏覽器",
"form_success": "表單提交成功",
"verification_timeout": "獲取驗證碼超時",
"verification_not_found": "未找到驗證碼",
"try_get_code": "第 {attempt} 次嘗試獲取驗證碼 | 剩餘時間: {time}秒",
"get_account": "獲取賬戶信息",
"get_token": "獲取 Cursor Session Token",
"token_success": "Token 獲取成功",
"token_attempt": "第 {attempt} 次嘗試未獲取到 Token{time}秒後重試",
"token_max_attempts": "已達到最大嘗試次數({max}),獲取 Token 失敗",
"token_failed": "獲取 Token 失敗: {error}",
"account_error": "獲取賬戶信息失敗: {error}",
"press_enter": "按回車鍵退出",
"browser_start": "正在啟動瀏覽器",
"open_mailbox": "正在打開郵箱頁面",
"email_error": "獲取郵箱地址失敗",
"setup_error": "郵箱設置出錯: {error}",
"start_getting_verification_code": "開始獲取驗證碼將在60秒內嘗試...",
"get_verification_code_timeout": "獲取驗證碼超時",
"get_verification_code_success": "成功獲取驗證碼",
"try_get_verification_code": "第 {attempt} 次嘗試未獲取到驗證碼,剩餘時間: {remaining_time}秒",
"verification_code_filled": "驗證碼填寫完成", "verification_code_filled": "驗證碼填寫完成",
"login_success_and_jump_to_settings_page": "成功登錄並跳轉到設置頁面", "login_success_and_jump_to_settings_page": "成功登錄並跳轉到設置頁面",
"detect_login_page": "檢測到登錄頁面,開始登錄...", "detect_login_page": "檢測到登錄頁面,開始登錄...",
@@ -123,7 +110,19 @@
"get_email_address": "獲取郵箱地址", "get_email_address": "獲取郵箱地址",
"register_process_error": "註冊流程錯誤: {error}", "register_process_error": "註冊流程錯誤: {error}",
"update_cursor_auth_info": "更新Cursor認證信息", "update_cursor_auth_info": "更新Cursor認證信息",
"setting_password": "設置密碼" "setting_password": "設置密碼",
"manual_code_input": "手動輸入驗證碼",
"manual_email_input": "手動輸入郵箱地址",
"password": "密碼",
"first_name": "名字",
"last_name": "姓氏",
"exit_signal": "退出信號",
"email_address": "郵箱地址",
"config_created": "配置已創建",
"verification_failed": "驗證失敗",
"verification_error": "驗證錯誤: {error}",
"config_option_added": "配置項已添加: {option}",
"config_updated": "配置已更新"
}, },
"auth": { "auth": {
"title": "Cursor 認證管理器", "title": "Cursor 認證管理器",
@@ -140,7 +139,10 @@
"connected_to_database": "已連接到數據庫", "connected_to_database": "已連接到數據庫",
"database_updated_successfully": "數據庫更新成功", "database_updated_successfully": "數據庫更新成功",
"database_connection_closed": "數據庫連接已關閉", "database_connection_closed": "數據庫連接已關閉",
"updating_pair": "更新鍵值對" "updating_pair": "更新鍵值對",
"db_not_found": "未找到數據庫文件:{path}",
"db_permission_error": "無法訪問數據庫文件,請檢查權限",
"db_connection_error": "連接數據庫失敗:{error}"
}, },
"control": { "control": {
"generate_email": "生成新郵箱", "generate_email": "生成新郵箱",
@@ -175,7 +177,7 @@
}, },
"email": { "email": {
"starting_browser": "啟動瀏覽器", "starting_browser": "啟動瀏覽器",
"visiting_site": "訪問 smailpro.com", "visiting_site": "訪問 mail.tm",
"create_success": "郵箱創建成功", "create_success": "郵箱創建成功",
"create_failed": "郵箱創建失敗", "create_failed": "郵箱創建失敗",
"create_error": "郵箱創建錯誤: {error}", "create_error": "郵箱創建錯誤: {error}",
@@ -189,7 +191,44 @@
"verification_code_found": "找到驗證碼", "verification_code_found": "找到驗證碼",
"verification_code_not_found": "未找到驗證碼", "verification_code_not_found": "未找到驗證碼",
"verification_code_error": "驗證碼錯誤: {error}", "verification_code_error": "驗證碼錯誤: {error}",
"address": "郵箱地址" "address": "郵箱地址",
} "all_domains_blocked": "所有域名都被屏蔽了,切換服務",
"no_available_domains_after_filtering": "過濾後沒有可用域名",
"switching_service": "切換到 {service} 服務",
"domains_list_error": "獲取域名列表失敗: {error}",
"failed_to_get_available_domains": "獲取可用域名失敗",
"domains_excluded": "排除的域名: {domains}",
"failed_to_create_account": "創建帳戶失敗",
"account_creation_error": "帳戶創建錯誤: {error}",
"blocked_domains": "被屏蔽的域名: {domains}",
"blocked_domains_loaded": "加載被屏蔽的域名: {domains}",
"blocked_domains_loaded_error": "加載被屏蔽的域名失敗: {error}",
"blocked_domains_loaded_success": "加載被屏蔽的域名成功",
"blocked_domains_loaded_timeout": "加載被屏蔽的域名超時: {timeout}秒",
"blocked_domains_loaded_timeout_error": "加載被屏蔽的域名超時錯誤: {error}",
"available_domains_loaded": "獲取到 {count} 個可用域名",
"domains_filtered": "過濾後剩餘 {count} 個可用域名",
"trying_to_create_email": "嘗試創建郵箱: {email}"
},
"update": {
"title": "禁用 Cursor 自动更新",
"disable_success": "自動更新禁用成功",
"disable_failed": "禁用自動更新失敗: {error}",
"press_enter": "按回車鍵退出",
"start_disable": "開始禁用自動更新",
"killing_processes": "殺死進程",
"processes_killed": "進程已殺死",
"removing_directory": "刪除目錄",
"directory_removed": "目錄已刪除",
"creating_block_file": "創建阻止文件",
"block_file_created": "阻止文件已創建"
},
"updater": {
"checking": "檢查更新...",
"new_version_available": "有新版本可用! (當前版本: {current}, 最新版本: {latest})",
"updating": "正在更新到最新版本。程序將自動重啟。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "檢查更新失敗: {error}",
"continue_anyway": "繼續使用當前版本..."
}
} }

14
logo.py
View File

@@ -2,17 +2,17 @@ from colorama import Fore, Style, init
from dotenv import load_dotenv from dotenv import load_dotenv
import os import os
# 獲取當前腳本所在目錄 # Get the current script directory
current_dir = os.path.dirname(os.path.abspath(__file__)) current_dir = os.path.dirname(os.path.abspath(__file__))
# 構建.env文件的完整路徑 # Build the full path to the .env file
env_path = os.path.join(current_dir, '.env') env_path = os.path.join(current_dir, '.env')
# 加載環境變量,指定.env文件路徑 # Load environment variables, specifying the .env file path
load_dotenv(env_path) load_dotenv(env_path)
# 獲取版本號,如果未找到則使用默認值 # Get the version number, using the default value if not found
version = os.getenv('VERSION', '1.0.0') version = os.getenv('VERSION', '1.0.0')
# 初始化 colorama # Initialize colorama
init() init()
CURSOR_LOGO = f""" CURSOR_LOGO = f"""
@@ -27,8 +27,10 @@ CURSOR_LOGO = f"""
Pro Version Activator v{version} Pro Version Activator v{version}
{Fore.GREEN} {Fore.GREEN}
Author: Pin Studios | yeongpin Author: Pin Studios | yeongpin
Github: https://github.com/yeongpin/cursor-free-vip
{Fore.RED} {Fore.RED}
Press 4 to change language | 按下 4 键切换语言 Press 5 to change language | 按下 5 键切换语言
{Style.RESET_ALL} {Style.RESET_ALL}
""" """

277
main.py
View File

@@ -3,8 +3,18 @@
import os import os
import sys import sys
import json import json
from logo import print_logo from logo import print_logo, version
from colorama import Fore, Style, init from colorama import Fore, Style, init
import locale
import platform
import requests
import subprocess
# 只在 Windows 系统上导入 windll
if platform.system() == 'Windows':
import ctypes
# 只在 Windows 上导入 windll
from ctypes import windll
# 初始化colorama # 初始化colorama
init() init()
@@ -19,48 +29,144 @@ EMOJI = {
"RESET": "🔄", "RESET": "🔄",
"MENU": "📋", "MENU": "📋",
"ARROW": "", "ARROW": "",
"LANG": "🌐" "LANG": "🌐",
"UPDATE": "🔄"
} }
class Translator: class Translator:
def __init__(self): def __init__(self):
self.current_language = 'zh_tw' # 默认语言
self.translations = {} self.translations = {}
self.current_language = self.detect_system_language() # 使用正确的方法名
self.fallback_language = 'en' # Fallback language if translation is missing
self.load_translations() self.load_translations()
def load_translations(self): def detect_system_language(self):
"""加载所有可用的翻译""" """Detect system language and return corresponding language code"""
locales_dir = os.path.join(os.path.dirname(__file__), 'locales') try:
if hasattr(sys, '_MEIPASS'): system = platform.system()
locales_dir = os.path.join(sys._MEIPASS, 'locales')
for file in os.listdir(locales_dir): if system == 'Windows':
if file.endswith('.json'): return self._detect_windows_language()
lang_code = file[:-5] # 移除 .json else:
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f: return self._detect_unix_language()
self.translations[lang_code] = json.load(f)
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Failed to detect system language: {e}{Style.RESET_ALL}")
return 'en'
def _detect_windows_language(self):
"""Detect language on Windows systems"""
try:
# 确保我们在 Windows 上
if platform.system() != 'Windows':
return 'en'
# 获取键盘布局
user32 = ctypes.windll.user32
hwnd = user32.GetForegroundWindow()
threadid = user32.GetWindowThreadProcessId(hwnd, 0)
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
# Map language ID to our language codes
language_map = {
0x0409: 'en', # English
0x0404: 'zh_tw', # Traditional Chinese
0x0804: 'zh_cn', # Simplified Chinese
}
return language_map.get(layout_id, 'en')
except:
return self._detect_unix_language()
def _detect_unix_language(self):
"""Detect language on Unix-like systems (Linux, macOS)"""
try:
# Get the system locale
system_locale = locale.getdefaultlocale()[0]
if not system_locale:
return 'en'
system_locale = system_locale.lower()
# Map locale to our language codes
if system_locale.startswith('zh_tw') or system_locale.startswith('zh_hk'):
return 'zh_tw'
elif system_locale.startswith('zh_cn'):
return 'zh_cn'
elif system_locale.startswith('en'):
return 'en'
# Try to get language from LANG environment variable as fallback
env_lang = os.getenv('LANG', '').lower()
if 'tw' in env_lang or 'hk' in env_lang:
return 'zh_tw'
elif 'cn' in env_lang:
return 'zh_cn'
return 'en'
except:
return 'en'
def load_translations(self):
"""Load all available translations"""
try:
locales_dir = os.path.join(os.path.dirname(__file__), 'locales')
if hasattr(sys, '_MEIPASS'):
locales_dir = os.path.join(sys._MEIPASS, 'locales')
if not os.path.exists(locales_dir):
print(f"{Fore.RED}{EMOJI['ERROR']} Locales directory not found{Style.RESET_ALL}")
return
for file in os.listdir(locales_dir):
if file.endswith('.json'):
lang_code = file[:-5] # Remove .json
try:
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
self.translations[lang_code] = json.load(f)
except (json.JSONDecodeError, UnicodeDecodeError) as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error loading {file}: {e}{Style.RESET_ALL}")
continue
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to load translations: {e}{Style.RESET_ALL}")
def get(self, key, **kwargs): def get(self, key, **kwargs):
"""获取翻译文本""" """Get translated text with fallback support"""
try:
# Try current language
result = self._get_translation(self.current_language, key)
if result == key and self.current_language != self.fallback_language:
# Try fallback language if translation not found
result = self._get_translation(self.fallback_language, key)
return result.format(**kwargs) if kwargs else result
except Exception:
return key
def _get_translation(self, lang_code, key):
"""Get translation for a specific language"""
try: try:
keys = key.split('.') keys = key.split('.')
value = self.translations.get(self.current_language, {}) value = self.translations.get(lang_code, {})
for k in keys: for k in keys:
if isinstance(value, dict): if isinstance(value, dict):
value = value.get(k, key) value = value.get(k, key)
else: else:
return key # 如果中間值不是字典返回原始key return key
return value.format(**kwargs) if kwargs else value return value
except Exception: except Exception:
return key # 出現任何錯誤時返回原始key return key
def set_language(self, lang_code): def set_language(self, lang_code):
"""设置当前语言""" """Set current language with validation"""
if lang_code in self.translations: if lang_code in self.translations:
self.current_language = lang_code self.current_language = lang_code
return True return True
return False return False
def get_available_languages(self):
"""Get list of available languages"""
return list(self.translations.keys())
# 创建翻译器实例 # 创建翻译器实例
translator = Translator() translator = Translator()
@@ -71,38 +177,120 @@ def print_menu():
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}") print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}") print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}") print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}") print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
print(f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}") print(f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}")
print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}") print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
def select_language(): def select_language():
"""语言选择菜单""" """Language selection menu"""
print(f"\n{Fore.CYAN}{EMOJI['LANG']} {translator.get('menu.select_language')}:{Style.RESET_ALL}") print(f"\n{Fore.CYAN}{EMOJI['LANG']} {translator.get('menu.select_language')}:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}") print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
languages = translator.get('languages') languages = translator.get_available_languages()
for i, (code, name) in enumerate(languages.items()): for i, lang in enumerate(languages):
print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {name}") lang_name = translator.get(f"languages.{lang}")
print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {lang_name}")
try: try:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-' + str(len(languages)-1))}: {Style.RESET_ALL}") choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{len(languages)-1}')}: {Style.RESET_ALL}")
if choice.isdigit() and 0 <= int(choice) < len(languages): if choice.isdigit() and 0 <= int(choice) < len(languages):
lang_code = list(languages.keys())[int(choice)] translator.set_language(languages[int(choice)])
translator.set_language(lang_code)
return True return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
return False
except (ValueError, IndexError): except (ValueError, IndexError):
pass print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
return False
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
return False def check_latest_version():
"""Check if current version matches the latest release version"""
try:
print(f"\n{Fore.CYAN}{EMOJI['UPDATE']} {translator.get('updater.checking')}{Style.RESET_ALL}")
# Get latest version from GitHub API with timeout and proper headers
headers = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'CursorFreeVIP-Updater'
}
response = requests.get(
"https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest",
headers=headers,
timeout=10
)
# Check if response is successful
if response.status_code != 200:
raise Exception(f"GitHub API returned status code {response.status_code}")
response_data = response.json()
if "tag_name" not in response_data:
raise Exception("No version tag found in GitHub response")
latest_version = response_data["tag_name"].lstrip('v')
# Validate version format
if not latest_version:
raise Exception("Invalid version format received")
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}")
try:
# Execute update command based on platform
if platform.system() == 'Windows':
update_command = 'irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex'
subprocess.run(['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', update_command], check=True)
else:
# For Linux/Mac, download and execute the install script
install_script_url = 'https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh'
# First verify the script exists
script_response = requests.get(install_script_url, timeout=5)
if script_response.status_code != 200:
raise Exception("Installation script not found")
# Save and execute the script
with open('install.sh', 'wb') as f:
f.write(script_response.content)
os.chmod('install.sh', 0o755) # Make executable
subprocess.run(['./install.sh'], check=True)
# Clean up
if os.path.exists('install.sh'):
os.remove('install.sh')
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.updating')}{Style.RESET_ALL}")
sys.exit(0)
except Exception as update_error:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.update_failed', error=str(update_error))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.manual_update_required')}{Style.RESET_ALL}")
return
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}")
except requests.exceptions.RequestException as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.network_error', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.continue_anyway')}{Style.RESET_ALL}")
return
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.check_failed', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.continue_anyway')}{Style.RESET_ALL}")
return
def main(): def main():
print_logo() print_logo()
check_latest_version() # Add version check before showing menu
print_menu() print_menu()
while True: while True:
try: try:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-4')}: {Style.RESET_ALL}") choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-6')}: {Style.RESET_ALL}")
if choice == "0": if choice == "0":
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}") print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
@@ -111,19 +299,27 @@ def main():
elif choice == "1": elif choice == "1":
import reset_machine_manual import reset_machine_manual
reset_machine_manual.run(translator) reset_machine_manual.run(translator)
break print_menu()
elif choice == "2": elif choice == "2":
import cursor_register import cursor_register
cursor_register.main(translator) cursor_register.main(translator)
break print_menu()
elif choice == "3": elif choice == "3":
import cursor_register_manual
cursor_register_manual.main(translator)
print_menu()
elif choice == "4":
import quit_cursor import quit_cursor
quit_cursor.quit_cursor(translator) quit_cursor.quit_cursor(translator)
break print_menu()
elif choice == "4": elif choice == "5":
if select_language(): if select_language():
print_menu() print_menu()
continue continue
elif choice == "6":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
else: else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu() print_menu()
@@ -134,10 +330,7 @@ def main():
return return
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.error_occurred', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.error_occurred', error=str(e))}{Style.RESET_ALL}")
break print_menu()
print(f"\n{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('menu.press_enter')}...{Style.RESET_ALL}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -4,6 +4,9 @@ import os
import signal import signal
import random import random
from colorama import Fore, Style from colorama import Fore, Style
import configparser
from pathlib import Path
import sys
# 在文件开头添加全局变量 # 在文件开头添加全局变量
_translator = None _translator = None
@@ -56,31 +59,15 @@ def fill_signup_form(page, first_name, last_name, email, translator=None):
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(random.uniform(0.5, 1.0))
# 填写姓氏
last_name_input = page.ele("@name=last_name")
if last_name_input:
last_name_input.input(last_name)
time.sleep(random.uniform(0.5, 1.0))
# 填写邮箱
email_input = page.ele("@name=email")
if email_input:
email_input.input(email)
time.sleep(random.uniform(0.5, 1.0))
# 点击提交按钮
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(random.uniform(2.0, 3.0))
# 构建带参数的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:
@@ -94,40 +81,204 @@ def fill_signup_form(page, first_name, last_name, email, translator=None):
print(f"填写表单时出错: {e}") print(f"填写表单时出错: {e}")
return False return False
def setup_driver(translator=None): def get_default_chrome_path():
"""设置浏览器驱动""" """Get default Chrome path"""
co = ChromiumOptions() if sys.platform == "win32":
paths = [
# 使用无痕模式 os.path.join(os.environ.get('PROGRAMFILES', ''), 'Google/Chrome/Application/chrome.exe'),
co.set_argument("--incognito") os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Google/Chrome/Application/chrome.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google/Chrome/Application/chrome.exe')
# 设置随机端口 ]
co.auto_port() elif sys.platform == "darwin":
paths = [
# 使用有头模式(一定要设置为False模擬人類操作) "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
co.headless(False) ]
else: # Linux
paths = [
"/usr/bin/google-chrome",
"/usr/bin/google-chrome-stable"
]
for path in paths:
if os.path.exists(path):
return path
return ""
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
return os.path.join(os.path.expanduser("~"), "Documents")
elif sys.platform == "darwin":
return os.path.join(os.path.expanduser("~"), "Documents")
else: # Linux
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
def parse_random_time(time_str):
"""解析随机时间范围配置"""
try: try:
# 加载插件 if '-' in time_str:
extension_path = os.path.join(os.getcwd(), "turnstilePatch") min_time, max_time = map(float, time_str.split('-'))
if os.path.exists(extension_path): elif ',' in time_str:
co.set_argument("--allow-extensions-in-incognito") min_time, max_time = map(float, time_str.split(','))
co.add_extension(extension_path) 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:
config.write(f)
if translator:
print(f"{Fore.GREEN}{translator.get('register.config_created') if translator else '已创建配置文件'}: {config_file}{Style.RESET_ALL}")
return config
except Exception as e: except Exception as e:
if translator: if translator:
print(f"{Fore.RED}{translator.get('register.extension_load_error', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{translator.get('register.config_setup_error', error=str(e)) if translator else f'配置设置出错: {str(e)}'}{Style.RESET_ALL}")
else: raise
print(f"加载插件失败: {e}")
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.starting_browser')}{Style.RESET_ALL}")
else:
print("正在启动浏览器...")
page = ChromiumPage(co)
return page
def handle_turnstile(page, translator=None): def setup_driver(translator=None):
"""Setup browser driver"""
try:
# 获取配置
config = setup_config(translator)
# Get Chrome path
chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path())
if not chrome_path or not os.path.exists(chrome_path):
if translator:
print(f"{Fore.YELLOW}⚠️ {translator.get('register.chrome_path_invalid') if translator else 'Chrome路径无效使用默认路径'}{Style.RESET_ALL}")
chrome_path = get_default_chrome_path()
# Set browser options
co = ChromiumOptions()
# Set Chrome path
co.set_browser_path(chrome_path)
# Use incognito mode
co.set_argument("--incognito")
# 设置随机端口
co.set_argument("--no-sandbox")
# 设置随机端口
co.auto_port()
# 使用有头模式(一定要设置为False模拟人类操作)
co.headless(False)
try:
# 加载插件
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
if os.path.exists(extension_path):
co.set_argument("--allow-extensions-in-incognito")
co.add_extension(extension_path)
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.extension_load_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"加载插件失败: {e}")
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.starting_browser')}{Style.RESET_ALL}")
else:
print("正在启动浏览器...")
page = ChromiumPage(co)
return config, page
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.browser_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"设置浏览器时出错: {e}")
raise
def handle_turnstile(page, config, translator=None):
"""处理 Turnstile 验证""" """处理 Turnstile 验证"""
try: try:
if translator: if translator:
@@ -135,6 +286,11 @@ def handle_turnstile(page, translator=None):
else: else:
print("\n正在处理 Turnstile 验证...") print("\n正在处理 Turnstile 验证...")
# from config
turnstile_time = float(config.get('Turnstile', 'handle_turnstile_time', fallback='2'))
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)
max_retries = 2 max_retries = 2
retry_count = 0 retry_count = 0
@@ -148,7 +304,7 @@ def handle_turnstile(page, translator=None):
try: try:
# 尝试重置 turnstile # 尝试重置 turnstile
page.run_js("try { turnstile.reset() } catch(e) { }") page.run_js("try { turnstile.reset() } catch(e) { }")
time.sleep(2) time.sleep(turnstile_time) # from config
# 定位验证框元素 # 定位验证框元素
challenge_check = ( challenge_check = (
@@ -165,12 +321,12 @@ def handle_turnstile(page, translator=None):
else: else:
print("检测到验证框...") print("检测到验证框...")
# 随机延时后点击验证 # from config
time.sleep(random.uniform(1, 3)) time.sleep(random.uniform(min_random_time, max_random_time))
challenge_check.click() challenge_check.click()
time.sleep(2) time.sleep(turnstile_time) # from config
# 检查验证结果 # check verification result
if check_verification_success(page, translator): if check_verification_success(page, 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}")
@@ -192,7 +348,7 @@ def handle_turnstile(page, translator=None):
print("验证通过!") print("验证通过!")
return True return True
time.sleep(random.uniform(1, 2)) time.sleep(random.uniform(min_random_time, max_random_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}")
@@ -237,34 +393,50 @@ 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, translator=None): def fill_password(page, password: str, translator=None) -> bool:
"""填写密码""" """
填写密码表单
"""
try: try:
if translator: print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else '设置密码'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password')}{Style.RESET_ALL}")
else: # 等待密码框出现并尝试多次
print("\n正在设置密码...") max_retries = 5
password_input = page.ele("@name=password") for i in range(max_retries):
if password_input: # 检查是否出现错误信息
password_input.input(password) if page.ele("This email is not available."):
time.sleep(random.uniform(0.5, 1.0)) print(f"{Fore.RED}{translator.get('register.email_used') if translator else '注册失败:邮箱已被使用'}{Style.RESET_ALL}")
return False
submit_button = page.ele("@type=submit")
if submit_button: # 查找密码输入框
submit_button.click() password_input = page.ele("@name=password")
time.sleep(random.uniform(2.0, 3.0)) if password_input:
# 清除可能存在的旧值并输入新密码
if translator: password_input.click()
print(f"{Fore.GREEN}{translator.get('register.password_success')}{Style.RESET_ALL}") time.sleep(random.uniform(0.5, 1))
else: password_input.input(password)
print(f"密码设置完成: {password}") time.sleep(random.uniform(1, 2))
return True
# 查找并点击提交按钮
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}")
# 如果没找到密码框,等待后重试
time.sleep(2)
if i < max_retries - 1: # 不是最后一次尝试时才打印
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:
if translator: 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))}{Style.RESET_ALL}")
else:
print(f"设置密码时出错: {e}")
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, email, password, translator=None):
@@ -272,139 +444,141 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
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}")
else:
print("\n等待并获取验证码...")
# 添加调试信息 # 检查是否使用手动输入验证码
print(f"\n{Fore.CYAN}DEBUG: email_tab exists: {email_tab is not None}{Style.RESET_ALL}") if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式
verification_code = controller.get_verification_code()
time.sleep(5) # 等待验证码邮件
# 使用已有的 email_tab 刷新邮箱
email_tab.refresh_inbox()
time.sleep(3)
# 检查邮箱是否有验证码邮件
if email_tab.check_for_cursor_email():
verification_code = email_tab.get_verification_code()
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(random.uniform(0.1, 0.3))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{translator.get('register.verification_success')}")
else:
print("验证码填写完成")
time.sleep(3) time.sleep(3)
# 处理最后一次 Turnstile 验证 # 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator): if handle_turnstile(browser_tab, 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}")
else:
print("最后一次验证通过!")
time.sleep(2) time.sleep(2)
# 访问设置页面 # 访问设置页面
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}")
else:
print("访问设置页面...")
browser_tab.get("https://www.cursor.com/settings") browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载 time.sleep(3) # 等待页面加载
return True return True, browser_tab
else:
print("最后一次验证失败")
return False
return False return False, None
# 获取验证码,设置超时 # 自动获取验证码逻辑
verification_code = None elif email_tab:
max_attempts = 20 print(f"{translator.get('register.waiting_for_verification_code')}")
retry_interval = 10 time.sleep(5) # 等待验证码邮件
start_time = time.time()
timeout = 160
if translator: # 使用已有的 email_tab 刷新邮箱
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
else:
print("开始获取验证码...")
for attempt in range(max_attempts):
# 检查是否超时
if time.time() - start_time > timeout:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_timeout')}{Style.RESET_ALL}")
else:
print("获取验证码超时...")
break
verification_code = controller.get_verification_code()
if verification_code:
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print(f"成功获取验证码: {verification_code}")
break
remaining_time = int(timeout - (time.time() - start_time))
if translator:
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
else:
print(f"{attempt + 1} 次尝试获取验证码,剩余时间: {remaining_time}秒...")
# 刷新邮箱
email_tab.refresh_inbox() email_tab.refresh_inbox()
time.sleep(retry_interval)
if verification_code:
# 在注册页面填写验证码
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print("验证码填写完成")
time.sleep(3) time.sleep(3)
# 检查邮箱是否有验证码邮件
if email_tab.check_for_cursor_email():
verification_code = email_tab.get_verification_code()
if verification_code:
# 在注册页面填写验证码
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(3)
# 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(2)
# 访问设置页面
if translator:
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")
time.sleep(3) # 等待页面加载
return True, browser_tab
else:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print("最后一次验证失败")
return False, None
# 获取验证码,设置超时
verification_code = None
max_attempts = 20
retry_interval = 10
start_time = time.time()
timeout = 160
if translator:
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
# 处理最后一次 Turnstile 验证 for attempt in range(max_attempts):
if handle_turnstile(browser_tab, translator): # 检查是否超时
if time.time() - start_time > timeout:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_timeout')}{Style.RESET_ALL}")
break
verification_code = controller.get_verification_code()
if verification_code:
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
break
remaining_time = int(timeout - (time.time() - start_time))
if translator:
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
# 刷新邮箱
email_tab.refresh_inbox()
time.sleep(retry_interval)
if verification_code:
# 在注册页面填写验证码
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(random.uniform(0.1, 0.3))
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}")
else: time.sleep(3)
print("最后一次验证通过!")
time.sleep(2)
# 直接访问设置页面 # 处理最后一次 Turnstile 验证
if translator: if handle_turnstile(browser_tab, translator):
print(f"{Fore.CYAN}{translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}") if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(2)
# 直接访问设置页面
if translator:
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")
time.sleep(3) # 等待页面加载
# 直接返回成功,让 cursor_register.py 处理账户信息获取
return True, browser_tab
else: else:
print("访问设置页面...") if translator:
browser_tab.get("https://www.cursor.com/settings") print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
time.sleep(3) # 等待页面加载 return False, None
# 直接返回成功,让 cursor_register.py 处理账户信息获取 return False, None
return True
else:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print("最后一次验证失败")
return False
return False
except Exception as e: except Exception as e:
if translator: if translator:
print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
else: return False, None
print(f"处理验证码时出错: {e}")
return False
def handle_sign_in(browser_tab, email, password, translator=None): def handle_sign_in(browser_tab, email, password, translator=None):
"""处理登录流程""" """处理登录流程"""
@@ -466,25 +640,19 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
page = None page = None
success = False success = False
try: try:
page = setup_driver(translator) config, page = setup_driver(translator)
if translator: if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}") print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}")
else:
print("浏览器已启动")
# 访问注册页面 # 访问注册页面
url = "https://authenticator.cursor.sh/sign-up" url = "https://authenticator.cursor.sh/sign-up"
if translator: if translator:
print(f"\n{Fore.CYAN}{translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}{translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
else:
print(f"\n正在访问: {url}")
# 访问页面 # 访问页面
simulate_human_input(page, url, translator) simulate_human_input(page, url, 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}")
else:
print("等待页面加载...")
time.sleep(5) time.sleep(5)
# 如果没有提供账号信息,则生成随机信息 # 如果没有提供账号信息,则生成随机信息
@@ -505,45 +673,33 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
if fill_signup_form(page, first_name, last_name, email, translator): if fill_signup_form(page, first_name, last_name, email, 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}")
else:
print("\n表单已提交,开始验证...")
# 处理第一次 Turnstile 验证 # 处理第一次 Turnstile 验证
if handle_turnstile(page, 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}")
else:
print("\n第一阶段验证通过!")
# 填写密码 # 填写密码
if fill_password(page, password, translator): if fill_password(page, password, 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}")
else:
print("\n等待第二次验证...")
time.sleep(2) time.sleep(2)
# 处理第二次 Turnstile 验证 # 处理第二次 Turnstile 验证
if handle_turnstile(page, 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}")
else:
print("\n开始处理验证码...")
if handle_verification_code(page, email_tab, controller, email, password, translator): if handle_verification_code(page, email_tab, controller, email, password, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print("\n注册流程完成!")
success = True success = True
return True, page # 返回成功状态和浏览器实例 return True, page # 返回浏览器实例
else: else:
print("\n验证码处理失败") print(f"\n{Fore.RED} {translator.get('register.verification_code_processing_failed') if translator else '验证码处理失败'}{Style.RESET_ALL}")
else: else:
print("\n第二次验证失败") print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
else: else:
print("\n密码设置失败") print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
else: else:
print("\n第一次验证失败") 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

@@ -3,128 +3,246 @@ import time
import os import os
import sys import sys
from colorama import Fore, Style, init from colorama import Fore, Style, init
import requests
import random
import string
# 初始化 colorama # Initialize colorama
init() init()
class NewTempEmail: class NewTempEmail:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
self.page = None # Randomly choose between mail.tm and mail.gw
self.setup_browser() self.services = [
{"name": "mail.tm", "api_url": "https://api.mail.tm"},
{"name": "mail.gw", "api_url": "https://api.mail.gw"}
]
self.selected_service = random.choice(self.services)
self.api_url = self.selected_service["api_url"]
self.token = None
self.email = None
self.password = None
self.blocked_domains = self.get_blocked_domains()
def get_extension_block(self): def get_blocked_domains(self):
"""获取插件路径""" """Get blocked domains list"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "uBlock0.chromium")
if hasattr(sys, "_MEIPASS"):
extension_path = os.path.join(sys._MEIPASS, "uBlock0.chromium")
if not os.path.exists(extension_path):
raise FileNotFoundError(f"插件不存在: {extension_path}")
return extension_path
def setup_browser(self):
"""设置浏览器"""
try: try:
if self.translator: block_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/block_domain.txt"
print(f"{Fore.CYAN} {self.translator.get('email.starting_browser')}{Style.RESET_ALL}") response = requests.get(block_url, timeout=5)
else: if response.status_code == 200:
print(f"{Fore.CYAN} 正在启动浏览器...{Style.RESET_ALL}") # Split text and remove empty lines
domains = [line.strip() for line in response.text.split('\n') if line.strip()]
# 创建浏览器选项
co = ChromiumOptions()
co.set_argument("--headless=new")
co.auto_port() # 自动设置端口
# 加载 uBlock 插件
try:
extension_path = self.get_extension_block()
co.set_argument("--allow-extensions-in-incognito")
co.add_extension(extension_path)
except Exception as e:
if self.translator: if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.extension_load_error')}: {str(e)}{Style.RESET_ALL}") print(f"{Fore.CYAN} {self.translator.get('email.blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
else: else:
print(f"{Fore.YELLOW} 加载插件失败: {str(e)}{Style.RESET_ALL}") print(f"{Fore.CYAN} 加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
return domains
self.page = ChromiumPage(co) return []
return True
except Exception as e: except Exception as e:
if self.translator: if self.translator:
print(f"{Fore.RED} {self.translator.get('email.browser_start_error')}: {str(e)}{Style.RESET_ALL}") print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
else: else:
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}") print(f"{Fore.YELLOW}⚠️ 获取被屏蔽域名列表失败: {str(e)}{Style.RESET_ALL}")
return False return []
def exclude_blocked_domains(self, domains):
"""Exclude blocked domains"""
if not self.blocked_domains:
return domains
def create_email(self): filtered_domains = []
"""创建临时邮箱""" for domain in domains:
try: if domain['domain'] not in self.blocked_domains:
if self.translator: filtered_domains.append(domain)
print(f"{Fore.CYAN} {self.translator.get('email.visiting_site')}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 正在访问 smailpro.com...{Style.RESET_ALL}")
# 访问网站
self.page.get("https://smailpro.com/")
time.sleep(2)
# 点击创建邮箱按钮
create_button = self.page.ele('xpath://button[@title="Create temporary email"]')
if create_button:
create_button.click()
time.sleep(1)
# 点击弹窗中的 Create 按钮 excluded_count = len(domains) - len(filtered_domains)
modal_create_button = self.page.ele('xpath://button[contains(text(), "Create")]') if excluded_count > 0:
if modal_create_button:
modal_create_button.click()
time.sleep(2)
# 获取邮箱地址 - 修改选择器
email_div = self.page.ele('xpath://div[@class="text-base sm:text-lg md:text-xl text-gray-700"]')
if email_div:
email = email_div.text.strip()
if '@' in email: # 验证是否是有效的邮箱地址
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}")
return email
if self.translator: if self.translator:
print(f"{Fore.RED} {self.translator.get('email.create_failed')}{Style.RESET_ALL}") print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domains_excluded', domains=excluded_count)}{Style.RESET_ALL}")
else: else:
print(f"{Fore.RED}❌ 创建邮箱失败{Style.RESET_ALL}") print(f"{Fore.YELLOW}⚠️ 已排除 {excluded_count} 个被屏蔽的域名{Style.RESET_ALL}")
return None
return filtered_domains
except Exception as e:
if self.translator: def _generate_credentials(self):
print(f"{Fore.RED}{self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}") """generate random username and password"""
else: username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
print(f"{Fore.RED}❌ 创建邮箱出错: {str(e)}{Style.RESET_ALL}") password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=12))
return None return username, password
def create_email(self):
"""create temporary email"""
max_retries = 3 # Maximum number of retries
attempt = 0 # Current attempt count
while attempt < max_retries:
attempt += 1
try:
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.visiting_site').replace('mail.tm', self.selected_service['name'])}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 正在访问 {self.selected_service['name']}...{Style.RESET_ALL}")
# Get available domain list
try:
domains_response = requests.get(f"{self.api_url}/domains", timeout=10)
if domains_response.status_code != 200:
print(f"{Fore.RED}{self.translator.get('email.domains_list_error', error=domains_response.status_code)}{Style.RESET_ALL}")
print(f"{Fore.RED}{self.translator.get('email.domains_list_error', error=domains_response.text)}{Style.RESET_ALL}")
raise Exception(f"{self.translator.get('email.failed_to_get_available_domains') if self.translator else 'Failed to get available domains'}")
domains = domains_response.json()["hydra:member"]
print(f"{Fore.CYAN} {self.translator.get('email.available_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
if not domains:
raise Exception(f"{self.translator.get('email.no_available_domains') if self.translator else '没有可用域名'}")
except Exception as e:
print(f"{Fore.RED}❌ 获取域名列表时出错: {str(e)}{Style.RESET_ALL}")
raise
# Exclude blocked domains
try:
filtered_domains = self.exclude_blocked_domains(domains)
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.domains_filtered', count=len(filtered_domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 过滤后剩余 {len(filtered_domains)} 个可用域名{Style.RESET_ALL}")
if not filtered_domains:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.all_domains_blocked')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 所有域名都被屏蔽了,尝试切换服务{Style.RESET_ALL}")
# Switch to another service
for service in self.services:
if service["api_url"] != self.api_url:
self.selected_service = service
self.api_url = service["api_url"]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.switching_service', service=service['name'])}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 切换到 {service['name']} 服务{Style.RESET_ALL}")
return self.create_email() # Recursively call
raise Exception(f"{self.translator.get('email.no_available_domains_after_filtering') if self.translator else '过滤后没有可用域名'}")
except Exception as e:
print(f"{Fore.RED}❌ 过滤域名时出错: {str(e)}{Style.RESET_ALL}")
raise
# Generate random username and password
try:
username, password = self._generate_credentials()
self.password = password
# Create email account
selected_domain = filtered_domains[0]['domain']
email = f"{username}@{selected_domain}"
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.trying_to_create_email', email=email)}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 尝试创建邮箱: {email}{Style.RESET_ALL}")
account_data = {
"address": email,
"password": password
}
except Exception as e:
print(f"{Fore.RED}❌ 生成凭据时出错: {str(e)}{Style.RESET_ALL}")
raise
# Create account
try:
create_response = requests.post(f"{self.api_url}/accounts", json=account_data, timeout=15)
if create_response.status_code != 201:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_create_account', error=create_response.status_code)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建账户失败: 状态码 {create_response.status_code}{Style.RESET_ALL}")
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_create_account', error=create_response.text)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 响应内容: {create_response.text}{Style.RESET_ALL}")
# If it's a domain problem, try the next available domain
if len(filtered_domains) > 1 and ("domain" in create_response.text.lower() or "address" in create_response.text.lower()):
print(f"{Fore.YELLOW}⚠️ 尝试使用下一个可用域名...{Style.RESET_ALL}")
# Add current domain to blocked list
if selected_domain not in self.blocked_domains:
self.blocked_domains.append(selected_domain)
# Recursively call yourself
return self.create_email()
raise Exception(f"{self.translator.get('email.failed_to_create_account') if self.translator else '创建账户失败'}")
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_create_account', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建账户时出错: {str(e)}{Style.RESET_ALL}")
raise
# Get access token
try:
token_data = {
"address": email,
"password": password
}
token_response = requests.post(f"{self.api_url}/token", json=token_data, timeout=10)
if token_response.status_code != 200:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_get_access_token', error=token_response.status_code)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 获取令牌失败: 状态码 {token_response.status_code}{Style.RESET_ALL}")
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_get_access_token', error=token_response.text)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 响应内容: {token_response.text}{Style.RESET_ALL}")
raise Exception(f"{self.translator.get('email.failed_to_get_access_token') if self.translator else '获取访问令牌失败'}")
self.token = token_response.json()["token"]
self.email = email
except Exception as e:
print(f"{Fore.RED}❌ 获取令牌时出错: {str(e)}{Style.RESET_ALL}")
raise
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}")
return email
except Exception as e:
if attempt < max_retries:
print(f"{Fore.YELLOW}⚠️ 尝试重新创建邮箱... (尝试 {attempt}/{max_retries}){Style.RESET_ALL}")
else:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建邮箱出错: {str(e)}{Style.RESET_ALL}")
return None
def close(self): def close(self):
"""关闭浏览器""" """close browser"""
if self.page: if self.page:
self.page.quit() self.page.quit()
def refresh_inbox(self): def refresh_inbox(self):
"""刷新邮箱""" """refresh inbox"""
try: try:
if self.translator: if self.translator:
print(f"{Fore.CYAN}🔄 {self.translator.get('email.refreshing')}{Style.RESET_ALL}") print(f"{Fore.CYAN}🔄 {self.translator.get('email.refreshing')}{Style.RESET_ALL}")
else: else:
print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}") print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}")
# 点击刷新按钮 # Use API to get latest email
refresh_button = self.page.ele('xpath://button[@id="refresh"]') headers = {"Authorization": f"Bearer {self.token}"}
if refresh_button: response = requests.get(f"{self.api_url}/messages", headers=headers)
refresh_button.click()
time.sleep(2) # 等待刷新完成 if response.status_code == 200:
if self.translator: if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.refresh_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{self.translator.get('email.refresh_success')}{Style.RESET_ALL}")
else: else:
@@ -132,9 +250,9 @@ class NewTempEmail:
return True return True
if self.translator: if self.translator:
print(f"{Fore.RED}{self.translator.get('email.refresh_button_not_found')}{Style.RESET_ALL}") print(f"{Fore.RED}{self.translator.get('email.refresh_failed')}{Style.RESET_ALL}")
else: else:
print(f"{Fore.RED}未找到刷新按钮{Style.RESET_ALL}") print(f"{Fore.RED}刷新邮箱失败{Style.RESET_ALL}")
return False return False
except Exception as e: except Exception as e:
@@ -147,17 +265,24 @@ class NewTempEmail:
def check_for_cursor_email(self): def check_for_cursor_email(self):
"""检查是否有 Cursor 的验证邮件""" """检查是否有 Cursor 的验证邮件"""
try: try:
# 查找验证邮件 - 使用更精确的选择器 # Use API to get email list
email_div = self.page.ele('xpath://div[contains(@class, "p-2") and contains(@class, "cursor-pointer") and contains(@class, "bg-white") and contains(@class, "shadow") and .//b[text()="no-reply@cursor.sh"] and .//span[text()="Verify your email address"]]') headers = {"Authorization": f"Bearer {self.token}"}
if email_div: response = requests.get(f"{self.api_url}/messages", headers=headers)
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.verification_found')}{Style.RESET_ALL}") if response.status_code == 200:
else: messages = response.json()["hydra:member"]
print(f"{Fore.GREEN}✅ 找到验证邮件{Style.RESET_ALL}") for message in messages:
# 使用 JavaScript 点击元素 if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]:
self.page.run_js('arguments[0].click()', email_div) # Get email content
time.sleep(2) # 等待邮件内容加载 message_id = message["id"]
return True message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers)
if message_response.status_code == 200:
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.verification_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 找到验证邮件{Style.RESET_ALL}")
return True
if self.translator: if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_not_found')}{Style.RESET_ALL}") print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_not_found')}{Style.RESET_ALL}")
else: else:
@@ -172,18 +297,35 @@ class NewTempEmail:
return False return False
def get_verification_code(self): def get_verification_code(self):
"""获取验证码""" """get verification code"""
try: try:
# 查找验证码元素 # Use API to get email list
code_element = self.page.ele('xpath://td//div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]') headers = {"Authorization": f"Bearer {self.token}"}
if code_element: response = requests.get(f"{self.api_url}/messages", headers=headers)
code = code_element.text.strip()
if code.isdigit() and len(code) == 6: if response.status_code == 200:
if self.translator: messages = response.json()["hydra:member"]
print(f"{Fore.GREEN}{self.translator.get('email.verification_code_found')}: {code}{Style.RESET_ALL}") for message in messages:
else: if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]:
print(f"{Fore.GREEN}✅ 获取验证码成功: {code}{Style.RESET_ALL}") # Get email content
return code message_id = message["id"]
message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers)
if message_response.status_code == 200:
# Extract verification code from email content
email_content = message_response.json()["text"]
# Find 6-digit verification code
import re
code_match = re.search(r'\b\d{6}\b', email_content)
if code_match:
code = code_match.group(0)
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.verification_code_found')}: {code}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 获取验证码成功: {code}{Style.RESET_ALL}")
return code
if self.translator: if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_code_not_found')}{Style.RESET_ALL}") print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_code_not_found')}{Style.RESET_ALL}")
else: else:
@@ -208,7 +350,7 @@ def main(translator=None):
else: else:
print(f"\n{Fore.CYAN}📧 临时邮箱地址: {email}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}📧 临时邮箱地址: {email}{Style.RESET_ALL}")
# 测试刷新功能 # Test refresh function
while True: while True:
if translator: if translator:
choice = input(f"\n{translator.get('email.refresh_prompt')}: ").lower() choice = input(f"\n{translator.get('email.refresh_prompt')}: ").lower()
@@ -223,4 +365,4 @@ def main(translator=None):
temp_email.close() temp_email.close()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -4,10 +4,10 @@ from colorama import Fore, Style, init
import sys import sys
import os import os
# 初始化colorama # Initialize colorama
init() init()
# 定义emoji常量 # Define emoji constants
EMOJI = { EMOJI = {
"PROCESS": "⚙️", "PROCESS": "⚙️",
"SUCCESS": "", "SUCCESS": "",
@@ -19,15 +19,15 @@ EMOJI = {
class CursorQuitter: class CursorQuitter:
def __init__(self, timeout=5, translator=None): def __init__(self, timeout=5, translator=None):
self.timeout = timeout self.timeout = timeout
self.translator = translator # 使用传入的翻译器 self.translator = translator # Use the passed translator
def quit_cursor(self): def quit_cursor(self):
"""温和地关闭 Cursor 进程""" """Gently close Cursor processes"""
try: try:
print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('quit_cursor.start')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('quit_cursor.start')}...{Style.RESET_ALL}")
cursor_processes = [] cursor_processes = []
# 收集所有 Cursor 进程 # Collect all Cursor processes
for proc in psutil.process_iter(['pid', 'name']): for proc in psutil.process_iter(['pid', 'name']):
try: try:
if proc.info['name'].lower() in ['cursor.exe', 'cursor']: if proc.info['name'].lower() in ['cursor.exe', 'cursor']:
@@ -39,7 +39,7 @@ class CursorQuitter:
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('quit_cursor.no_process')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('quit_cursor.no_process')}{Style.RESET_ALL}")
return True return True
# 温和地请求进程终止 # Gently request processes to terminate
for proc in cursor_processes: for proc in cursor_processes:
try: try:
if proc.is_running(): if proc.is_running():
@@ -48,7 +48,7 @@ class CursorQuitter:
except (psutil.NoSuchProcess, psutil.AccessDenied): except (psutil.NoSuchProcess, psutil.AccessDenied):
continue continue
# 等待进程自然终止 # Wait for processes to terminate naturally
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('quit_cursor.waiting')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('quit_cursor.waiting')}...{Style.RESET_ALL}")
start_time = time.time() start_time = time.time()
while time.time() - start_time < self.timeout: while time.time() - start_time < self.timeout:
@@ -66,7 +66,7 @@ class CursorQuitter:
time.sleep(0.5) time.sleep(0.5)
# 如果超时后仍有进程在运行 # If processes are still running after timeout
if still_running: if still_running:
process_list = ", ".join([str(p.pid) for p in still_running]) process_list = ", ".join([str(p.pid) for p in still_running])
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('quit_cursor.timeout', pids=process_list)}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('quit_cursor.timeout', pids=process_list)}{Style.RESET_ALL}")
@@ -79,11 +79,11 @@ class CursorQuitter:
return False return False
def quit_cursor(translator=None, timeout=5): def quit_cursor(translator=None, timeout=5):
"""便捷函数,用于直接调用退出功能""" """Convenient function for directly calling the quit function"""
quitter = CursorQuitter(timeout, translator) quitter = CursorQuitter(timeout, translator)
return quitter.quit_cursor() return quitter.quit_cursor()
if __name__ == "__main__": if __name__ == "__main__":
# 如果直接运行,使用默认翻译器 # If run directly, use the default translator
from main import translator as main_translator from main import translator as main_translator
quit_cursor(main_translator) quit_cursor(main_translator)

View File

@@ -10,11 +10,13 @@ import re
import tempfile import tempfile
from colorama import Fore, Style, init from colorama import Fore, Style, init
from typing import Tuple from typing import Tuple
import configparser
from new_signup import get_user_documents_path
# 初始化colorama # Initialize colorama
init() init()
# 定义emoji常量 # Define emoji constants
EMOJI = { EMOJI = {
"FILE": "📄", "FILE": "📄",
"BACKUP": "💾", "BACKUP": "💾",
@@ -25,7 +27,7 @@ EMOJI = {
} }
def get_cursor_paths(translator=None) -> Tuple[str, str]: def get_cursor_paths(translator=None) -> Tuple[str, str]:
"""根据不同操作系统获取 Cursor 相关路径""" """ Get Cursor related paths"""
system = platform.system() system = platform.system()
paths_map = { paths_map = {
@@ -64,8 +66,87 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
os.path.join(base_path, paths_map[system]["main"]), os.path.join(base_path, paths_map[system]["main"]),
) )
def get_cursor_machine_id_path(translator=None) -> str:
"""
Get Cursor machineId file path based on operating system
Returns:
str: Path to machineId file
"""
# Read configuration
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
config = configparser.ConfigParser()
if os.path.exists(config_file):
config.read(config_file)
if sys.platform == "win32": # Windows
if not config.has_section('WindowsPaths'):
config.add_section('WindowsPaths')
config.set('WindowsPaths', 'machine_id_path',
os.path.join(os.getenv("APPDATA"), "Cursor", "machineId"))
return config.get('WindowsPaths', 'machine_id_path')
elif sys.platform == "linux": # Linux
if not config.has_section('LinuxPaths'):
config.add_section('LinuxPaths')
config.set('LinuxPaths', 'machine_id_path',
os.path.expanduser("~/.config/Cursor/machineId"))
return config.get('LinuxPaths', 'machine_id_path')
elif sys.platform == "darwin": # macOS
if not config.has_section('MacPaths'):
config.add_section('MacPaths')
config.set('MacPaths', 'machine_id_path',
os.path.expanduser("~/Library/Application Support/Cursor/machineId"))
return config.get('MacPaths', 'machine_id_path')
else:
raise OSError(f"Unsupported operating system: {sys.platform}")
# Save any changes to config file
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
def get_workbench_cursor_path(translator=None) -> str:
"""Get Cursor workbench.desktop.main.js path"""
system = platform.system()
paths_map = {
"Darwin": { # macOS
"base": "/Applications/Cursor.app/Contents/Resources/app",
"main": "out/vs/workbench/workbench.desktop.main.js"
},
"Windows": {
"base": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
"main": "out/vs/workbench/workbench.desktop.main.js"
},
"Linux": {
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
"main": "out/vs/workbench/workbench.desktop.main.js"
}
}
if system not in paths_map:
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
if system == "Linux":
for base in paths_map["Linux"]["bases"]:
main_path = os.path.join(base, paths_map["Linux"]["main"])
if os.path.exists(main_path):
return main_path
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径")
base_path = paths_map[system]["base"]
main_path = os.path.join(base_path, paths_map[system]["main"])
if not os.path.exists(main_path):
raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}")
return main_path
def version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool: def version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool:
"""版本号检查""" """Version number check"""
version_pattern = r"^\d+\.\d+\.\d+$" version_pattern = r"^\d+\.\d+\.\d+$"
try: try:
if not re.match(version_pattern, version): if not re.match(version_pattern, version):
@@ -92,7 +173,7 @@ def version_check(version: str, min_version: str = "", max_version: str = "", tr
return False return False
def check_cursor_version(translator) -> bool: def check_cursor_version(translator) -> bool:
"""检查 Cursor 版本""" """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: with open(pkg_path, "r", encoding="utf-8") as f:
@@ -102,8 +183,79 @@ def check_cursor_version(translator) -> bool:
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}")
return False return False
def modify_workbench_js(file_path: str, translator=None) -> bool:
"""
Modify file content
"""
try:
# Save original file permissions
original_stat = os.stat(file_path)
original_mode = original_stat.st_mode
original_uid = original_stat.st_uid
original_gid = original_stat.st_gid
# Create temporary file
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", errors="ignore", delete=False) as tmp_file:
# Read original content
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
content = main_file.read()
if sys.platform == "win32":
# Define replacement patterns
CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
elif sys.platform == "linux":
CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
elif sys.platform == "darwin":
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
CBadge_old_pattern = r'<div>Pro Trial'
CBadge_new_pattern = r'<div>Pro'
CToast_old_pattern = r'notifications-toasts'
CToast_new_pattern = r'notifications-toasts hidden'
# Replace content
content = content.replace(CButton_old_pattern, CButton_new_pattern)
content = content.replace(CBadge_old_pattern, CBadge_new_pattern)
content = content.replace(CToast_old_pattern, CToast_new_pattern)
# Write to temporary file
tmp_file.write(content)
tmp_path = tmp_file.name
# Backup original file
backup_path = file_path + ".backup"
if os.path.exists(backup_path):
os.remove(backup_path)
shutil.copy2(file_path, backup_path)
# Move temporary file to original position
if os.path.exists(file_path):
os.remove(file_path)
shutil.move(tmp_path, file_path)
# Restore original permissions
os.chmod(file_path, original_mode)
if os.name != "nt": # Not Windows
os.chown(file_path, original_uid, original_gid)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
if "tmp_path" in locals():
try:
os.unlink(tmp_path)
except:
pass
return False
def modify_main_js(main_path: str, translator) -> bool: def modify_main_js(main_path: str, translator) -> bool:
"""修改 main.js 文件""" """Modify main.js file"""
try: try:
original_stat = os.stat(main_path) original_stat = os.stat(main_path)
original_mode = original_stat.st_mode original_mode = original_stat.st_mode
@@ -142,14 +294,14 @@ def modify_main_js(main_path: str, translator) -> bool:
return False return False
def patch_cursor_get_machine_id(translator) -> bool: def patch_cursor_get_machine_id(translator) -> bool:
"""修补 Cursor getMachineId 函数""" """Patch Cursor getMachineId function"""
try: try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}")
# 获取路径 # Get paths
pkg_path, main_path = get_cursor_paths(translator) pkg_path, main_path = get_cursor_paths(translator)
# 检查文件权限 # Check file permissions
for file_path in [pkg_path, main_path]: for file_path in [pkg_path, main_path]:
if not os.path.isfile(file_path): if not os.path.isfile(file_path):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}")
@@ -158,7 +310,7 @@ def patch_cursor_get_machine_id(translator) -> bool:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}")
return False return False
# 获取版本号 # Get version number
try: try:
with open(pkg_path, "r", encoding="utf-8") as f: with open(pkg_path, "r", encoding="utf-8") as f:
version = json.load(f)["version"] version = json.load(f)["version"]
@@ -167,20 +319,20 @@ def patch_cursor_get_machine_id(translator) -> bool:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}")
return False return False
# 检查版本 # Check version
if not version_check(version, min_version="0.45.0", translator=translator): if not version_check(version, min_version="0.45.0", translator=translator):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}")
return False return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
# 备份文件 # Backup file
backup_path = main_path + ".bak" backup_path = main_path + ".bak"
if not os.path.exists(backup_path): if not os.path.exists(backup_path):
shutil.copy2(main_path, backup_path) shutil.copy2(main_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
# 修改文件 # Modify file
if not modify_main_js(main_path, translator): if not modify_main_js(main_path, translator):
return False return False
@@ -195,58 +347,99 @@ class MachineIDResetter:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
# 判断操作系统 # Read configuration
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
config = configparser.ConfigParser()
if not os.path.exists(config_file):
raise FileNotFoundError(f"Config file not found: {config_file}")
config.read(config_file)
# Check operating system
if sys.platform == "win32": # Windows if sys.platform == "win32": # Windows
appdata = os.getenv("APPDATA") appdata = os.getenv("APPDATA")
if appdata is None: if appdata is None:
raise EnvironmentError("APPDATA Environment Variable Not Set") raise EnvironmentError("APPDATA Environment Variable Not Set")
self.db_path = os.path.join(
appdata, "Cursor", "User", "globalStorage", "storage.json" if not config.has_section('WindowsPaths'):
) config.add_section('WindowsPaths')
self.sqlite_path = os.path.join( config.set('WindowsPaths', 'storage_path', os.path.join(
appdata, "Cursor", "User", "globalStorage", "state.vscdb" appdata, "Cursor", "User", "globalStorage", "storage.json"
) ))
config.set('WindowsPaths', 'sqlite_path', os.path.join(
appdata, "Cursor", "User", "globalStorage", "state.vscdb"
))
self.db_path = config.get('WindowsPaths', 'storage_path')
self.sqlite_path = config.get('WindowsPaths', 'sqlite_path')
elif sys.platform == "darwin": # macOS elif sys.platform == "darwin": # macOS
self.db_path = os.path.abspath(os.path.expanduser( if not config.has_section('MacPaths'):
"~/Library/Application Support/Cursor/User/globalStorage/storage.json" config.add_section('MacPaths')
)) config.set('MacPaths', 'storage_path', os.path.abspath(os.path.expanduser(
self.sqlite_path = os.path.abspath(os.path.expanduser( "~/Library/Application Support/Cursor/User/globalStorage/storage.json"
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb" )))
)) config.set('MacPaths', 'sqlite_path', os.path.abspath(os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
)))
self.db_path = config.get('MacPaths', 'storage_path')
self.sqlite_path = config.get('MacPaths', 'sqlite_path')
elif sys.platform == "linux": # Linux elif sys.platform == "linux": # Linux
self.db_path = os.path.abspath(os.path.expanduser( if not config.has_section('LinuxPaths'):
"~/.config/Cursor/User/globalStorage/storage.json" config.add_section('LinuxPaths')
)) # 获取实际用户的主目录
self.sqlite_path = os.path.abspath(os.path.expanduser( sudo_user = os.environ.get('SUDO_USER')
"~/.config/Cursor/User/globalStorage/state.vscdb" actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
))
config.set('LinuxPaths', 'storage_path', os.path.abspath(os.path.join(
actual_home,
".config/Cursor/User/globalStorage/storage.json"
)))
config.set('LinuxPaths', 'sqlite_path', os.path.abspath(os.path.join(
actual_home,
".config/Cursor/User/globalStorage/state.vscdb"
)))
self.db_path = config.get('LinuxPaths', 'storage_path')
self.sqlite_path = config.get('LinuxPaths', 'sqlite_path')
else: else:
raise NotImplementedError(f"Not Supported OS: {sys.platform}") raise NotImplementedError(f"Not Supported OS: {sys.platform}")
# Save any changes to config file
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
def generate_new_ids(self): def generate_new_ids(self):
"""生成新的机器ID""" """Generate new machine ID"""
# 生成新的UUID # Generate new UUID
dev_device_id = str(uuid.uuid4()) dev_device_id = str(uuid.uuid4())
# 生成新的machineId (64个字符的十六进制) # Generate new machineId (64 characters of hexadecimal)
machine_id = hashlib.sha256(os.urandom(32)).hexdigest() machine_id = hashlib.sha256(os.urandom(32)).hexdigest()
# 生成新的macMachineId (128个字符的十六进制) # Generate new macMachineId (128 characters of hexadecimal)
mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest() mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest()
# 生成新的sqmId # Generate new sqmId
sqm_id = "{" + str(uuid.uuid4()).upper() + "}" sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
self.update_machine_id_file(dev_device_id)
return { return {
"telemetry.devDeviceId": dev_device_id, "telemetry.devDeviceId": dev_device_id,
"telemetry.macMachineId": mac_machine_id, "telemetry.macMachineId": mac_machine_id,
"telemetry.machineId": machine_id, "telemetry.machineId": machine_id,
"telemetry.sqmId": sqm_id, "telemetry.sqmId": sqm_id,
"storage.serviceMachineId": dev_device_id, # 添加 storage.serviceMachineId "storage.serviceMachineId": dev_device_id, # Add storage.serviceMachineId
} }
def update_sqlite_db(self, new_ids): def update_sqlite_db(self, new_ids):
"""更新 SQLite 数据库中的机器ID""" """Update machine ID in SQLite database"""
try: try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_sqlite')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_sqlite')}...{Style.RESET_ALL}")
@@ -281,7 +474,7 @@ class MachineIDResetter:
return False return False
def update_system_ids(self, new_ids): def update_system_ids(self, new_ids):
"""更新系统级别的ID""" """Update system-level IDs"""
try: try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_system_ids')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_system_ids')}...{Style.RESET_ALL}")
@@ -297,7 +490,7 @@ class MachineIDResetter:
return False return False
def _update_windows_machine_guid(self): def _update_windows_machine_guid(self):
"""更新Windows MachineGuid""" """Update Windows MachineGuid"""
try: try:
import winreg import winreg
key = winreg.OpenKey( key = winreg.OpenKey(
@@ -309,32 +502,32 @@ class MachineIDResetter:
new_guid = str(uuid.uuid4()) new_guid = str(uuid.uuid4())
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid) winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
winreg.CloseKey(key) winreg.CloseKey(key)
print("Windows MachineGuid updated successfully") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_guid_updated')}{Style.RESET_ALL}")
except PermissionError: except PermissionError:
print("Permission denied: Run as administrator to update Windows MachineGuid") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
raise raise
except Exception as e: except Exception as e:
print(f"Failed to update Windows MachineGuid: {e}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
raise raise
def _update_macos_platform_uuid(self, new_ids): def _update_macos_platform_uuid(self, new_ids):
"""更新macOS Platform UUID""" """Update macOS Platform UUID"""
try: try:
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist" uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
if os.path.exists(uuid_file): if os.path.exists(uuid_file):
# 使用sudo来执行plutil命令 # Use sudo to execute plutil command
cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"' cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"'
result = os.system(cmd) result = os.system(cmd)
if result == 0: if result == 0:
print("macOS Platform UUID updated successfully") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.macos_platform_uuid_updated')}{Style.RESET_ALL}")
else: else:
raise Exception("Failed to execute plutil command") raise Exception(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.failed_to_execute_plutil_command')}{Style.RESET_ALL}")
except Exception as e: except Exception as e:
print(f"Failed to update macOS Platform UUID: {e}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_macos_platform_uuid_failed', error=str(e))}{Style.RESET_ALL}")
raise raise
def reset_machine_ids(self): def reset_machine_ids(self):
"""重置机器ID并备份原文件""" """Reset machine ID and backup original file"""
try: try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.checking')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.checking')}...{Style.RESET_ALL}")
@@ -360,26 +553,35 @@ class MachineIDResetter:
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
new_ids = self.generate_new_ids() new_ids = self.generate_new_ids()
# 更新配置文件 # Update configuration file
config.update(new_ids) config.update(new_ids)
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.saving_json')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.saving_json')}...{Style.RESET_ALL}")
with open(self.db_path, "w", encoding="utf-8") as f: with open(self.db_path, "w", encoding="utf-8") as f:
json.dump(config, f, indent=4) json.dump(config, f, indent=4)
# 更新SQLite数据库 # Update SQLite database
self.update_sqlite_db(new_ids) self.update_sqlite_db(new_ids)
# 更新系统ID # Update system IDs
self.update_system_ids(new_ids) self.update_system_ids(new_ids)
# 检查 Cursor 版本并执行相应的操作
greater_than_0_45 = check_cursor_version(self.translator) ### Remove In v1.7.02
if greater_than_0_45: # Modify workbench.desktop.main.js
print(f"{Fore.CYAN}{EMOJI['INFO']} 检测到 Cursor 版本 >= 0.45.0,正在修补 getMachineId...{Style.RESET_ALL}")
patch_cursor_get_machine_id(self.translator) # workbench_path = get_workbench_cursor_path(self.translator)
else: # modify_workbench_js(workbench_path, self.translator)
print(f"{Fore.YELLOW}{EMOJI['INFO']} Cursor 版本 < 0.45.0,跳过 getMachineId 修补{Style.RESET_ALL}")
### Remove In v1.7.02
# Check Cursor version and perform corresponding actions
# greater_than_0_45 = check_cursor_version(self.translator)
# 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}")
# patch_cursor_get_machine_id(self.translator)
# else:
# 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}")
@@ -396,13 +598,51 @@ class MachineIDResetter:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.process_error', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.process_error', error=str(e))}{Style.RESET_ALL}")
return False return False
def update_machine_id_file(self, machine_id: str) -> bool:
"""
Update machineId file with new machine_id
Args:
machine_id (str): New machine ID to write
Returns:
bool: True if successful, False otherwise
"""
try:
# Get the machineId file path
machine_id_path = get_cursor_machine_id_path()
# Create directory if it doesn't exist
os.makedirs(os.path.dirname(machine_id_path), exist_ok=True)
# Create backup if file exists
if os.path.exists(machine_id_path):
backup_path = machine_id_path + ".backup"
try:
shutil.copy2(machine_id_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('reset.backup_created', path=backup_path) if self.translator else f'Backup created at: {backup_path}'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_creation_failed', error=str(e)) if self.translator else f'Could not create backup: {str(e)}'}{Style.RESET_ALL}")
# Write new machine ID to file
with open(machine_id_path, "w", encoding="utf-8") as f:
f.write(machine_id)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.update_success') if self.translator else 'Successfully updated machineId file'}{Style.RESET_ALL}")
return True
except Exception as e:
error_msg = f"Failed to update machineId file: {str(e)}"
if self.translator:
error_msg = self.translator.get('reset.update_failed', error=str(e))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_msg}{Style.RESET_ALL}")
return False
def run(translator=None): def run(translator=None):
"""便捷函数,用于直接调用重置功能""" """Convenient function for directly calling the reset function"""
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}")
resetter = MachineIDResetter(translator) # 正確傳遞 translator resetter = MachineIDResetter(translator) # Correctly pass translator
resetter.reset_machine_ids() resetter.reset_machine_ids()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")

View File

@@ -1,4 +1,4 @@
# 設置顏色主題 # set color theme
$Theme = @{ $Theme = @{
Primary = 'Cyan' Primary = 'Cyan'
Success = 'Green' Success = 'Green'
@@ -17,7 +17,7 @@ $Logo = @"
"@ "@
# 美化輸出函數 # Beautiful Output Function
function Write-Styled { function Write-Styled {
param ( param (
[string]$Message, [string]$Message,
@@ -40,7 +40,7 @@ function Write-Styled {
} }
} }
# 獲取版本號函數 # Get version number function
function Get-LatestVersion { function Get-LatestVersion {
try { try {
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest" $latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest"
@@ -54,28 +54,28 @@ function Get-LatestVersion {
} }
} }
# 顯示 Logo # Show Logo
Write-Host $Logo -ForegroundColor $Theme.Primary Write-Host $Logo -ForegroundColor $Theme.Primary
$releaseInfo = Get-LatestVersion $releaseInfo = Get-LatestVersion
$version = $releaseInfo.Version $version = $releaseInfo.Version
Write-Host "Version $version" -ForegroundColor $Theme.Info Write-Host "Version $version" -ForegroundColor $Theme.Info
Write-Host "Created by YeongPin`n" -ForegroundColor $Theme.Info Write-Host "Created by YeongPin`n" -ForegroundColor $Theme.Info
# 設置 TLS 1.2 # Set TLS 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# 主安裝函數 # Main installation function
function Install-CursorFreeVIP { function Install-CursorFreeVIP {
Write-Styled "Start downloading Cursor Free VIP" -Color $Theme.Primary -Prefix "Download" Write-Styled "Start downloading Cursor Free VIP" -Color $Theme.Primary -Prefix "Download"
try { try {
# 獲取最新版本 # Get latest version
Write-Styled "Checking latest version..." -Color $Theme.Primary -Prefix "Update" Write-Styled "Checking latest version..." -Color $Theme.Primary -Prefix "Update"
$releaseInfo = Get-LatestVersion $releaseInfo = Get-LatestVersion
$version = $releaseInfo.Version $version = $releaseInfo.Version
Write-Styled "Found latest version: $version" -Color $Theme.Success -Prefix "Version" Write-Styled "Found latest version: $version" -Color $Theme.Success -Prefix "Version"
# 查找對應的資源 # Find corresponding resources
$asset = $releaseInfo.Assets | Where-Object { $_.name -eq "CursorFreeVIP_${version}_windows.exe" } $asset = $releaseInfo.Assets | Where-Object { $_.name -eq "CursorFreeVIP_${version}_windows.exe" }
if (!$asset) { if (!$asset) {
Write-Styled "File not found: CursorFreeVIP_${version}_windows.exe" -Color $Theme.Error -Prefix "Error" Write-Styled "File not found: CursorFreeVIP_${version}_windows.exe" -Color $Theme.Error -Prefix "Error"
@@ -86,22 +86,114 @@ function Install-CursorFreeVIP {
throw "Cannot find target file" throw "Cannot find target file"
} }
# 下載到Downloads文件夾 # Check if Downloads folder already exists for the corresponding version
$DownloadsPath = [Environment]::GetFolderPath("UserProfile") + "\Downloads" $DownloadsPath = [Environment]::GetFolderPath("UserProfile") + "\Downloads"
$downloadPath = Join-Path $DownloadsPath "CursorFreeVIP.exe" $downloadPath = Join-Path $DownloadsPath "CursorFreeVIP_${version}_windows.exe"
Write-Styled "Downloading to Downloads folder..." -Color $Theme.Primary -Prefix "Download" if (Test-Path $downloadPath) {
Write-Styled "Found existing installation file" -Color $Theme.Success -Prefix "Found"
Write-Styled "Location: $downloadPath" -Color $Theme.Info -Prefix "Location"
# Check if running with administrator privileges
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Styled "Requesting administrator privileges..." -Color $Theme.Warning -Prefix "Admin"
# Create new process with administrator privileges
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = $downloadPath
$startInfo.UseShellExecute = $true
$startInfo.Verb = "runas"
try {
[System.Diagnostics.Process]::Start($startInfo)
Write-Styled "Program started with admin privileges" -Color $Theme.Success -Prefix "Launch"
return
}
catch {
Write-Styled "Failed to start with admin privileges. Starting normally..." -Color $Theme.Warning -Prefix "Warning"
Start-Process $downloadPath
return
}
}
# If already running with administrator privileges, start directly
Start-Process $downloadPath
return
}
Write-Styled "No existing installation file found, starting download..." -Color $Theme.Primary -Prefix "Download"
# Create WebClient and add progress event
$webClient = New-Object System.Net.WebClient $webClient = New-Object System.Net.WebClient
$webClient.Headers.Add("User-Agent", "PowerShell Script") $webClient.Headers.Add("User-Agent", "PowerShell Script")
$webClient.DownloadFile($asset.browser_download_url, $downloadPath)
# Define progress variables
$Global:downloadedBytes = 0
$Global:totalBytes = 0
$Global:lastProgress = 0
$Global:lastBytes = 0
$Global:lastTime = Get-Date
# Download progress event
$eventId = [guid]::NewGuid()
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
$Global:downloadedBytes = $EventArgs.BytesReceived
$Global:totalBytes = $EventArgs.TotalBytesToReceive
$progress = [math]::Round(($Global:downloadedBytes / $Global:totalBytes) * 100, 1)
# Only update display when progress changes by more than 1%
if ($progress -gt $Global:lastProgress + 1) {
$Global:lastProgress = $progress
$downloadedMB = [math]::Round($Global:downloadedBytes / 1MB, 2)
$totalMB = [math]::Round($Global:totalBytes / 1MB, 2)
# Calculate download speed
$currentTime = Get-Date
$timeSpan = ($currentTime - $Global:lastTime).TotalSeconds
if ($timeSpan -gt 0) {
$bytesChange = $Global:downloadedBytes - $Global:lastBytes
$speed = $bytesChange / $timeSpan
# Choose appropriate unit based on speed
$speedDisplay = if ($speed -gt 1MB) {
"$([math]::Round($speed / 1MB, 2)) MB/s"
} elseif ($speed -gt 1KB) {
"$([math]::Round($speed / 1KB, 2)) KB/s"
} else {
"$([math]::Round($speed, 2)) B/s"
}
Write-Host "`rDownloading: $downloadedMB MB / $totalMB MB ($progress%) - $speedDisplay" -NoNewline -ForegroundColor Cyan
# Update last data
$Global:lastBytes = $Global:downloadedBytes
$Global:lastTime = $currentTime
}
}
} | Out-Null
# Download completed event
Register-ObjectEvent -InputObject $webClient -EventName DownloadFileCompleted -Action {
Write-Host "`r" -NoNewline
Write-Styled "Download completed!" -Color $Theme.Success -Prefix "Complete"
Unregister-Event -SourceIdentifier $eventId
} | Out-Null
# Start download
$webClient.DownloadFileAsync([Uri]$asset.browser_download_url, $downloadPath)
# Wait for download to complete
while ($webClient.IsBusy) {
Start-Sleep -Milliseconds 100
}
Write-Styled "Download completed!" -Color $Theme.Success -Prefix "Complete"
Write-Styled "File location: $downloadPath" -Color $Theme.Info -Prefix "Location" Write-Styled "File location: $downloadPath" -Color $Theme.Info -Prefix "Location"
Write-Styled "Starting program..." -Color $Theme.Primary -Prefix "Launch" Write-Styled "Starting program..." -Color $Theme.Primary -Prefix "Launch"
# 運行程序 # Run program
Start-Process $downloadPath Start-Process $downloadPath
} }
catch { catch {
Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "Error" Write-Styled $_.Exception.Message -Color $Theme.Error -Prefix "Error"
@@ -109,7 +201,7 @@ function Install-CursorFreeVIP {
} }
} }
# 執行安裝 # Execute installation
try { try {
Install-CursorFreeVIP Install-CursorFreeVIP
} }
@@ -120,4 +212,4 @@ catch {
finally { finally {
Write-Host "`nPress any key to exit..." -ForegroundColor $Theme.Info Write-Host "`nPress any key to exit..." -ForegroundColor $Theme.Info
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
} }

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# 顏色定義 # Color definitions
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
@@ -22,7 +22,7 @@ EOF
echo -e "${NC}" echo -e "${NC}"
} }
# 获取下载文件夹路径 # Get download folder path
get_downloads_dir() { get_downloads_dir() {
if [[ "$(uname)" == "Darwin" ]]; then if [[ "$(uname)" == "Darwin" ]]; then
echo "$HOME/Downloads" echo "$HOME/Downloads"
@@ -36,59 +36,139 @@ get_downloads_dir() {
fi fi
} }
# 獲取最新版本 # Get latest version
get_latest_version() { get_latest_version() {
echo -e "${CYAN} 正在檢查最新版本...${NC}" echo -e "${CYAN} Checking latest version...${NC}"
local latest_release local latest_release
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest) latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo -e "${RED}無法獲取最新版本信息${NC}" echo -e "${RED}Cannot get latest version information${NC}"
exit 1 exit 1
fi fi
VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v') VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v')
echo -e "${GREEN}找到最新版本: ${VERSION}${NC}" echo -e "${GREEN}Found latest version: ${VERSION}${NC}"
} }
# 檢測系統類型 # Detect system type and architecture
detect_os() { detect_os() {
if [[ "$(uname)" == "Darwin" ]]; then if [[ "$(uname)" == "Darwin" ]]; then
OS="mac" # Detect macOS architecture
else ARCH=$(uname -m)
if [[ "$ARCH" == "arm64" ]]; then
OS="mac_arm64"
echo -e "${CYAN} Detected macOS ARM64 architecture${NC}"
else
OS="mac_intel"
echo -e "${CYAN} Detected macOS Intel architecture${NC}"
fi
elif [[ "$(uname)" == "Linux" ]]; then
OS="linux" OS="linux"
echo -e "${CYAN} Detected Linux system${NC}"
else
# Assume Windows
OS="windows"
echo -e "${CYAN} Detected Windows system${NC}"
fi fi
} }
# 下載並安裝 # Install and download
install_cursor_free_vip() { install_cursor_free_vip() {
local downloads_dir=$(get_downloads_dir) local downloads_dir=$(get_downloads_dir)
local binary_name="CursorFreeVIP_${VERSION}_${OS}" local binary_name="CursorFreeVIP_${VERSION}_${OS}"
local binary_path="${downloads_dir}/cursor-free-vip" local binary_path="${downloads_dir}/${binary_name}"
local download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}" local download_url="https://github.com/yeongpin/cursor-free-vip/releases/download/v${VERSION}/${binary_name}"
echo -e "${CYAN} 正在下載到 ${downloads_dir}...${NC}" # Check if file already exists
if [ -f "${binary_path}" ]; then
echo -e "${GREEN}✅ Found existing installation file${NC}"
echo -e "${CYAN} Location: ${binary_path}${NC}"
# Check if running as root
if [ "$EUID" -ne 0 ]; then
echo -e "${YELLOW}⚠️ Requesting administrator privileges...${NC}"
if command -v sudo >/dev/null 2>&1; then
echo -e "${CYAN} Starting program with sudo...${NC}"
sudo chmod +x "${binary_path}"
sudo "${binary_path}"
else
echo -e "${YELLOW}⚠️ sudo not found, trying to run normally...${NC}"
chmod +x "${binary_path}"
"${binary_path}"
fi
else
# Already running as root
echo -e "${CYAN} Already running as root, starting program...${NC}"
chmod +x "${binary_path}"
"${binary_path}"
fi
return
fi
echo -e "${CYAN} No existing installation file found, starting download...${NC}"
echo -e "${CYAN} Downloading to ${downloads_dir}...${NC}"
echo -e "${CYAN} Download link: ${download_url}${NC}"
# Check if file exists
if curl --output /dev/null --silent --head --fail "$download_url"; then
echo -e "${GREEN}✅ File exists, starting download...${NC}"
else
echo -e "${RED}❌ Download link does not exist: ${download_url}${NC}"
echo -e "${YELLOW}⚠️ Trying without architecture...${NC}"
# Try without architecture
if [[ "$OS" == "mac_arm64" || "$OS" == "mac_intel" ]]; then
OS="mac"
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
echo -e "${RED}❌ New download link does not exist${NC}"
exit 1
fi
else
exit 1
fi
fi
# Download file
if ! curl -L -o "${binary_path}" "$download_url"; then if ! curl -L -o "${binary_path}" "$download_url"; then
echo -e "${RED}下載失敗${NC}" echo -e "${RED}Download failed${NC}"
exit 1 exit 1
fi fi
echo -e "${CYAN} 正在設置執行權限...${NC}" # Check downloaded file size
local file_size=$(stat -f%z "${binary_path}" 2>/dev/null || stat -c%s "${binary_path}" 2>/dev/null)
echo -e "${CYAN} Downloaded file size: ${file_size} bytes${NC}"
# If file is too small, it might be an error message
if [ "$file_size" -lt 1000 ]; then
echo -e "${YELLOW}⚠️ Warning: Downloaded file is too small, possibly not a valid executable file${NC}"
echo -e "${YELLOW}⚠️ File content:${NC}"
cat "${binary_path}"
echo ""
echo -e "${RED}❌ Download failed, please check version and operating system${NC}"
exit 1
fi
echo -e "${CYAN} Setting executable permissions...${NC}"
chmod +x "${binary_path}" chmod +x "${binary_path}"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo -e "${GREEN}安裝完成!${NC}" echo -e "${GREEN}Installation completed!${NC}"
echo -e "${CYAN} 程序已下載到: ${binary_path}${NC}" echo -e "${CYAN} Program downloaded to: ${binary_path}${NC}"
echo -e "${CYAN} 正在啟動程序...${NC}" echo -e "${CYAN} Starting program...${NC}"
# 直接运行程序 # Run program directly
"${binary_path}" "${binary_path}"
else else
echo -e "${RED}安裝失敗${NC}" echo -e "${RED}Installation failed${NC}"
exit 1 exit 1
fi fi
} }
# 主程序 # Main program
main() { main() {
print_logo print_logo
get_latest_version get_latest_version
@@ -96,5 +176,5 @@ main() {
install_cursor_free_vip install_cursor_free_vip
} }
# 運行主程序 # Run main program
main main