Compare commits

...

42 Commits

Author SHA1 Message Date
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
20 changed files with 1263 additions and 365 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.4.04 version=1.7.01
VERSION=1.4.04 VERSION=1.7.01

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.4.01' default: '1.5.03'
permissions: permissions:
contents: write contents: write
@@ -206,4 +206,4 @@ jobs:
draft: false draft: false
prerelease: false prerelease: false
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

53
.gitignore vendored
View File

@@ -1,8 +1,51 @@
cursor_accounts.txt __pycache__
/venv server/
/__pycache__ venv/
dist check_license.py
build 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 install.bat
run.bat run.bat
temp_account_info.txt 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,63 @@
# Change Log # Change Log
## 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 ## v1.4.04
1. Change Some Language Info to English | 更改一些語言信息為英文 1. Change Some Language Info to English | 更改一些語言信息為英文
@@ -184,4 +242,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">
@@ -12,14 +12,14 @@
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
</p> </p>
<h4>Support Latest 0.46.3 Version | 支持最新0.46.3本</h4> <h4>Support Latest 0.46.8 Version | 支持最新0.46.8本</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.
這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。 這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。
<p align="center"> <p align="center">
<img src="./images/pronew_2025-02-13_15-01-32.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/)
@@ -86,6 +86,21 @@ 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
```
* Use administrator to run the script <br>請使用管理員身份運行腳本 * 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>
@@ -98,9 +113,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 |
@@ -109,6 +124,11 @@ 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 | 免責聲明

3
block_domain.txt Normal file
View File

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

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,21 @@ 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): if not os.path.exists(self.db_path):
@@ -83,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")
@@ -127,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,38 +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 实例 self.email_tab = self.temp_email # Pass NewTempEmail instance
return True return True
@@ -92,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,
@@ -107,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()
@@ -126,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()
@@ -134,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)
@@ -149,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
@@ -185,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}")
@@ -200,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")
@@ -217,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():
@@ -225,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()
@@ -233,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)

View File

@@ -44,22 +44,33 @@ class CursorRegistration:
self.signup_tab = None self.signup_tab = None
self.email_tab = None self.email_tab = None
# Account information # Generate account information
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""" """Setup Email"""
try: try:
@@ -70,6 +81,7 @@ class CursorRegistration:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}\n{Style.RESET_ALL}")
return True return True
except Exception as e: except Exception as e:
@@ -155,6 +167,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

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -131,7 +131,17 @@
"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_code_input": "Manual Code Input",
"manual_email_input": "Manual Email 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",
@@ -203,7 +213,24 @@
"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": { "update": {
"title": "Disable Cursor Auto Update", "title": "Disable Cursor Auto Update",
@@ -217,5 +244,13 @@
"directory_removed": "Directory Removed", "directory_removed": "Directory Removed",
"creating_block_file": "Creating Block File", "creating_block_file": "Creating Block File",
"block_file_created": "Block File Created" "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

@@ -131,7 +131,17 @@
"update_cursor_auth_info": "更新Cursor认证信息", "update_cursor_auth_info": "更新Cursor认证信息",
"setting_password": "设置密码", "setting_password": "设置密码",
"manual_code_input": "手动输入验证码", "manual_code_input": "手动输入验证码",
"manual_email_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 认证管理器",
@@ -200,7 +210,24 @@
"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": { "update": {
"title": "禁用 Cursor 自动更新", "title": "禁用 Cursor 自动更新",
@@ -214,5 +241,13 @@
"directory_removed": "目录已删除", "directory_removed": "目录已删除",
"creating_block_file": "创建阻止文件", "creating_block_file": "创建阻止文件",
"block_file_created": "阻止文件已创建" "block_file_created": "阻止文件已创建"
},
"updater": {
"checking": "检查更新...",
"new_version_available": "有新版本可用! (当前版本: {current}, 最新版本: {latest})",
"updating": "正在更新到最新版本。程序将自动重启。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "检查更新失败: {error}",
"continue_anyway": "继续使用当前版本..."
} }
} }

View File

@@ -112,7 +112,17 @@
"update_cursor_auth_info": "更新Cursor認證信息", "update_cursor_auth_info": "更新Cursor認證信息",
"setting_password": "設置密碼", "setting_password": "設置密碼",
"manual_code_input": "手動輸入驗證碼", "manual_code_input": "手動輸入驗證碼",
"manual_email_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 認證管理器",
@@ -181,7 +191,24 @@
"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": { "update": {
"title": "禁用 Cursor 自动更新", "title": "禁用 Cursor 自动更新",
@@ -195,5 +222,13 @@
"directory_removed": "目錄已刪除", "directory_removed": "目錄已刪除",
"creating_block_file": "創建阻止文件", "creating_block_file": "創建阻止文件",
"block_file_created": "阻止文件已創建" "block_file_created": "阻止文件已創建"
},
"updater": {
"checking": "檢查更新...",
"new_version_available": "有新版本可用! (當前版本: {current}, 最新版本: {latest})",
"updating": "正在更新到最新版本。程序將自動重啟。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "檢查更新失敗: {error}",
"continue_anyway": "繼續使用當前版本..."
} }
} }

116
main.py
View File

@@ -3,12 +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 ctypes
from ctypes import windll
import locale import locale
import platform import platform
import requests
import subprocess
# 只在 Windows 系统上导入 windll
if platform.system() == 'Windows':
import ctypes
# 只在 Windows 上导入 windll
from ctypes import windll
# 初始化colorama # 初始化colorama
init() init()
@@ -29,8 +35,8 @@ EMOJI = {
class Translator: class Translator:
def __init__(self): def __init__(self):
self.current_language = self.detect_system_language() # Changed to use system language
self.translations = {} self.translations = {}
self.current_language = self.detect_system_language() # 使用正确的方法名
self.fallback_language = 'en' # Fallback language if translation is missing self.fallback_language = 'en' # Fallback language if translation is missing
self.load_translations() self.load_translations()
@@ -51,8 +57,12 @@ class Translator:
def _detect_windows_language(self): def _detect_windows_language(self):
"""Detect language on Windows systems""" """Detect language on Windows systems"""
try: try:
# Get the keyboard layout # 确保我们在 Windows 上
user32 = ctypes.WinDLL('user32', use_last_error=True) if platform.system() != 'Windows':
return 'en'
# 获取键盘布局
user32 = ctypes.windll.user32
hwnd = user32.GetForegroundWindow() hwnd = user32.GetForegroundWindow()
threadid = user32.GetWindowThreadProcessId(hwnd, 0) threadid = user32.GetWindowThreadProcessId(hwnd, 0)
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
@@ -195,8 +205,87 @@ def select_language():
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}")
return False 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:
@@ -210,19 +299,19 @@ 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 import cursor_register_manual
cursor_register_manual.main(translator) cursor_register_manual.main(translator)
break print_menu()
elif choice == "4": elif choice == "4":
import quit_cursor import quit_cursor
quit_cursor.quit_cursor(translator) quit_cursor.quit_cursor(translator)
break print_menu()
elif choice == "5": elif choice == "5":
if select_language(): if select_language():
print_menu() print_menu()
@@ -230,7 +319,7 @@ def main():
elif choice == "6": elif choice == "6":
import disable_auto_update import disable_auto_update
disable_auto_update.run(translator) disable_auto_update.run(translator)
break 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()
@@ -241,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
@@ -94,40 +97,181 @@ 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'
}
}
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 +279,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 +297,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 +314,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 +341,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 +386,51 @@ 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(f"\n{translator.get('register.setting_password')}") max_retries = 5
password_input = page.ele("@name=password") for i in range(max_retries):
try:
# 使用 DrissionPage 的方式查找密码输入框
password_input = page.ele('@type=password', timeout=3)
if password_input:
break
time.sleep(2)
except:
if i == max_retries - 1:
print(f"{Fore.RED}{translator.get('register.password_field_not_found') if translator else '未找到密码输入框'}{Style.RESET_ALL}")
return False
continue
if password_input: if password_input:
# 清除可能存在的旧值
password_input.click()
time.sleep(0.5)
password_input.input(password) password_input.input(password)
time.sleep(random.uniform(0.5, 1.0)) time.sleep(1)
submit_button = page.ele("@type=submit") # 查找并点击提交按钮
submit_button = page.ele('@type=submit')
if submit_button: if submit_button:
submit_button.click() submit_button.click()
time.sleep(random.uniform(2.0, 3.0)) time.sleep(2)
return True
if translator:
print(f"{Fore.GREEN}{translator.get('register.password_success')}{Style.RESET_ALL}")
else: else:
print(f"{translator.get('register.password_success')}: {password}") print(f"{Fore.RED}{translator.get('register.continue_button_not_found') if translator else '未找到继续按钮'}{Style.RESET_ALL}")
return True return False
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e))}{Style.RESET_ALL}")
else: else:
print(f"{translator.get('register.password_error')}: {e}") print(f"{Fore.RED}{translator.get('register.password_input_failed') if translator else '密码输入失败'}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{translator.get('register.password_setting_error', error=str(e)) if translator else f'设置密码时出错: {str(e)}'}{Style.RESET_ALL}")
return False return False
def handle_verification_code(browser_tab, email_tab, controller, email, password, translator=None): def handle_verification_code(browser_tab, email_tab, controller, email, password, translator=None):
@@ -272,8 +438,6 @@ 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(f"\n{translator.get('register.waiting_for_verification_code')}")
# 检查是否使用手动输入验证码 # 检查是否使用手动输入验证码
if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式 if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式
@@ -290,13 +454,11 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 处理最后一次 Turnstile 验证 # 处理最后一次 Turnstile 验证
if handle_turnstile(browser_tab, translator): if handle_turnstile(browser_tab, translator):
if translator: if translator:
print(f"{translator.get('register.verification_success')}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print(f"{translator.get('register.verification_success')}")
time.sleep(2) time.sleep(2)
# 访问设置页面 # 访问设置页面
print(f"{translator.get('register.visiting_url')}: https://www.cursor.com/settings") print(f"{Fore.CYAN} {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings") browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载 time.sleep(3) # 等待页面加载
return True, browser_tab return True, browser_tab
@@ -322,23 +484,17 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
time.sleep(random.uniform(0.1, 0.3)) 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:
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: 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, browser_tab return True, browser_tab
@@ -359,31 +515,23 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
if translator: if translator:
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}") print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
else:
print("开始获取验证码...")
for attempt in range(max_attempts): for attempt in range(max_attempts):
# 检查是否超时 # 检查是否超时
if time.time() - start_time > timeout: if time.time() - start_time > timeout:
if translator: if translator:
print(f"{Fore.RED}{translator.get('register.verification_timeout')}{Style.RESET_ALL}") print(f"{Fore.RED}{translator.get('register.verification_timeout')}{Style.RESET_ALL}")
else:
print("获取验证码超时...")
break break
verification_code = controller.get_verification_code() verification_code = controller.get_verification_code()
if verification_code: if verification_code:
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(f"成功获取验证码: {verification_code}")
break break
remaining_time = int(timeout - (time.time() - start_time)) remaining_time = int(timeout - (time.time() - start_time))
if translator: if translator:
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}") 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()
@@ -397,23 +545,17 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
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(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: 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) # 等待页面加载
@@ -423,8 +565,6 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
else: else:
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}")
else:
print("最后一次验证失败")
return False, None return False, None
return False, None return False, None
@@ -432,8 +572,6 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
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:
print(f"处理验证码时出错: {e}")
return False, None return False, None
def handle_sign_in(browser_tab, email, password, translator=None): def handle_sign_in(browser_tab, email, password, translator=None):
@@ -496,25 +634,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)
# 如果没有提供账号信息,则生成随机信息 # 如果没有提供账号信息,则生成随机信息
@@ -535,41 +667,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):
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

@@ -7,7 +7,7 @@ import requests
import random import random
import string import string
# 初始化 colorama # Initialize colorama
init() init()
class NewTempEmail: class NewTempEmail:
@@ -23,85 +23,222 @@ class NewTempEmail:
self.token = None self.token = None
self.email = None self.email = None
self.password = None self.password = None
self.blocked_domains = self.get_blocked_domains()
def get_blocked_domains(self):
"""Get blocked domains list"""
try:
block_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/block_domain.txt"
response = requests.get(block_url, timeout=5)
if response.status_code == 200:
# Split text and remove empty lines
domains = [line.strip() for line in response.text.split('\n') if line.strip()]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 已加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
return domains
return []
except Exception as e:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 获取被屏蔽域名列表失败: {str(e)}{Style.RESET_ALL}")
return []
def exclude_blocked_domains(self, domains):
"""Exclude blocked domains"""
if not self.blocked_domains:
return domains
filtered_domains = []
for domain in domains:
if domain['domain'] not in self.blocked_domains:
filtered_domains.append(domain)
excluded_count = len(domains) - len(filtered_domains)
if excluded_count > 0:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domains_excluded', domains=excluded_count)}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 已排除 {excluded_count} 个被屏蔽的域名{Style.RESET_ALL}")
return filtered_domains
def _generate_credentials(self): def _generate_credentials(self):
"""生成随机用户名和密码""" """generate random username and password"""
username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10)) username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=12)) password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=12))
return username, password return username, password
def create_email(self): def create_email(self):
"""创建临时邮箱""" """create temporary email"""
try: max_retries = 3 # Maximum number of retries
if self.translator: attempt = 0 # Current attempt count
print(f"{Fore.CYAN} {self.translator.get('email.visiting_site').replace('mail.tm', self.selected_service['name'])}{Style.RESET_ALL}")
else: while attempt < max_retries:
print(f"{Fore.CYAN} 正在访问 {self.selected_service['name']}...{Style.RESET_ALL}") attempt += 1
try:
# 获取可用域名列表 if self.translator:
domains_response = requests.get(f"{self.api_url}/domains") print(f"{Fore.CYAN} {self.translator.get('email.visiting_site').replace('mail.tm', self.selected_service['name'])}{Style.RESET_ALL}")
if domains_response.status_code != 200: else:
raise Exception(f"{self.translator.get('email.failed_to_get_available_domains')}") print(f"{Fore.CYAN} 正在访问 {self.selected_service['name']}...{Style.RESET_ALL}")
domains = domains_response.json()["hydra:member"] # Get available domain list
if not domains: try:
raise Exception(f"{self.translator.get('email.no_available_domains')}") 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}")
username, password = self._generate_credentials() print(f"{Fore.RED}{self.translator.get('email.domains_list_error', error=domains_response.text)}{Style.RESET_ALL}")
self.password = password 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"]
email = f"{username}@{domains[0]['domain']}" print(f"{Fore.CYAN} {self.translator.get('email.available_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
account_data = {
"address": email, if not domains:
"password": password 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}")
create_response = requests.post(f"{self.api_url}/accounts", json=account_data) raise
if create_response.status_code != 201:
raise Exception(f"{self.translator.get('email.failed_to_create_account')}") # Exclude blocked domains
try:
# 获取访问令牌 filtered_domains = self.exclude_blocked_domains(domains)
token_data = { if self.translator:
"address": email, print(f"{Fore.CYAN} {self.translator.get('email.domains_filtered', count=len(filtered_domains))}{Style.RESET_ALL}")
"password": password else:
} print(f"{Fore.CYAN} 过滤后剩余 {len(filtered_domains)} 个可用域名{Style.RESET_ALL}")
token_response = requests.post(f"{self.api_url}/token", json=token_data) if not filtered_domains:
if token_response.status_code != 200: if self.translator:
raise Exception(f"{self.translator.get('email.failed_to_get_access_token')}") print(f"{Fore.RED}{self.translator.get('email.all_domains_blocked')}{Style.RESET_ALL}")
else:
self.token = token_response.json()["token"] print(f"{Fore.RED}❌ 所有域名都被屏蔽了,尝试切换服务{Style.RESET_ALL}")
self.email = email
# Switch to another service
if self.translator: for service in self.services:
print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}") if service["api_url"] != self.api_url:
else: self.selected_service = service
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}") self.api_url = service["api_url"]
return email if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.switching_service', service=service['name'])}{Style.RESET_ALL}")
except Exception as e: else:
if self.translator: print(f"{Fore.CYAN} 切换到 {service['name']} 服务{Style.RESET_ALL}")
print(f"{Fore.RED}{self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}") return self.create_email() # Recursively call
else:
print(f"{Fore.RED}❌ 创建邮箱出错: {str(e)}{Style.RESET_ALL}") raise Exception(f"{self.translator.get('email.no_available_domains_after_filtering') if self.translator else '过滤后没有可用域名'}")
return None 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}")
# 使用 API 获取最新邮件 # Use API to get latest email
headers = {"Authorization": f"Bearer {self.token}"} headers = {"Authorization": f"Bearer {self.token}"}
response = requests.get(f"{self.api_url}/messages", headers=headers) response = requests.get(f"{self.api_url}/messages", headers=headers)
@@ -128,7 +265,7 @@ class NewTempEmail:
def check_for_cursor_email(self): def check_for_cursor_email(self):
"""检查是否有 Cursor 的验证邮件""" """检查是否有 Cursor 的验证邮件"""
try: try:
# 使用 API 获取邮件列表 # Use API to get email list
headers = {"Authorization": f"Bearer {self.token}"} headers = {"Authorization": f"Bearer {self.token}"}
response = requests.get(f"{self.api_url}/messages", headers=headers) response = requests.get(f"{self.api_url}/messages", headers=headers)
@@ -136,7 +273,7 @@ class NewTempEmail:
messages = response.json()["hydra:member"] messages = response.json()["hydra:member"]
for message in messages: for message in messages:
if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]: if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]:
# 获取邮件内容 # Get email content
message_id = message["id"] message_id = message["id"]
message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers) message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers)
if message_response.status_code == 200: if message_response.status_code == 200:
@@ -160,9 +297,9 @@ class NewTempEmail:
return False return False
def get_verification_code(self): def get_verification_code(self):
"""获取验证码""" """get verification code"""
try: try:
# 使用 API 获取邮件列表 # Use API to get email list
headers = {"Authorization": f"Bearer {self.token}"} headers = {"Authorization": f"Bearer {self.token}"}
response = requests.get(f"{self.api_url}/messages", headers=headers) response = requests.get(f"{self.api_url}/messages", headers=headers)
@@ -170,14 +307,14 @@ class NewTempEmail:
messages = response.json()["hydra:member"] messages = response.json()["hydra:member"]
for message in messages: for message in messages:
if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]: if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]:
# 获取邮件内容 # Get email content
message_id = message["id"] message_id = message["id"]
message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers) message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers)
if message_response.status_code == 200: if message_response.status_code == 200:
# 从邮件内容中提取验证码 # Extract verification code from email content
email_content = message_response.json()["text"] email_content = message_response.json()["text"]
# 查找6位数字验证码 # Find 6-digit verification code
import re import re
code_match = re.search(r'\b\d{6}\b', email_content) code_match = re.search(r'\b\d{6}\b', email_content)
@@ -213,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()

View File

@@ -64,6 +64,58 @@ 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
"""
if sys.platform == "win32": # Windows
return os.path.join(os.getenv("APPDATA"), "Cursor", "machineId")
elif sys.platform == "linux": # Linux
return os.path.expanduser("~/.config/Cursor/machineId")
elif sys.platform == "darwin": # macOS
return os.path.expanduser("~/Library/Application Support/Cursor/machineId")
else:
raise OSError(f"Unsupported operating system: {sys.platform}")
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 number check"""
version_pattern = r"^\d+\.\d+\.\d+$" version_pattern = r"^\d+\.\d+\.\d+$"
@@ -102,6 +154,77 @@ 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:
"""Modify main.js file""" """Modify main.js file"""
try: try:
@@ -214,11 +337,20 @@ class MachineIDResetter:
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb" "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
)) ))
elif sys.platform == "linux": # Linux elif sys.platform == "linux": # Linux
self.db_path = os.path.abspath(os.path.expanduser( # 获取实际用户的主目录
"~/.config/Cursor/User/globalStorage/storage.json" sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
actual_home = f"/home/{sudo_user}"
else:
actual_home = os.path.expanduser("~")
self.db_path = os.path.abspath(os.path.join(
actual_home,
".config/Cursor/User/globalStorage/storage.json"
)) ))
self.sqlite_path = os.path.abspath(os.path.expanduser( self.sqlite_path = os.path.abspath(os.path.join(
"~/.config/Cursor/User/globalStorage/state.vscdb" actual_home,
".config/Cursor/User/globalStorage/state.vscdb"
)) ))
else: else:
raise NotImplementedError(f"Not Supported OS: {sys.platform}") raise NotImplementedError(f"Not Supported OS: {sys.platform}")
@@ -237,6 +369,8 @@ class MachineIDResetter:
# Generate new 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,
@@ -309,12 +443,12 @@ 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):
@@ -326,11 +460,11 @@ class MachineIDResetter:
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):
@@ -373,6 +507,10 @@ class MachineIDResetter:
# Update system IDs # Update system IDs
self.update_system_ids(new_ids) self.update_system_ids(new_ids)
# Modify workbench.desktop.main.js
workbench_path = get_workbench_cursor_path(self.translator)
modify_workbench_js(workbench_path, self.translator)
# Check Cursor version and perform corresponding actions # Check Cursor version and perform corresponding actions
greater_than_0_45 = check_cursor_version(self.translator) greater_than_0_45 = check_cursor_version(self.translator)
if greater_than_0_45: if greater_than_0_45:
@@ -396,6 +534,44 @@ 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""" """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}")

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