mirror of
https://git.axenov.dev/mirrors/cursor-free-vip.git
synced 2025-12-28 14:40:39 +03:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe3e27561b | ||
|
|
bf2bea71eb | ||
|
|
77a61647dd | ||
|
|
813dd4431e | ||
|
|
d7116b8cf3 | ||
|
|
f5a7acc4e3 | ||
|
|
479933844a | ||
|
|
93046d7f03 | ||
|
|
e7ca31b710 | ||
|
|
87b99b0d16 | ||
|
|
e983b6f560 | ||
|
|
21ca7ab24f | ||
|
|
e7468644a4 | ||
|
|
2bca7d7d14 | ||
|
|
040c5f5836 | ||
|
|
4a86bbeeb4 | ||
|
|
68b7888a83 | ||
|
|
78dee025a7 | ||
|
|
17c2b4b243 | ||
|
|
2acb271f19 | ||
|
|
b688b67c26 | ||
|
|
3543615a69 | ||
|
|
24b0a5c09e | ||
|
|
5bbba05f69 | ||
|
|
72c95e4b4f | ||
|
|
8afd5df4ea | ||
|
|
a7a97b5621 | ||
|
|
e2e2ebc12e | ||
|
|
d131bccac0 | ||
|
|
d852bcff50 | ||
|
|
4c91525082 | ||
|
|
b565232ba6 | ||
|
|
5725a2b2f7 | ||
|
|
6ec6f8bdbf | ||
|
|
5c1bcff55a | ||
|
|
025c4e2ef3 | ||
|
|
6ac1294bee | ||
|
|
fb443592d3 | ||
|
|
c30a62b072 | ||
|
|
8bc509cccf | ||
|
|
9f814708d1 | ||
|
|
1889f9827a | ||
|
|
57d2d40e67 | ||
|
|
04fa6ee935 | ||
|
|
ed1e0f787e | ||
|
|
240716e45f | ||
|
|
35bbe6c93c | ||
|
|
fed50a31cc | ||
|
|
57ea4dd25a |
39
.github/workflows/build.yml
vendored
39
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
version:
|
version:
|
||||||
description: 'Version number (e.g. 1.0.9)'
|
description: 'Version number (e.g. 1.0.9)'
|
||||||
required: true
|
required: true
|
||||||
default: '1.0.9-dev'
|
default: '1.4.07'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -63,7 +63,7 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
name: CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||||
path: dist/*
|
path: dist/CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||||
|
|
||||||
build-macos-arm64:
|
build-macos-arm64:
|
||||||
needs: create-tag
|
needs: create-tag
|
||||||
@@ -90,12 +90,13 @@ jobs:
|
|||||||
- name: Build MacOS ARM executable
|
- name: Build MacOS ARM executable
|
||||||
run: |
|
run: |
|
||||||
pyinstaller build.spec
|
pyinstaller build.spec
|
||||||
|
mv "dist/CursorFreeVIP_${{ env.VERSION }}_mac" "dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64"
|
||||||
|
|
||||||
- name: Upload MacOS ARM artifact
|
- name: Upload MacOS ARM artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||||
path: dist/*
|
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
needs: create-tag
|
needs: create-tag
|
||||||
@@ -120,14 +121,18 @@ jobs:
|
|||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
|
|
||||||
- name: Build Linux executable
|
- name: Build Linux executable
|
||||||
|
env:
|
||||||
|
VERSION: ${{ env.VERSION }}
|
||||||
run: |
|
run: |
|
||||||
pyinstaller build.spec
|
pyinstaller build.spec
|
||||||
|
echo "Contents of dist directory:"
|
||||||
|
ls -la dist/
|
||||||
|
|
||||||
- name: Upload Linux artifact
|
- name: Upload Linux artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CursorFreeVIP_${{ env.VERSION }}_linux
|
name: CursorFreeVIP_${{ env.VERSION }}_linux
|
||||||
path: dist/*
|
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux
|
||||||
|
|
||||||
|
|
||||||
build-macos-intel:
|
build-macos-intel:
|
||||||
@@ -155,14 +160,16 @@ jobs:
|
|||||||
- name: Build MacOS Intel executable
|
- name: Build MacOS Intel executable
|
||||||
env:
|
env:
|
||||||
TARGET_ARCH: 'x86_64'
|
TARGET_ARCH: 'x86_64'
|
||||||
|
VERSION: ${{ env.VERSION }}
|
||||||
run: |
|
run: |
|
||||||
arch -x86_64 python3 -m PyInstaller build.spec
|
arch -x86_64 python3 -m PyInstaller build.spec
|
||||||
|
mv "dist/CursorFreeVIP_${{ env.VERSION }}_mac" "dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel"
|
||||||
|
|
||||||
- name: Upload MacOS Intel artifact
|
- name: Upload MacOS Intel artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
name: CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||||
path: dist/*
|
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||||
|
|
||||||
|
|
||||||
create-release:
|
create-release:
|
||||||
@@ -182,25 +189,21 @@ jobs:
|
|||||||
- name: Prepare release files
|
- name: Prepare release files
|
||||||
run: |
|
run: |
|
||||||
cd artifacts
|
cd artifacts
|
||||||
# 重命名文件为最终的可执行文件名
|
echo "Contents of artifacts directory:"
|
||||||
mv CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP.exe CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
|
||||||
mv CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
|
||||||
mv CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_linux
|
|
||||||
mv CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
|
||||||
# 删除空目录
|
|
||||||
rm -rf */
|
|
||||||
ls -la
|
ls -la
|
||||||
|
echo "Contents of subdirectories:"
|
||||||
|
ls -la */
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
tag_name: v${{ env.VERSION }}
|
tag_name: v${{ env.VERSION }}
|
||||||
files: |
|
files: |
|
||||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe
|
||||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
|
||||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux
|
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP_${{ env.VERSION }}_linux
|
||||||
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
47
.gitignore
vendored
Normal file
47
.gitignore
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
__pycache__
|
||||||
|
server/
|
||||||
|
venv/
|
||||||
|
check_license.py
|
||||||
|
cursor_modifier.py
|
||||||
|
reset_machine.py
|
||||||
|
Run_Venv.bat
|
||||||
|
token_monitor.py
|
||||||
|
get_mac.py
|
||||||
|
.gitignore
|
||||||
|
build.bat
|
||||||
|
build.mac.command
|
||||||
|
build.py
|
||||||
|
build.sh
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
install.bat
|
||||||
|
run.bat
|
||||||
|
|
||||||
|
temp_account_info.txt
|
||||||
|
|
||||||
|
.env copy
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
*.spec2
|
||||||
|
|
||||||
|
credentials.txt
|
||||||
|
cursor_accounts.txt
|
||||||
|
recaptcha.py
|
||||||
|
install_requirements.bat
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Project specific
|
||||||
|
*.log
|
||||||
|
*.db
|
||||||
|
*.sqlite3
|
||||||
71
CHANGELOG.md
71
CHANGELOG.md
@@ -1,5 +1,74 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## v1.5.01
|
||||||
|
1. Add: Check Latest Version | 增加檢查最新版本
|
||||||
|
2. Add: Update Command | 增加更新命令
|
||||||
|
|
||||||
|
## v1.4.08
|
||||||
|
1. Add: Print Some Account Info | 增加打印一些賬號信息
|
||||||
|
|
||||||
|
## v1.4.07
|
||||||
|
1. Add Removed break statements after each operation | 修改結束event後的break暫停應用
|
||||||
|
2. Added print_menu() calls to show the menu again | 添加print_menu()調用以再次顯示菜單
|
||||||
|
3. Updated error handling to show menu instead of exiting | 更新錯誤處理以顯示菜單而不是退出
|
||||||
|
|
||||||
|
## v1.4.06
|
||||||
|
|
||||||
|
1. Add: Blocked Domains Loaded | 增加被屏蔽的域名加載
|
||||||
|
2. Fix: Cleanup Error | 修復清理進程時出錯
|
||||||
|
3. Fix: Blocked Domains Loaded Error | 修復被屏蔽的域名加載錯誤
|
||||||
|
4. Fix: Available Domains Loaded Error | 修復可用域名加載錯誤
|
||||||
|
5. Fix: Domains Filtered Error | 修復過濾後剩餘域名錯誤
|
||||||
|
6. Fix: Domains Excluded Error | 修復排除域名錯誤
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.05
|
||||||
|
|
||||||
|
1. Fix: macOS Language Detection | 修復macOS語言檢測
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.04
|
||||||
|
|
||||||
|
1. Change Some Language Info to English | 更改一些語言信息為英文
|
||||||
|
2. Add Auto Detect System Language | 增加自動檢測系統語言
|
||||||
|
3. Fixed Some Issues | 修復一些問題
|
||||||
|
|
||||||
|
|
||||||
|
## v1.4.03
|
||||||
|
|
||||||
|
1. Switch to API-based Registration System | 改用API註冊系統替代瀏覽器操作
|
||||||
|
2. Add Support for Latest Cursor Version | 增加支持最新版本Cursor
|
||||||
|
3. Enhance Translation System | 優化多語言翻譯系統
|
||||||
|
4. Add Database Connection Status Messages | 增加數據庫連接狀態提示
|
||||||
|
5. Improve Error Handling for Database Operations | 改進數據庫操作的錯誤處理
|
||||||
|
6. Add New API Integration | 新增API集成
|
||||||
|
7. Optimize Performance and Stability | 優化性能和穩定性
|
||||||
|
|
||||||
|
## v1.4.01
|
||||||
|
|
||||||
|
1. Add Disable Cursor Auto Upgrade | 增加禁用Cursor自動升級
|
||||||
|
|
||||||
|
## v1.3.02
|
||||||
|
|
||||||
|
1. Add Buy Me a Coffee | 增加請我喝杯咖啡
|
||||||
|
2. Add PayPal | 增加PayPal
|
||||||
|
3. Very Small Fix | 非常小的修復
|
||||||
|
4. Fix main.py option number | 修復main.py選項數量
|
||||||
|
|
||||||
|
## v1.3.01
|
||||||
|
|
||||||
|
1. Add Manual Email Input | 增加手動輸入郵箱地址
|
||||||
|
2. Add Manual Code Input | 增加手動輸入驗證碼
|
||||||
|
3. Fix Cursor Options | 修復Cursor選項
|
||||||
|
|
||||||
|
|
||||||
|
## v1.2.02
|
||||||
|
|
||||||
|
1. Add PBlock | 增加PBlock
|
||||||
|
2. Remove uBlock0.chromium | 移除uBlock0.chromium
|
||||||
|
3. Optimize the logic of the script | 優化腳本邏輯
|
||||||
|
4. Optimize Size | 優化大小
|
||||||
|
|
||||||
|
|
||||||
## v1.2.01
|
## v1.2.01
|
||||||
|
|
||||||
@@ -142,4 +211,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>
|
||||||
|
|||||||
45
README.md
45
README.md
@@ -1,7 +1,7 @@
|
|||||||
# ➤ Cursor Free VIP
|
# ➤ Cursor Free VIP
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200"/>
|
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -9,16 +9,17 @@
|
|||||||
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||||
[](https://creativecommons.org/licenses/by-nc-nd/4.0/)
|
[](https://creativecommons.org/licenses/by-nc-nd/4.0/)
|
||||||
[](https://github.com/yeongpin/cursor-free-vip/stargazers)
|
[](https://github.com/yeongpin/cursor-free-vip/stargazers)
|
||||||
|
[](https://github.com/yeongpin/cursor-free-vip/releases/latest)
|
||||||
|
|
||||||
</p>
|
</p>
|
||||||
<h4>Support Latest 0.45.11 Version | 支持最新0.45.11版本</h4>
|
<h4>Support Latest 0.46.3 Version | 支持最新0.46.3本</h4>
|
||||||
|
|
||||||
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
|
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
|
||||||
|
|
||||||
這是一個自動化工具,自動註冊(除了Google驗證碼),支持 Windows 和 macOS 系統,完成Auth驗證,重置Cursor的配置。
|
這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統,完成Auth驗證,重置Cursor的配置。
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="./images/new107_2025-01-15_13-53-56.png" alt="new" width="400"/><br>
|
<img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
||||||
@@ -85,6 +86,8 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
|
|||||||
|
|
||||||
## ❗ Note | 注意事項
|
## ❗ Note | 注意事項
|
||||||
|
|
||||||
|
* Use administrator to run the script <br>請使用管理員身份運行腳本
|
||||||
|
|
||||||
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
|
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
|
||||||
|
|
||||||
* This tool is only for learning and research purposes <br>此工具僅供學習和研究使用<br>
|
* This tool is only for learning and research purposes <br>此工具僅供學習和研究使用<br>
|
||||||
@@ -111,6 +114,36 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
|
|||||||
|
|
||||||
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br>
|
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br>
|
||||||
|
|
||||||
源代碼靈感來之 | Original code inspiration from [Here](https://github.com/hmhm2022/gpt-cursor-auto)
|
|
||||||
|
|
||||||
This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne by the user.
|
This tool is only for learning and research purposes, and any consequences arising from the use of this tool are borne by the user.
|
||||||
|
|
||||||
|
## 💰 Buy Me a Coffee | 請我喝杯咖啡
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src="./images/provi-code.jpg" alt="buy_me_a_coffee" width="280"/><br>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<img src="./images/paypal.png" alt="buy_me_a_coffee" width="280"/><br>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## ⭐ Star History | 星星數
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
[](https://star-history.com/#yeongpin/cursor-free-vip&Date)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 📝 License | 授權
|
||||||
|
|
||||||
|
本項目採用 [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) 授權。
|
||||||
|
Please refer to the [LICENSE](LICENSE.md) file for details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
3
block_domain.txt
Normal file
3
block_domain.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
oakon.com
|
||||||
|
famamail.com
|
||||||
|
2925.com
|
||||||
30
build.bat
30
build.bat
@@ -2,28 +2,28 @@
|
|||||||
chcp 65001 > nul
|
chcp 65001 > nul
|
||||||
cls
|
cls
|
||||||
|
|
||||||
:: 檢查是否以管理員權限運行
|
:: Check if running with administrator privileges
|
||||||
net session >nul 2>&1
|
net session >nul 2>&1
|
||||||
if %errorLevel% == 0 (
|
if %errorLevel% == 0 (
|
||||||
:: 如果是管理員權限,只創建虛擬環境後就降權運行
|
:: If running with administrator privileges, create virtual environment and then run with normal user privileges
|
||||||
if not exist venv (
|
if not exist venv (
|
||||||
echo ℹ️ 正在創建虛擬環境...
|
echo ℹ️ 正在創建虛擬環境...
|
||||||
python -m venv venv
|
python -m venv venv
|
||||||
)
|
)
|
||||||
|
|
||||||
:: 降權運行剩餘的步驟
|
:: Run remaining steps with normal user privileges
|
||||||
echo ℹ️ 以普通用戶權限繼續...
|
echo ℹ️ 以普通用戶權限繼續...
|
||||||
powershell -Command "Start-Process -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0 run' -Verb RunAs:NO"
|
powershell -Command "Start-Process -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0 run' -Verb RunAs:NO"
|
||||||
exit /b
|
exit /b
|
||||||
) else (
|
) else (
|
||||||
:: 檢查是否是第二階段運行
|
:: Check if running in second stage
|
||||||
if "%1"=="run" (
|
if "%1"=="run" (
|
||||||
goto RUN_BUILD
|
goto RUN_BUILD
|
||||||
) else (
|
) else (
|
||||||
:: 如果是普通權限且需要創建虛擬環境,請求管理員權限
|
:: If running with normal privileges and creating virtual environment is required, request administrator privileges
|
||||||
if not exist venv (
|
if not exist venv (
|
||||||
echo ⚠️ 需要管理員權限來創建虛擬環境
|
echo ⚠️ Requires administrator privileges to create virtual environment
|
||||||
echo ℹ️ 正在請求管理員權限...
|
echo ℹ️ Requesting administrator privileges...
|
||||||
powershell -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0'"
|
powershell -Command "Start-Process -Verb RunAs -FilePath '%comspec%' -ArgumentList '/c cd /d %cd% && %~f0'"
|
||||||
exit /b
|
exit /b
|
||||||
) else (
|
) else (
|
||||||
@@ -33,30 +33,30 @@ if %errorLevel% == 0 (
|
|||||||
)
|
)
|
||||||
|
|
||||||
:RUN_BUILD
|
:RUN_BUILD
|
||||||
echo ℹ️ 啟動虛擬環境...
|
echo ℹ️ Starting virtual environment...
|
||||||
call venv\Scripts\activate.bat
|
call venv\Scripts\activate.bat
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo ❌ 啟動虛擬環境失敗
|
echo ❌ Failed to start virtual environment
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
:: 檢查並安裝缺失的依賴
|
:: Check and install missing dependencies
|
||||||
echo ℹ️ 檢查依賴...
|
echo ℹ️ Checking dependencies...
|
||||||
for /f "tokens=1" %%i in (requirements.txt) do (
|
for /f "tokens=1" %%i in (requirements.txt) do (
|
||||||
pip show %%i >nul 2>&1 || (
|
pip show %%i >nul 2>&1 || (
|
||||||
echo ℹ️ 安裝 %%i...
|
echo ℹ️ Installing %%i...
|
||||||
pip install %%i
|
pip install %%i
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
echo ℹ️ 開始構建...
|
echo ℹ️ Starting build...
|
||||||
python build.py
|
python build.py
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo ❌ 構建失敗
|
echo ❌ Build failed
|
||||||
pause
|
pause
|
||||||
exit /b 1
|
exit /b 1
|
||||||
)
|
)
|
||||||
|
|
||||||
echo ✅ 完成!
|
echo ✅ Completed!
|
||||||
pause
|
pause
|
||||||
26
build.py
26
build.py
@@ -8,7 +8,7 @@ import shutil
|
|||||||
from logo import print_logo
|
from logo import print_logo
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
# 忽略特定警告
|
# Ignore specific warnings
|
||||||
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
warnings.filterwarnings("ignore", category=SyntaxWarning)
|
||||||
|
|
||||||
class LoadingAnimation:
|
class LoadingAnimation:
|
||||||
@@ -50,21 +50,21 @@ def simulate_progress(message, duration=1.0, steps=20):
|
|||||||
progress_bar(i, steps, prefix="Progress:", length=40)
|
progress_bar(i, steps, prefix="Progress:", length=40)
|
||||||
|
|
||||||
def build():
|
def build():
|
||||||
# 清理屏幕
|
# Clean screen
|
||||||
os.system("cls" if platform.system().lower() == "windows" else "clear")
|
os.system("cls" if platform.system().lower() == "windows" else "clear")
|
||||||
|
|
||||||
# 顯示 logo
|
# Display logo
|
||||||
print_logo()
|
print_logo()
|
||||||
|
|
||||||
# 清理 PyInstaller 緩存
|
# Clean PyInstaller cache
|
||||||
print("\033[93m🧹 清理構建緩存...\033[0m")
|
print("\033[93m🧹 Cleaning build cache...\033[0m")
|
||||||
if os.path.exists('build'):
|
if os.path.exists('build'):
|
||||||
shutil.rmtree('build')
|
shutil.rmtree('build')
|
||||||
|
|
||||||
# 重新加載環境變量以確保獲取最新版本
|
# Reload environment variables to ensure getting the latest version
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
version = os.getenv('VERSION', '1.0.0')
|
version = os.getenv('VERSION', '1.0.0')
|
||||||
print(f"\033[93m📦 正在構建版本: v{version}\033[0m")
|
print(f"\033[93m📦 Building version: v{version}\033[0m")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
simulate_progress("Preparing build environment...", 0.5)
|
simulate_progress("Preparing build environment...", 0.5)
|
||||||
@@ -72,7 +72,7 @@ def build():
|
|||||||
loading = LoadingAnimation()
|
loading = LoadingAnimation()
|
||||||
loading.start("Building in progress")
|
loading.start("Building in progress")
|
||||||
|
|
||||||
# 根据系统类型设置输出名称
|
# Set output name based on system type
|
||||||
system = platform.system().lower()
|
system = platform.system().lower()
|
||||||
if system == "windows":
|
if system == "windows":
|
||||||
os_type = "windows"
|
os_type = "windows"
|
||||||
@@ -86,7 +86,7 @@ def build():
|
|||||||
|
|
||||||
output_name = f"CursorFreeVIP_{version}_{os_type}"
|
output_name = f"CursorFreeVIP_{version}_{os_type}"
|
||||||
|
|
||||||
# 构建命令
|
# Build command
|
||||||
build_command = f'pyinstaller --clean --noconfirm build.spec'
|
build_command = f'pyinstaller --clean --noconfirm build.spec'
|
||||||
output_path = os.path.join('dist', f'{output_name}{ext}')
|
output_path = os.path.join('dist', f'{output_name}{ext}')
|
||||||
|
|
||||||
@@ -95,16 +95,16 @@ def build():
|
|||||||
loading.stop()
|
loading.stop()
|
||||||
|
|
||||||
if os.path.exists(output_path):
|
if os.path.exists(output_path):
|
||||||
print(f"\n\033[92m✅ 構建完成!")
|
print(f"\n\033[92m✅ Build completed!")
|
||||||
print(f"📦 可執行文件位於: {output_path}\033[0m")
|
print(f"📦 Executable file located: {output_path}\033[0m")
|
||||||
else:
|
else:
|
||||||
print("\n\033[91m❌ 構建失敗:未找到輸出文件\033[0m")
|
print("\n\033[91m❌ Build failed: Output file not found\033[0m")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if loading:
|
if loading:
|
||||||
loading.stop()
|
loading.stop()
|
||||||
print(f"\n\033[91m❌ 構建過程出錯: {str(e)}\033[0m")
|
print(f"\n\033[91m❌ Build process error: {str(e)}\033[0m")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|||||||
20
build.sh
20
build.sh
@@ -8,7 +8,7 @@ NC='\033[0m' # No Color
|
|||||||
|
|
||||||
# 检查并安装必要的依赖
|
# 检查并安装必要的依赖
|
||||||
check_dependencies() {
|
check_dependencies() {
|
||||||
echo -e "${YELLOW}检查系统依赖...${NC}"
|
echo -e "${YELLOW}Checking system dependencies...${NC}"
|
||||||
|
|
||||||
# 检查是否为 Ubuntu/Debian
|
# 检查是否为 Ubuntu/Debian
|
||||||
if [ -f /etc/debian_version ]; then
|
if [ -f /etc/debian_version ]; then
|
||||||
@@ -16,42 +16,42 @@ check_dependencies() {
|
|||||||
PACKAGES="python3 python3-pip python3-venv"
|
PACKAGES="python3 python3-pip python3-venv"
|
||||||
for pkg in $PACKAGES; do
|
for pkg in $PACKAGES; do
|
||||||
if ! dpkg -l | grep -q "^ii $pkg "; then
|
if ! dpkg -l | grep -q "^ii $pkg "; then
|
||||||
echo -e "${YELLOW}安装 $pkg...${NC}"
|
echo -e "${YELLOW}Installing $pkg...${NC}"
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y $pkg
|
sudo apt-get install -y $pkg
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
echo -e "${RED}不支持的系统,请手动安装 python3, pip3 和 python3-venv${NC}"
|
echo -e "${RED}Unsupported system, please install python3, pip3 and python3-venv manually${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# 创建并激活虚拟环境
|
# 创建并激活虚拟环境
|
||||||
setup_venv() {
|
setup_venv() {
|
||||||
echo -e "${GREEN}正在创建虚拟环境...${NC}"
|
echo -e "${GREEN}Creating virtual environment...${NC}"
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
|
|
||||||
echo -e "${GREEN}启动虚拟环境...${NC}"
|
echo -e "${GREEN}Starting virtual environment...${NC}"
|
||||||
. ./venv/bin/activate || source ./venv/bin/activate
|
. ./venv/bin/activate || source ./venv/bin/activate
|
||||||
}
|
}
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
install_dependencies() {
|
install_dependencies() {
|
||||||
echo -e "${GREEN}安装依赖...${NC}"
|
echo -e "${GREEN}Installing dependencies...${NC}"
|
||||||
python3 -m pip install --upgrade pip
|
python3 -m pip install --upgrade pip
|
||||||
pip3 install -r requirements.txt
|
pip3 install -r requirements.txt
|
||||||
}
|
}
|
||||||
|
|
||||||
# 构建程序
|
# 构建程序
|
||||||
build_program() {
|
build_program() {
|
||||||
echo -e "${GREEN}开始构建...${NC}"
|
echo -e "${GREEN}Starting build...${NC}"
|
||||||
python3 build.py
|
python3 build.py
|
||||||
}
|
}
|
||||||
|
|
||||||
# 清理
|
# 清理
|
||||||
cleanup() {
|
cleanup() {
|
||||||
echo -e "${GREEN}清理虚拟环境...${NC}"
|
echo -e "${GREEN}Cleaning virtual environment...${NC}"
|
||||||
deactivate 2>/dev/null || true
|
deactivate 2>/dev/null || true
|
||||||
rm -rf venv
|
rm -rf venv
|
||||||
}
|
}
|
||||||
@@ -73,8 +73,8 @@ main() {
|
|||||||
# 清理
|
# 清理
|
||||||
cleanup
|
cleanup
|
||||||
|
|
||||||
echo -e "${GREEN}完成!${NC}"
|
echo -e "${GREEN}Completed!${NC}"
|
||||||
echo "按任意键退出..."
|
echo "Press any key to exit..."
|
||||||
# 使用兼容的方式读取输入
|
# 使用兼容的方式读取输入
|
||||||
if [ "$(uname)" = "Linux" ]; then
|
if [ "$(uname)" = "Linux" ]; then
|
||||||
read dummy
|
read dummy
|
||||||
|
|||||||
@@ -26,11 +26,32 @@ class CursorAuth:
|
|||||||
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"
|
||||||
)
|
)
|
||||||
|
elif os.name =='posix':
|
||||||
|
self.db_path = os.path.expanduser(
|
||||||
|
"~/.config/Cursor/User/globalStorage/state.vscdb"
|
||||||
|
)
|
||||||
else: # macOS
|
else: # 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 检查数据库文件是否存在
|
||||||
|
if not os.path.exists(self.db_path):
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_not_found', path=self.db_path)}{Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 检查文件权限
|
||||||
|
if not os.access(self.db_path, os.R_OK | os.W_OK):
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_permission_error')}{Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.conn = sqlite3.connect(self.db_path)
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
|
||||||
|
except sqlite3.Error as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_connection_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
def update_auth(self, email=None, access_token=None, refresh_token=None):
|
def update_auth(self, email=None, access_token=None, refresh_token=None):
|
||||||
conn = None
|
conn = None
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ class CursorRegistration:
|
|||||||
self.password = self._generate_password()
|
self.password = self._generate_password()
|
||||||
self.first_name = self._generate_name()
|
self.first_name = self._generate_name()
|
||||||
self.last_name = self._generate_name()
|
self.last_name = self._generate_name()
|
||||||
|
print(f"Password: {self.password}\n")
|
||||||
|
print(f"First Name: {self.first_name}\n")
|
||||||
|
print(f"Last Name: {self.last_name}\n")
|
||||||
|
|
||||||
def _generate_password(self, length=12):
|
def _generate_password(self, length=12):
|
||||||
"""Generate Random Password"""
|
"""Generate Random Password"""
|
||||||
@@ -76,10 +79,10 @@ class CursorRegistration:
|
|||||||
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
|
||||||
|
|
||||||
# 保存邮箱地址和浏览器实例
|
# 保存邮箱地址
|
||||||
self.email_address = email_address
|
self.email_address = email_address
|
||||||
self.email_tab = self.temp_email # 传递 NewTempEmail 实例而不是 page
|
print(f"Email Address: {self.email_address}\n")
|
||||||
self.controller = BrowserControl(self.temp_email.page, self.translator)
|
self.email_tab = self.temp_email # 传递 NewTempEmail 实例
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -150,6 +153,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
|
||||||
|
|||||||
265
cursor_register_manual.py
Normal file
265
cursor_register_manual.py
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
import os
|
||||||
|
from colorama import Fore, Style, init
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
from browser import BrowserManager
|
||||||
|
from control import BrowserControl
|
||||||
|
from cursor_auth import CursorAuth
|
||||||
|
from reset_machine_manual import MachineIDResetter
|
||||||
|
|
||||||
|
os.environ["PYTHONVERBOSE"] = "0"
|
||||||
|
os.environ["PYINSTALLER_VERBOSE"] = "0"
|
||||||
|
|
||||||
|
# Initialize colorama
|
||||||
|
init()
|
||||||
|
|
||||||
|
# Define emoji constants
|
||||||
|
EMOJI = {
|
||||||
|
'START': '🚀',
|
||||||
|
'FORM': '📝',
|
||||||
|
'VERIFY': '🔄',
|
||||||
|
'PASSWORD': '🔑',
|
||||||
|
'CODE': '📱',
|
||||||
|
'DONE': '✨',
|
||||||
|
'ERROR': '❌',
|
||||||
|
'WAIT': '⏳',
|
||||||
|
'SUCCESS': '✅',
|
||||||
|
'MAIL': '📧',
|
||||||
|
'KEY': '🔐',
|
||||||
|
'UPDATE': '🔄',
|
||||||
|
'INFO': 'ℹ️'
|
||||||
|
}
|
||||||
|
|
||||||
|
class CursorRegistration:
|
||||||
|
def __init__(self, translator=None):
|
||||||
|
self.translator = translator
|
||||||
|
# Set to display mode
|
||||||
|
os.environ['BROWSER_HEADLESS'] = 'False'
|
||||||
|
self.browser_manager = BrowserManager()
|
||||||
|
self.browser = None
|
||||||
|
self.controller = None
|
||||||
|
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
|
||||||
|
self.settings_url = "https://www.cursor.com/settings"
|
||||||
|
self.email_address = None
|
||||||
|
self.signup_tab = None
|
||||||
|
self.email_tab = None
|
||||||
|
|
||||||
|
# Account information
|
||||||
|
self.password = self._generate_password()
|
||||||
|
self.first_name = self._generate_name()
|
||||||
|
self.last_name = self._generate_name()
|
||||||
|
print(f"Password: {self.password}\n")
|
||||||
|
print(f"First Name: {self.first_name}\n")
|
||||||
|
print(f"Last Name: {self.last_name}\n")
|
||||||
|
|
||||||
|
def _generate_password(self, length=12):
|
||||||
|
"""Generate Random Password"""
|
||||||
|
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
|
||||||
|
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):
|
||||||
|
"""Setup Email"""
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.manual_email_input') if self.translator else 'Please enter your email address:'}")
|
||||||
|
self.email_address = input().strip()
|
||||||
|
|
||||||
|
if '@' not in self.email_address:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"Email Address: {self.email_address}\n")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_setup_failed', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_verification_code(self):
|
||||||
|
"""Manually Get Verification Code"""
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['CODE']} {self.translator.get('register.manual_code_input') if self.translator else 'Please enter the verification code:'}")
|
||||||
|
code = input().strip()
|
||||||
|
|
||||||
|
if not code.isdigit() or len(code) != 6:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_code') if self.translator else '无效的验证码'}{Style.RESET_ALL}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return code
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.code_input_failed', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def register_cursor(self):
|
||||||
|
"""Register Cursor"""
|
||||||
|
browser_tab = None
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Use new_signup.py directly for registration
|
||||||
|
from new_signup import main as new_signup_main
|
||||||
|
|
||||||
|
# Execute new registration process, passing translator
|
||||||
|
result, browser_tab = new_signup_main(
|
||||||
|
email=self.email_address,
|
||||||
|
password=self.password,
|
||||||
|
first_name=self.first_name,
|
||||||
|
last_name=self.last_name,
|
||||||
|
email_tab=None, # No email tab needed
|
||||||
|
controller=self, # Pass self instead of self.controller
|
||||||
|
translator=self.translator
|
||||||
|
)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
# Use the returned browser instance to get account information
|
||||||
|
self.signup_tab = browser_tab # Save browser instance
|
||||||
|
success = self._get_account_info()
|
||||||
|
|
||||||
|
# Close browser after getting information
|
||||||
|
if browser_tab:
|
||||||
|
try:
|
||||||
|
browser_tab.quit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
# Ensure browser is closed in any case
|
||||||
|
if browser_tab:
|
||||||
|
try:
|
||||||
|
browser_tab.quit()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _get_account_info(self):
|
||||||
|
"""Get Account Information and Token"""
|
||||||
|
try:
|
||||||
|
self.signup_tab.get(self.settings_url)
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
usage_selector = (
|
||||||
|
"css:div.col-span-2 > div > div > div > div > "
|
||||||
|
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
|
||||||
|
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
|
||||||
|
)
|
||||||
|
usage_ele = self.signup_tab.ele(usage_selector)
|
||||||
|
total_usage = "未知"
|
||||||
|
if usage_ele:
|
||||||
|
total_usage = usage_ele.text.split("/")[-1].strip()
|
||||||
|
|
||||||
|
print(f"Total Usage: {total_usage}\n")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
|
||||||
|
max_attempts = 30
|
||||||
|
retry_interval = 2
|
||||||
|
attempts = 0
|
||||||
|
|
||||||
|
while attempts < max_attempts:
|
||||||
|
try:
|
||||||
|
cookies = self.signup_tab.cookies()
|
||||||
|
for cookie in cookies:
|
||||||
|
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||||
|
token = cookie["value"].split("%3A%3A")[1]
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
|
||||||
|
self._save_account_info(token, total_usage)
|
||||||
|
return True
|
||||||
|
|
||||||
|
attempts += 1
|
||||||
|
if attempts < max_attempts:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
|
||||||
|
time.sleep(retry_interval)
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_max_attempts', max=max_attempts)}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_failed', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
attempts += 1
|
||||||
|
if attempts < max_attempts:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
|
||||||
|
time.sleep(retry_interval)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.account_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _save_account_info(self, token, total_usage):
|
||||||
|
"""Save Account Information to File"""
|
||||||
|
try:
|
||||||
|
# Update authentication information first
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
|
||||||
|
if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Reset machine ID
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
|
||||||
|
resetter = MachineIDResetter(self.translator) # Create instance with translator
|
||||||
|
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
|
||||||
|
raise Exception("Failed to reset machine ID")
|
||||||
|
|
||||||
|
# Save account information to file
|
||||||
|
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
|
||||||
|
f.write(f"\n{'='*50}\n")
|
||||||
|
f.write(f"Email: {self.email_address}\n")
|
||||||
|
f.write(f"Password: {self.password}\n")
|
||||||
|
f.write(f"Token: {token}\n")
|
||||||
|
f.write(f"Usage Limit: {total_usage}\n")
|
||||||
|
f.write(f"{'='*50}\n")
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""Start Registration Process"""
|
||||||
|
try:
|
||||||
|
if self.setup_email():
|
||||||
|
if self.register_cursor():
|
||||||
|
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
# Close email tab
|
||||||
|
if hasattr(self, 'temp_email'):
|
||||||
|
try:
|
||||||
|
self.temp_email.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
|
||||||
|
"""Convenient function to update Cursor authentication information"""
|
||||||
|
auth_manager = CursorAuth(translator=self.translator)
|
||||||
|
return auth_manager.update_auth(email, access_token, refresh_token)
|
||||||
|
|
||||||
|
def main(translator=None):
|
||||||
|
"""Main function to be called from main.py"""
|
||||||
|
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['START']} {translator.get('register.title')}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
registration = CursorRegistration(translator)
|
||||||
|
registration.start()
|
||||||
|
|
||||||
|
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
input(f"{EMOJI['INFO']} {translator.get('register.press_enter')}...")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from main import translator as main_translator
|
||||||
|
main(main_translator)
|
||||||
135
disable_auto_update.py
Normal file
135
disable_auto_update.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import platform
|
||||||
|
import shutil
|
||||||
|
from colorama import Fore, Style, init
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# Initialize colorama
|
||||||
|
init()
|
||||||
|
|
||||||
|
# Define emoji constants
|
||||||
|
EMOJI = {
|
||||||
|
"PROCESS": "🔄",
|
||||||
|
"SUCCESS": "✅",
|
||||||
|
"ERROR": "❌",
|
||||||
|
"INFO": "ℹ️",
|
||||||
|
"FOLDER": "📁",
|
||||||
|
"FILE": "📄",
|
||||||
|
"STOP": "🛑",
|
||||||
|
"CHECK": "✔️"
|
||||||
|
}
|
||||||
|
|
||||||
|
class AutoUpdateDisabler:
|
||||||
|
def __init__(self, translator=None):
|
||||||
|
self.translator = translator
|
||||||
|
self.system = platform.system()
|
||||||
|
self.updater_paths = {
|
||||||
|
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"),
|
||||||
|
"Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"),
|
||||||
|
"Linux": os.path.expanduser("~/.config/cursor-updater")
|
||||||
|
}
|
||||||
|
|
||||||
|
def _kill_cursor_processes(self):
|
||||||
|
"""End all Cursor processes"""
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('update.killing_processes') if self.translator else '正在结束 Cursor 进程...'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
if self.system == "Windows":
|
||||||
|
subprocess.run(['taskkill', '/F', '/IM', 'Cursor.exe', '/T'], capture_output=True)
|
||||||
|
else:
|
||||||
|
subprocess.run(['pkill', '-f', 'Cursor'], capture_output=True)
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.processes_killed') if self.translator else 'Cursor 进程已结束'}{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.kill_process_failed', error=str(e)) if self.translator else f'结束进程失败: {e}'}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _remove_updater_directory(self):
|
||||||
|
"""Delete updater directory"""
|
||||||
|
try:
|
||||||
|
updater_path = self.updater_paths.get(self.system)
|
||||||
|
if not updater_path:
|
||||||
|
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['FOLDER']} {self.translator.get('update.removing_directory') if self.translator else '正在删除更新程序目录...'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
if os.path.exists(updater_path):
|
||||||
|
if os.path.isdir(updater_path):
|
||||||
|
shutil.rmtree(updater_path)
|
||||||
|
else:
|
||||||
|
os.remove(updater_path)
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.directory_removed') if self.translator else '更新程序目录已删除'}{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.remove_directory_failed', error=str(e)) if self.translator else f'删除目录失败: {e}'}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _create_blocking_file(self):
|
||||||
|
"""Create blocking file"""
|
||||||
|
try:
|
||||||
|
updater_path = self.updater_paths.get(self.system)
|
||||||
|
if not updater_path:
|
||||||
|
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
|
||||||
|
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('update.creating_block_file') if self.translator else '正在创建阻止文件...'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Create empty file
|
||||||
|
open(updater_path, 'w').close()
|
||||||
|
|
||||||
|
# Set read-only attribute
|
||||||
|
if self.system == "Windows":
|
||||||
|
os.system(f'attrib +r "{updater_path}"')
|
||||||
|
else:
|
||||||
|
os.chmod(updater_path, 0o444) # Set to read-only
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('update.block_file_created') if self.translator else '阻止文件已创建'}{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.create_block_file_failed', error=str(e)) if self.translator else f'创建阻止文件失败: {e}'}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def disable_auto_update(self):
|
||||||
|
"""Disable auto update"""
|
||||||
|
try:
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('update.start_disable') if self.translator else '开始禁用自动更新...'}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# 1. End processes
|
||||||
|
if not self._kill_cursor_processes():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 2. Delete directory
|
||||||
|
if not self._remove_updater_directory():
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 3. Create blocking file
|
||||||
|
if not self._create_blocking_file():
|
||||||
|
return False
|
||||||
|
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['CHECK']} {self.translator.get('update.disable_success') if self.translator else '自动更新已禁用'}{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('update.disable_failed', error=str(e)) if self.translator else f'禁用自动更新失败: {e}'}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run(translator=None):
|
||||||
|
"""Convenient function for directly calling the disable function"""
|
||||||
|
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{EMOJI['STOP']} {translator.get('update.title') if translator else 'Disable Cursor Auto Update'}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
disabler = AutoUpdateDisabler(translator)
|
||||||
|
disabler.disable_auto_update()
|
||||||
|
|
||||||
|
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
input(f"{EMOJI['INFO']} {translator.get('update.press_enter') if translator else 'Press Enter to Continue...'}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from main import translator as main_translator
|
||||||
|
run(main_translator)
|
||||||
BIN
images/new_2025-02-27_10-42-44.png
Normal file
BIN
images/new_2025-02-27_10-42-44.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
BIN
images/paypal.png
Normal file
BIN
images/paypal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
images/pronew_2025-02-13_15-01-32.png
Normal file
BIN
images/pronew_2025-02-13_15-01-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
images/provi-code.jpg
Normal file
BIN
images/provi-code.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
106
locales/en.json
106
locales/en.json
@@ -2,15 +2,17 @@
|
|||||||
"menu": {
|
"menu": {
|
||||||
"title": "Available Options",
|
"title": "Available Options",
|
||||||
"exit": "Exit Program",
|
"exit": "Exit Program",
|
||||||
"reset": "Reset Machine Manual",
|
"reset": "Reset Machine ID",
|
||||||
"register": "Register Cursor",
|
"register": "Register New Cursor Account",
|
||||||
"quit": "Quit Cursor",
|
"register_manual": "Register Cursor with Custom Email",
|
||||||
"select_language": "Select Language",
|
"quit": "Close Cursor Application",
|
||||||
"input_choice": "Enter your choice ({choices})",
|
"select_language": "Change Language",
|
||||||
"invalid_choice": "Invalid choice. Please try again",
|
"input_choice": "Please enter your choice ({choices})",
|
||||||
"program_terminated": "Program terminated by user",
|
"invalid_choice": "Invalid selection. Please enter a number from {choices}",
|
||||||
"error_occurred": "An error occurred: {error}",
|
"program_terminated": "Program was terminated by user",
|
||||||
"press_enter": "Press Enter to Exit"
|
"error_occurred": "An error occurred: {error}. Please try again",
|
||||||
|
"press_enter": "Press Enter to Exit",
|
||||||
|
"disable_auto_update": "Disable Cursor Auto-Update"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"en": "English",
|
"en": "English",
|
||||||
@@ -62,25 +64,29 @@
|
|||||||
"patch_completed": "Patching getMachineId Completed",
|
"patch_completed": "Patching getMachineId Completed",
|
||||||
"patch_failed": "Patching getMachineId Failed: {error}",
|
"patch_failed": "Patching getMachineId Failed: {error}",
|
||||||
"version_check_passed": "Cursor Version Check Passed",
|
"version_check_passed": "Cursor Version Check Passed",
|
||||||
"file_modified": "File Modified"
|
"file_modified": "File Modified",
|
||||||
|
"version_less_than_0_45": "Cursor Version < 0.45.0, Skip Patching getMachineId",
|
||||||
|
"detecting_version": "Detecting Cursor Version",
|
||||||
|
"patching_getmachineid": "Patching getMachineId",
|
||||||
|
"version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"title": "Cursor Registration Tool",
|
"title": "Cursor Registration Tool",
|
||||||
"start": "Starting Registration Process",
|
"start": "Starting registration process...",
|
||||||
"handling_turnstile": "Handling Turnstile",
|
"handling_turnstile": "Processing security verification...",
|
||||||
"retry_verification": "Retry Verification",
|
"retry_verification": "Retrying verification...",
|
||||||
"detect_turnstile": "Detect Turnstile",
|
"detect_turnstile": "Checking security verification...",
|
||||||
"verification_success": "Verification Success",
|
"verification_success": "Security verification successful",
|
||||||
"starting_browser": "Starting Browser",
|
"starting_browser": "Opening browser...",
|
||||||
"form_success": "Form Success",
|
"form_success": "Form submitted successfully",
|
||||||
"browser_started": "Browser Started",
|
"browser_started": "Browser opened successfully",
|
||||||
"waiting_for_second_verification": "Waiting for Second Verification",
|
"waiting_for_second_verification": "Waiting for email verification...",
|
||||||
"waiting_for_verification_code": "Waiting for Verification Code",
|
"waiting_for_verification_code": "Waiting for verification code...",
|
||||||
"password_success": "Password Set Successfully",
|
"password_success": "Password set successfully",
|
||||||
"password_error": "Password Set Failed: {error}",
|
"password_error": "Could not set password: {error}. Please try again",
|
||||||
"waiting_for_page_load": "Waiting for Page Load",
|
"waiting_for_page_load": "Loading page...",
|
||||||
"first_verification_passed": "First Verification Passed",
|
"first_verification_passed": "Initial verification successful",
|
||||||
"mailbox": "Successfully Entered Mailbox",
|
"mailbox": "Successfully accessed email inbox",
|
||||||
"register_start": "Start Register",
|
"register_start": "Start Register",
|
||||||
"form_submitted": "Form Submitted, Start Verification...",
|
"form_submitted": "Form Submitted, Start Verification...",
|
||||||
"filling_form": "Fill Form",
|
"filling_form": "Fill Form",
|
||||||
@@ -123,7 +129,9 @@
|
|||||||
"get_email_address": "Get Email Address",
|
"get_email_address": "Get Email Address",
|
||||||
"update_cursor_auth_info": "Update Cursor Auth Info",
|
"update_cursor_auth_info": "Update Cursor Auth Info",
|
||||||
"register_process_error": "Register Process Error: {error}",
|
"register_process_error": "Register Process Error: {error}",
|
||||||
"setting_password": "Setting Password"
|
"setting_password": "Setting Password",
|
||||||
|
"manual_code_input": "Manual Code Input",
|
||||||
|
"manual_email_input": "Manual Email Input"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"title": "Cursor Auth Manager",
|
"title": "Cursor Auth Manager",
|
||||||
@@ -141,7 +149,10 @@
|
|||||||
"database_connection_closed": "Database Connection Closed",
|
"database_connection_closed": "Database Connection Closed",
|
||||||
"database_updated_successfully": "Database Updated Successfully",
|
"database_updated_successfully": "Database Updated Successfully",
|
||||||
"connected_to_database": "Connected to Database",
|
"connected_to_database": "Connected to Database",
|
||||||
"updating_pair": "Updating Key-Value Pair"
|
"updating_pair": "Updating Key-Value Pair",
|
||||||
|
"db_not_found": "Database file not found at: {path}",
|
||||||
|
"db_permission_error": "Cannot access database file. Please check permissions",
|
||||||
|
"db_connection_error": "Failed to connect to database: {error}"
|
||||||
},
|
},
|
||||||
"control": {
|
"control": {
|
||||||
"generate_email": "Generating New Email",
|
"generate_email": "Generating New Email",
|
||||||
@@ -178,7 +189,7 @@
|
|||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"starting_browser": "Starting Browser",
|
"starting_browser": "Starting Browser",
|
||||||
"visiting_site": "Visiting smailpro.com",
|
"visiting_site": "Visiting mail.tm",
|
||||||
"create_success": "Email Created Successfully",
|
"create_success": "Email Created Successfully",
|
||||||
"create_failed": "Failed to Create Email",
|
"create_failed": "Failed to Create Email",
|
||||||
"create_error": "Email Creation Error: {error}",
|
"create_error": "Email Creation Error: {error}",
|
||||||
@@ -192,6 +203,43 @@
|
|||||||
"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}"
|
||||||
|
},
|
||||||
|
"update": {
|
||||||
|
"title": "Disable Cursor Auto Update",
|
||||||
|
"disable_success": "Auto Update Disabled Successfully",
|
||||||
|
"disable_failed": "Disable Auto Update Failed: {error}",
|
||||||
|
"press_enter": "Press Enter to Exit",
|
||||||
|
"start_disable": "Start Disabling Auto Update",
|
||||||
|
"killing_processes": "Killing Processes",
|
||||||
|
"processes_killed": "Processes Killed",
|
||||||
|
"removing_directory": "Removing Directory",
|
||||||
|
"directory_removed": "Directory Removed",
|
||||||
|
"creating_block_file": "Creating Block File",
|
||||||
|
"block_file_created": "Block File Created"
|
||||||
|
},
|
||||||
|
"updater": {
|
||||||
|
"checking": "Checking for updates...",
|
||||||
|
"new_version_available": "New version available! (Current: {current}, Latest: {latest})",
|
||||||
|
"updating": "Updating to the latest version. The program will restart automatically.",
|
||||||
|
"up_to_date": "You are using the latest version.",
|
||||||
|
"check_failed": "Failed to check for updates: {error}",
|
||||||
|
"continue_anyway": "Continuing with current version..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,14 +3,16 @@
|
|||||||
"title": "可用选项",
|
"title": "可用选项",
|
||||||
"exit": "退出程序",
|
"exit": "退出程序",
|
||||||
"reset": "重置机器标识",
|
"reset": "重置机器标识",
|
||||||
"register": "注册 Cursor",
|
"register": "注册新 Cursor 账号",
|
||||||
"quit": "退出 Cursor",
|
"register_manual": "使用自定义邮箱注册",
|
||||||
"select_language": "选择语言",
|
"quit": "关闭 Cursor 应用",
|
||||||
"input_choice": "输入选择 ({choices})",
|
"select_language": "更改语言",
|
||||||
"invalid_choice": "无效选择,请重试",
|
"input_choice": "请输入您的选择 ({choices})",
|
||||||
"program_terminated": "程序被用户终止",
|
"invalid_choice": "选择无效,请输入 {choices} 范围内的数字",
|
||||||
"error_occurred": "发生错误: {error}",
|
"program_terminated": "程序已被用户终止",
|
||||||
"press_enter": "按回车键退出"
|
"error_occurred": "发生错误:{error},请重试",
|
||||||
|
"press_enter": "按回车键退出",
|
||||||
|
"disable_auto_update": "禁用 Cursor 自动更新"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"en": "English",
|
"en": "English",
|
||||||
@@ -62,29 +64,33 @@
|
|||||||
"patch_completed": "getMachineId修补完成",
|
"patch_completed": "getMachineId修补完成",
|
||||||
"patch_failed": "getMachineId修补失败: {error}",
|
"patch_failed": "getMachineId修补失败: {error}",
|
||||||
"version_check_passed": "Cursor版本检查通过",
|
"version_check_passed": "Cursor版本检查通过",
|
||||||
"file_modified": "文件已修改"
|
"file_modified": "文件已修改",
|
||||||
|
"version_less_than_0_45": "Cursor版本 < 0.45.0,跳过getMachineId修补",
|
||||||
|
"detecting_version": "检测Cursor版本",
|
||||||
|
"patching_getmachineid": "修补getMachineId",
|
||||||
|
"version_greater_than_0_45": "Cursor版本 >= 0.45.0,修补getMachineId"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"title": "Cursor 注册工具",
|
"title": "Cursor 注册工具",
|
||||||
"start": "开始注册流程",
|
"start": "正在启动注册流程...",
|
||||||
"browser_started": "浏览器已启动",
|
"browser_started": "浏览器已成功打开",
|
||||||
"password_success": "密码设置完成",
|
"password_success": "密码设置成功",
|
||||||
"password_error": "密码设置失败: {error}",
|
"password_error": "无法设置密码:{error},请重试",
|
||||||
"waiting_for_page_load": "等待页面加载",
|
"waiting_for_page_load": "页面加载中...",
|
||||||
"mailbox": "成功进入邮箱",
|
"mailbox": "已成功访问邮箱",
|
||||||
"waiting_for_second_verification": "等待第二阶段验证",
|
"waiting_for_second_verification": "等待邮箱验证...",
|
||||||
"waiting_for_verification_code": "等待验证码",
|
"waiting_for_verification_code": "等待验证码...",
|
||||||
"first_verification_passed": "第一阶段验证通过",
|
"first_verification_passed": "初始验证通过",
|
||||||
"register_start": "开始注册流程",
|
"register_start": "开始注册流程",
|
||||||
"form_submitted": "表单已提交,开始验证...",
|
"form_submitted": "表单已提交,开始验证...",
|
||||||
"filling_form": "填写注册信息",
|
"filling_form": "填写注册信息",
|
||||||
"visiting_url": "访问URL",
|
"visiting_url": "访问URL",
|
||||||
"basic_info": "基本信息提交完成",
|
"basic_info": "基本信息提交完成",
|
||||||
"handling_turnstile": "处理 Turnstile 验证",
|
"handling_turnstile": "正在处理安全验证...",
|
||||||
"retry_verification": "重试验证",
|
"retry_verification": "正在重试验证...",
|
||||||
"detect_turnstile": "检测 Turnstile 验证",
|
"detect_turnstile": "正在检查安全验证...",
|
||||||
"verification_success": "验证成功",
|
"verification_success": "安全验证通过",
|
||||||
"starting_browser": "启动浏览器",
|
"starting_browser": "正在打开浏览器...",
|
||||||
"form_success": "表单提交成功",
|
"form_success": "表单提交成功",
|
||||||
"handle_turnstile": "处理 Turnstile 验证",
|
"handle_turnstile": "处理 Turnstile 验证",
|
||||||
"no_turnstile": "未检测到 Turnstile 验证",
|
"no_turnstile": "未检测到 Turnstile 验证",
|
||||||
@@ -123,7 +129,9 @@
|
|||||||
"get_email_address": "获取邮箱地址",
|
"get_email_address": "获取邮箱地址",
|
||||||
"register_process_error": "注册流程错误: {error}",
|
"register_process_error": "注册流程错误: {error}",
|
||||||
"update_cursor_auth_info": "更新Cursor认证信息",
|
"update_cursor_auth_info": "更新Cursor认证信息",
|
||||||
"setting_password": "设置密码"
|
"setting_password": "设置密码",
|
||||||
|
"manual_code_input": "手动输入验证码",
|
||||||
|
"manual_email_input": "手动输入邮箱"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"title": "Cursor 认证管理器",
|
"title": "Cursor 认证管理器",
|
||||||
@@ -140,7 +148,10 @@
|
|||||||
"connected_to_database": "已连接到数据库",
|
"connected_to_database": "已连接到数据库",
|
||||||
"database_updated_successfully": "数据库更新成功",
|
"database_updated_successfully": "数据库更新成功",
|
||||||
"database_connection_closed": "数据库连接已关闭",
|
"database_connection_closed": "数据库连接已关闭",
|
||||||
"updating_pair": "更新键值对"
|
"updating_pair": "更新键值对",
|
||||||
|
"db_not_found": "未找到数据库文件:{path}",
|
||||||
|
"db_permission_error": "无法访问数据库文件,请检查权限",
|
||||||
|
"db_connection_error": "连接数据库失败:{error}"
|
||||||
},
|
},
|
||||||
"control": {
|
"control": {
|
||||||
"generate_email": "生成新邮箱",
|
"generate_email": "生成新邮箱",
|
||||||
@@ -175,7 +186,7 @@
|
|||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"starting_browser": "启动浏览器",
|
"starting_browser": "启动浏览器",
|
||||||
"visiting_site": "访问 smailpro.com",
|
"visiting_site": "访问 mail.tm",
|
||||||
"create_success": "邮箱创建成功",
|
"create_success": "邮箱创建成功",
|
||||||
"create_failed": "邮箱创建失败",
|
"create_failed": "邮箱创建失败",
|
||||||
"create_error": "邮箱创建错误: {error}",
|
"create_error": "邮箱创建错误: {error}",
|
||||||
@@ -189,6 +200,43 @@
|
|||||||
"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} 個可用域名"
|
||||||
|
},
|
||||||
|
"update": {
|
||||||
|
"title": "禁用 Cursor 自动更新",
|
||||||
|
"disable_success": "自动更新禁用成功",
|
||||||
|
"disable_failed": "禁用自动更新失败: {error}",
|
||||||
|
"press_enter": "按回车键退出",
|
||||||
|
"start_disable": "开始禁用自动更新",
|
||||||
|
"killing_processes": "杀死进程",
|
||||||
|
"processes_killed": "进程已杀死",
|
||||||
|
"removing_directory": "删除目录",
|
||||||
|
"directory_removed": "目录已删除",
|
||||||
|
"creating_block_file": "创建阻止文件",
|
||||||
|
"block_file_created": "阻止文件已创建"
|
||||||
|
},
|
||||||
|
"updater": {
|
||||||
|
"checking": "检查更新...",
|
||||||
|
"new_version_available": "有新版本可用! (当前版本: {current}, 最新版本: {latest})",
|
||||||
|
"updating": "正在更新到最新版本。程序将自动重启。",
|
||||||
|
"up_to_date": "您使用的是最新版本。",
|
||||||
|
"check_failed": "检查更新失败: {error}",
|
||||||
|
"continue_anyway": "继续使用当前版本..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
{
|
{
|
||||||
"menu": {
|
"menu": {
|
||||||
"title": "可用選項",
|
"title": "可用選項",
|
||||||
"exit": "退出程序",
|
"exit": "退出程式",
|
||||||
"reset": "重置機器標識",
|
"reset": "重置機器識別碼",
|
||||||
"register": "註冊 Cursor",
|
"register": "註冊新 Cursor 帳號",
|
||||||
"quit": "退出 Cursor",
|
"register_manual": "使用自訂郵箱註冊",
|
||||||
"select_language": "選擇語言",
|
"quit": "關閉 Cursor 應用程式",
|
||||||
"input_choice": "輸入選擇 ({choices})",
|
"select_language": "變更語言",
|
||||||
"invalid_choice": "無效選擇,請重試",
|
"input_choice": "請輸入您的選擇 ({choices})",
|
||||||
"program_terminated": "程序被用戶終止",
|
"invalid_choice": "選擇無效,請輸入 {choices} 範圍內的數字",
|
||||||
"error_occurred": "發生錯誤: {error}",
|
"program_terminated": "程式已被使用者終止",
|
||||||
"press_enter": "按回車鍵退出"
|
"error_occurred": "發生錯誤:{error},請重試",
|
||||||
|
"press_enter": "按返回鍵退出",
|
||||||
|
"disable_auto_update": "停用 Cursor 自動更新"
|
||||||
},
|
},
|
||||||
"languages": {
|
"languages": {
|
||||||
"en": "English",
|
"en": "English",
|
||||||
@@ -62,53 +64,38 @@
|
|||||||
"patch_completed": "getMachineId修補完成",
|
"patch_completed": "getMachineId修補完成",
|
||||||
"patch_failed": "getMachineId修補失敗: {error}",
|
"patch_failed": "getMachineId修補失敗: {error}",
|
||||||
"version_check_passed": "Cursor版本檢查通過",
|
"version_check_passed": "Cursor版本檢查通過",
|
||||||
"file_modified": "文件已修改"
|
"file_modified": "文件已修改",
|
||||||
|
"version_less_than_0_45": "Cursor版本 < 0.45.0,跳过getMachineId修补",
|
||||||
|
"detecting_version": "檢測Cursor版本",
|
||||||
|
"patching_getmachineid": "修補getMachineId",
|
||||||
|
"version_greater_than_0_45": "Cursor版本 >= 0.45.0,修補getMachineId"
|
||||||
},
|
},
|
||||||
"register": {
|
"register": {
|
||||||
"title": "Cursor 註冊工具",
|
"title": "Cursor 註冊工具",
|
||||||
"start": "開始註冊流程",
|
"start": "正在啟動註冊流程...",
|
||||||
"mailbox": "成功進入郵箱",
|
"handling_turnstile": "正在處理安全驗證...",
|
||||||
"browser_started": "瀏覽器已啟動",
|
"retry_verification": "正在重試驗證...",
|
||||||
"waiting_for_page_load": "等待頁面加載",
|
"detect_turnstile": "正在檢查安全驗證...",
|
||||||
"password_success": "密碼設置完成",
|
"verification_success": "安全驗證通過",
|
||||||
"password_error": "密碼設置失敗: {error}",
|
"starting_browser": "正在開啟瀏覽器...",
|
||||||
|
"form_success": "表單提交成功",
|
||||||
|
"browser_started": "瀏覽器已成功開啟",
|
||||||
|
"waiting_for_second_verification": "等待郵箱驗證...",
|
||||||
|
"waiting_for_verification_code": "等待驗證碼...",
|
||||||
|
"password_success": "密碼設定成功",
|
||||||
|
"password_error": "無法設定密碼:{error},請重試",
|
||||||
|
"waiting_for_page_load": "頁面載入中...",
|
||||||
|
"first_verification_passed": "初始驗證通過",
|
||||||
|
"mailbox": "已成功存取郵箱",
|
||||||
"visiting_url": "訪問URL",
|
"visiting_url": "訪問URL",
|
||||||
"first_verification_passed": "第一階段驗證通過",
|
|
||||||
"register_start": "開始註冊流程",
|
"register_start": "開始註冊流程",
|
||||||
"form_submitted": "表單已提交,開始驗證...",
|
"form_submitted": "表單已提交,開始驗證...",
|
||||||
"waiting_for_second_verification": "等待第二階段驗證",
|
|
||||||
"filling_form": "填寫註冊信息",
|
"filling_form": "填寫註冊信息",
|
||||||
"basic_info": "基本信息提交完成",
|
"basic_info": "基本信息提交完成",
|
||||||
"handle_turnstile": "處理 Turnstile 驗證",
|
"handle_turnstile": "處理 Turnstile 驗證",
|
||||||
"no_turnstile": "未檢測到 Turnstile 驗證",
|
"no_turnstile": "未檢測到 Turnstile 驗證",
|
||||||
"turnstile_passed": "驗證通過",
|
"turnstile_passed": "驗證通過",
|
||||||
"verification_start": "開始獲取驗證碼",
|
"verification_start": "開始獲取驗證碼",
|
||||||
"waiting_for_verification_code": "等待驗證碼",
|
|
||||||
"handling_turnstile": "處理 Turnstile 驗證",
|
|
||||||
"retry_verification": "重試驗證",
|
|
||||||
"detect_turnstile": "檢測 Turnstile 驗證",
|
|
||||||
"verification_success": "驗證成功",
|
|
||||||
"starting_browser": "啟動瀏覽器",
|
|
||||||
"form_success": "表單提交成功",
|
|
||||||
"verification_timeout": "獲取驗證碼超時",
|
|
||||||
"verification_not_found": "未找到驗證碼",
|
|
||||||
"try_get_code": "第 {attempt} 次嘗試獲取驗證碼 | 剩餘時間: {time}秒",
|
|
||||||
"get_account": "獲取賬戶信息",
|
|
||||||
"get_token": "獲取 Cursor Session Token",
|
|
||||||
"token_success": "Token 獲取成功",
|
|
||||||
"token_attempt": "第 {attempt} 次嘗試未獲取到 Token,{time}秒後重試",
|
|
||||||
"token_max_attempts": "已達到最大嘗試次數({max}),獲取 Token 失敗",
|
|
||||||
"token_failed": "獲取 Token 失敗: {error}",
|
|
||||||
"account_error": "獲取賬戶信息失敗: {error}",
|
|
||||||
"press_enter": "按回車鍵退出",
|
|
||||||
"browser_start": "正在啟動瀏覽器",
|
|
||||||
"open_mailbox": "正在打開郵箱頁面",
|
|
||||||
"email_error": "獲取郵箱地址失敗",
|
|
||||||
"setup_error": "郵箱設置出錯: {error}",
|
|
||||||
"start_getting_verification_code": "開始獲取驗證碼,將在60秒內嘗試...",
|
|
||||||
"get_verification_code_timeout": "獲取驗證碼超時",
|
|
||||||
"get_verification_code_success": "成功獲取驗證碼",
|
|
||||||
"try_get_verification_code": "第 {attempt} 次嘗試未獲取到驗證碼,剩餘時間: {remaining_time}秒",
|
|
||||||
"verification_code_filled": "驗證碼填寫完成",
|
"verification_code_filled": "驗證碼填寫完成",
|
||||||
"login_success_and_jump_to_settings_page": "成功登錄並跳轉到設置頁面",
|
"login_success_and_jump_to_settings_page": "成功登錄並跳轉到設置頁面",
|
||||||
"detect_login_page": "檢測到登錄頁面,開始登錄...",
|
"detect_login_page": "檢測到登錄頁面,開始登錄...",
|
||||||
@@ -123,7 +110,9 @@
|
|||||||
"get_email_address": "獲取郵箱地址",
|
"get_email_address": "獲取郵箱地址",
|
||||||
"register_process_error": "註冊流程錯誤: {error}",
|
"register_process_error": "註冊流程錯誤: {error}",
|
||||||
"update_cursor_auth_info": "更新Cursor認證信息",
|
"update_cursor_auth_info": "更新Cursor認證信息",
|
||||||
"setting_password": "設置密碼"
|
"setting_password": "設置密碼",
|
||||||
|
"manual_code_input": "手動輸入驗證碼",
|
||||||
|
"manual_email_input": "手動輸入郵箱地址"
|
||||||
},
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"title": "Cursor 認證管理器",
|
"title": "Cursor 認證管理器",
|
||||||
@@ -140,7 +129,10 @@
|
|||||||
"connected_to_database": "已連接到數據庫",
|
"connected_to_database": "已連接到數據庫",
|
||||||
"database_updated_successfully": "數據庫更新成功",
|
"database_updated_successfully": "數據庫更新成功",
|
||||||
"database_connection_closed": "數據庫連接已關閉",
|
"database_connection_closed": "數據庫連接已關閉",
|
||||||
"updating_pair": "更新鍵值對"
|
"updating_pair": "更新鍵值對",
|
||||||
|
"db_not_found": "未找到數據庫文件:{path}",
|
||||||
|
"db_permission_error": "無法訪問數據庫文件,請檢查權限",
|
||||||
|
"db_connection_error": "連接數據庫失敗:{error}"
|
||||||
},
|
},
|
||||||
"control": {
|
"control": {
|
||||||
"generate_email": "生成新郵箱",
|
"generate_email": "生成新郵箱",
|
||||||
@@ -175,7 +167,7 @@
|
|||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"starting_browser": "啟動瀏覽器",
|
"starting_browser": "啟動瀏覽器",
|
||||||
"visiting_site": "訪問 smailpro.com",
|
"visiting_site": "訪問 mail.tm",
|
||||||
"create_success": "郵箱創建成功",
|
"create_success": "郵箱創建成功",
|
||||||
"create_failed": "郵箱創建失敗",
|
"create_failed": "郵箱創建失敗",
|
||||||
"create_error": "郵箱創建錯誤: {error}",
|
"create_error": "郵箱創建錯誤: {error}",
|
||||||
@@ -189,7 +181,43 @@
|
|||||||
"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} 個可用域名"
|
||||||
|
},
|
||||||
|
"update": {
|
||||||
|
"title": "禁用 Cursor 自动更新",
|
||||||
|
"disable_success": "自動更新禁用成功",
|
||||||
|
"disable_failed": "禁用自動更新失敗: {error}",
|
||||||
|
"press_enter": "按回車鍵退出",
|
||||||
|
"start_disable": "開始禁用自動更新",
|
||||||
|
"killing_processes": "殺死進程",
|
||||||
|
"processes_killed": "進程已殺死",
|
||||||
|
"removing_directory": "刪除目錄",
|
||||||
|
"directory_removed": "目錄已刪除",
|
||||||
|
"creating_block_file": "創建阻止文件",
|
||||||
|
"block_file_created": "阻止文件已創建"
|
||||||
|
},
|
||||||
|
"updater": {
|
||||||
|
"checking": "檢查更新...",
|
||||||
|
"new_version_available": "有新版本可用! (當前版本: {current}, 最新版本: {latest})",
|
||||||
|
"updating": "正在更新到最新版本。程序將自動重啟。",
|
||||||
|
"up_to_date": "您使用的是最新版本。",
|
||||||
|
"check_failed": "檢查更新失敗: {error}",
|
||||||
|
"continue_anyway": "繼續使用當前版本..."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
14
logo.py
14
logo.py
@@ -2,17 +2,17 @@ from colorama import Fore, Style, init
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# 獲取當前腳本所在目錄
|
# Get the current script directory
|
||||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
# 構建.env文件的完整路徑
|
# Build the full path to the .env file
|
||||||
env_path = os.path.join(current_dir, '.env')
|
env_path = os.path.join(current_dir, '.env')
|
||||||
|
|
||||||
# 加載環境變量,指定.env文件路徑
|
# Load environment variables, specifying the .env file path
|
||||||
load_dotenv(env_path)
|
load_dotenv(env_path)
|
||||||
# 獲取版本號,如果未找到則使用默認值
|
# Get the version number, using the default value if not found
|
||||||
version = os.getenv('VERSION', '1.0.0')
|
version = os.getenv('VERSION', '1.0.0')
|
||||||
|
|
||||||
# 初始化 colorama
|
# Initialize colorama
|
||||||
init()
|
init()
|
||||||
|
|
||||||
CURSOR_LOGO = f"""
|
CURSOR_LOGO = f"""
|
||||||
@@ -27,8 +27,10 @@ CURSOR_LOGO = f"""
|
|||||||
Pro Version Activator v{version}
|
Pro Version Activator v{version}
|
||||||
{Fore.GREEN}
|
{Fore.GREEN}
|
||||||
Author: Pin Studios | yeongpin
|
Author: Pin Studios | yeongpin
|
||||||
|
|
||||||
|
Github: https://github.com/yeongpin/cursor-free-vip
|
||||||
{Fore.RED}
|
{Fore.RED}
|
||||||
Press 4 to change language | 按下 4 键切换语言
|
Press 5 to change language | 按下 5 键切换语言
|
||||||
{Style.RESET_ALL}
|
{Style.RESET_ALL}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
229
main.py
229
main.py
@@ -3,8 +3,18 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
from logo import print_logo
|
from logo import print_logo, version
|
||||||
from colorama import Fore, Style, init
|
from colorama import Fore, Style, init
|
||||||
|
import locale
|
||||||
|
import platform
|
||||||
|
import requests
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# 只在 Windows 系统上导入 windll
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
import ctypes
|
||||||
|
# 只在 Windows 上导入 windll
|
||||||
|
from ctypes import windll
|
||||||
|
|
||||||
# 初始化colorama
|
# 初始化colorama
|
||||||
init()
|
init()
|
||||||
@@ -19,48 +29,144 @@ EMOJI = {
|
|||||||
"RESET": "🔄",
|
"RESET": "🔄",
|
||||||
"MENU": "📋",
|
"MENU": "📋",
|
||||||
"ARROW": "➜",
|
"ARROW": "➜",
|
||||||
"LANG": "🌐"
|
"LANG": "🌐",
|
||||||
|
"UPDATE": "🔄"
|
||||||
}
|
}
|
||||||
|
|
||||||
class Translator:
|
class Translator:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.current_language = 'zh_tw' # 默认语言
|
|
||||||
self.translations = {}
|
self.translations = {}
|
||||||
|
self.current_language = self.detect_system_language() # 使用正确的方法名
|
||||||
|
self.fallback_language = 'en' # Fallback language if translation is missing
|
||||||
self.load_translations()
|
self.load_translations()
|
||||||
|
|
||||||
def load_translations(self):
|
def detect_system_language(self):
|
||||||
"""加载所有可用的翻译"""
|
"""Detect system language and return corresponding language code"""
|
||||||
locales_dir = os.path.join(os.path.dirname(__file__), 'locales')
|
try:
|
||||||
if hasattr(sys, '_MEIPASS'):
|
system = platform.system()
|
||||||
locales_dir = os.path.join(sys._MEIPASS, 'locales')
|
|
||||||
|
|
||||||
for file in os.listdir(locales_dir):
|
if system == 'Windows':
|
||||||
if file.endswith('.json'):
|
return self._detect_windows_language()
|
||||||
lang_code = file[:-5] # 移除 .json
|
else:
|
||||||
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
|
return self._detect_unix_language()
|
||||||
self.translations[lang_code] = json.load(f)
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} Failed to detect system language: {e}{Style.RESET_ALL}")
|
||||||
|
return 'en'
|
||||||
|
|
||||||
|
def _detect_windows_language(self):
|
||||||
|
"""Detect language on Windows systems"""
|
||||||
|
try:
|
||||||
|
# 确保我们在 Windows 上
|
||||||
|
if platform.system() != 'Windows':
|
||||||
|
return 'en'
|
||||||
|
|
||||||
|
# 获取键盘布局
|
||||||
|
user32 = ctypes.windll.user32
|
||||||
|
hwnd = user32.GetForegroundWindow()
|
||||||
|
threadid = user32.GetWindowThreadProcessId(hwnd, 0)
|
||||||
|
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
|
||||||
|
|
||||||
|
# Map language ID to our language codes
|
||||||
|
language_map = {
|
||||||
|
0x0409: 'en', # English
|
||||||
|
0x0404: 'zh_tw', # Traditional Chinese
|
||||||
|
0x0804: 'zh_cn', # Simplified Chinese
|
||||||
|
}
|
||||||
|
|
||||||
|
return language_map.get(layout_id, 'en')
|
||||||
|
except:
|
||||||
|
return self._detect_unix_language()
|
||||||
|
|
||||||
|
def _detect_unix_language(self):
|
||||||
|
"""Detect language on Unix-like systems (Linux, macOS)"""
|
||||||
|
try:
|
||||||
|
# Get the system locale
|
||||||
|
system_locale = locale.getdefaultlocale()[0]
|
||||||
|
if not system_locale:
|
||||||
|
return 'en'
|
||||||
|
|
||||||
|
system_locale = system_locale.lower()
|
||||||
|
|
||||||
|
# Map locale to our language codes
|
||||||
|
if system_locale.startswith('zh_tw') or system_locale.startswith('zh_hk'):
|
||||||
|
return 'zh_tw'
|
||||||
|
elif system_locale.startswith('zh_cn'):
|
||||||
|
return 'zh_cn'
|
||||||
|
elif system_locale.startswith('en'):
|
||||||
|
return 'en'
|
||||||
|
|
||||||
|
# Try to get language from LANG environment variable as fallback
|
||||||
|
env_lang = os.getenv('LANG', '').lower()
|
||||||
|
if 'tw' in env_lang or 'hk' in env_lang:
|
||||||
|
return 'zh_tw'
|
||||||
|
elif 'cn' in env_lang:
|
||||||
|
return 'zh_cn'
|
||||||
|
|
||||||
|
return 'en'
|
||||||
|
except:
|
||||||
|
return 'en'
|
||||||
|
|
||||||
|
def load_translations(self):
|
||||||
|
"""Load all available translations"""
|
||||||
|
try:
|
||||||
|
locales_dir = os.path.join(os.path.dirname(__file__), 'locales')
|
||||||
|
if hasattr(sys, '_MEIPASS'):
|
||||||
|
locales_dir = os.path.join(sys._MEIPASS, 'locales')
|
||||||
|
|
||||||
|
if not os.path.exists(locales_dir):
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Locales directory not found{Style.RESET_ALL}")
|
||||||
|
return
|
||||||
|
|
||||||
|
for file in os.listdir(locales_dir):
|
||||||
|
if file.endswith('.json'):
|
||||||
|
lang_code = file[:-5] # Remove .json
|
||||||
|
try:
|
||||||
|
with open(os.path.join(locales_dir, file), 'r', encoding='utf-8') as f:
|
||||||
|
self.translations[lang_code] = json.load(f)
|
||||||
|
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Error loading {file}: {e}{Style.RESET_ALL}")
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to load translations: {e}{Style.RESET_ALL}")
|
||||||
|
|
||||||
def get(self, key, **kwargs):
|
def get(self, key, **kwargs):
|
||||||
"""获取翻译文本"""
|
"""Get translated text with fallback support"""
|
||||||
|
try:
|
||||||
|
# Try current language
|
||||||
|
result = self._get_translation(self.current_language, key)
|
||||||
|
if result == key and self.current_language != self.fallback_language:
|
||||||
|
# Try fallback language if translation not found
|
||||||
|
result = self._get_translation(self.fallback_language, key)
|
||||||
|
return result.format(**kwargs) if kwargs else result
|
||||||
|
except Exception:
|
||||||
|
return key
|
||||||
|
|
||||||
|
def _get_translation(self, lang_code, key):
|
||||||
|
"""Get translation for a specific language"""
|
||||||
try:
|
try:
|
||||||
keys = key.split('.')
|
keys = key.split('.')
|
||||||
value = self.translations.get(self.current_language, {})
|
value = self.translations.get(lang_code, {})
|
||||||
for k in keys:
|
for k in keys:
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
value = value.get(k, key)
|
value = value.get(k, key)
|
||||||
else:
|
else:
|
||||||
return key # 如果中間值不是字典,返回原始key
|
return key
|
||||||
return value.format(**kwargs) if kwargs else value
|
return value
|
||||||
except Exception:
|
except Exception:
|
||||||
return key # 出現任何錯誤時返回原始key
|
return key
|
||||||
|
|
||||||
def set_language(self, lang_code):
|
def set_language(self, lang_code):
|
||||||
"""设置当前语言"""
|
"""Set current language with validation"""
|
||||||
if lang_code in self.translations:
|
if lang_code in self.translations:
|
||||||
self.current_language = lang_code
|
self.current_language = lang_code
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_available_languages(self):
|
||||||
|
"""Get list of available languages"""
|
||||||
|
return list(self.translations.keys())
|
||||||
|
|
||||||
# 创建翻译器实例
|
# 创建翻译器实例
|
||||||
translator = Translator()
|
translator = Translator()
|
||||||
|
|
||||||
@@ -71,38 +177,72 @@ def print_menu():
|
|||||||
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
|
||||||
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
|
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
|
||||||
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
|
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
|
||||||
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
|
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
|
||||||
print(f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
|
print(f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
|
||||||
|
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
|
||||||
|
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}")
|
||||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||||
|
|
||||||
def select_language():
|
def select_language():
|
||||||
"""语言选择菜单"""
|
"""Language selection menu"""
|
||||||
print(f"\n{Fore.CYAN}{EMOJI['LANG']} {translator.get('menu.select_language')}:{Style.RESET_ALL}")
|
print(f"\n{Fore.CYAN}{EMOJI['LANG']} {translator.get('menu.select_language')}:{Style.RESET_ALL}")
|
||||||
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}{'─' * 40}{Style.RESET_ALL}")
|
||||||
|
|
||||||
languages = translator.get('languages')
|
languages = translator.get_available_languages()
|
||||||
for i, (code, name) in enumerate(languages.items()):
|
for i, lang in enumerate(languages):
|
||||||
print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {name}")
|
lang_name = translator.get(f"languages.{lang}")
|
||||||
|
print(f"{Fore.GREEN}{i}{Style.RESET_ALL}. {lang_name}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-' + str(len(languages)-1))}: {Style.RESET_ALL}")
|
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{len(languages)-1}')}: {Style.RESET_ALL}")
|
||||||
if choice.isdigit() and 0 <= int(choice) < len(languages):
|
if choice.isdigit() and 0 <= int(choice) < len(languages):
|
||||||
lang_code = list(languages.keys())[int(choice)]
|
translator.set_language(languages[int(choice)])
|
||||||
translator.set_language(lang_code)
|
|
||||||
return True
|
return True
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError):
|
||||||
pass
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||||
|
return False
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
|
||||||
return False
|
def check_latest_version():
|
||||||
|
"""Check if current version matches the latest release version"""
|
||||||
|
try:
|
||||||
|
print(f"\n{Fore.CYAN}{EMOJI['UPDATE']} {translator.get('updater.checking')}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# Get latest version from GitHub API with timeout
|
||||||
|
response = requests.get("https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest", timeout=5)
|
||||||
|
latest_version = response.json()["tag_name"].lstrip('v')
|
||||||
|
|
||||||
|
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}")
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
update_command = 'curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh'
|
||||||
|
subprocess.Popen(update_command, shell=True)
|
||||||
|
|
||||||
|
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.updating')}{Style.RESET_ALL}")
|
||||||
|
sys.exit(0)
|
||||||
|
else:
|
||||||
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
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 # Continue with the program instead of blocking
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
print_logo()
|
print_logo()
|
||||||
|
check_latest_version() # Add version check before showing menu
|
||||||
print_menu()
|
print_menu()
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-4')}: {Style.RESET_ALL}")
|
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-6')}: {Style.RESET_ALL}")
|
||||||
|
|
||||||
if choice == "0":
|
if choice == "0":
|
||||||
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
|
||||||
@@ -111,19 +251,27 @@ def main():
|
|||||||
elif choice == "1":
|
elif choice == "1":
|
||||||
import reset_machine_manual
|
import reset_machine_manual
|
||||||
reset_machine_manual.run(translator)
|
reset_machine_manual.run(translator)
|
||||||
break
|
print_menu()
|
||||||
elif choice == "2":
|
elif choice == "2":
|
||||||
import cursor_register
|
import cursor_register
|
||||||
cursor_register.main(translator)
|
cursor_register.main(translator)
|
||||||
break
|
print_menu()
|
||||||
elif choice == "3":
|
elif choice == "3":
|
||||||
|
import cursor_register_manual
|
||||||
|
cursor_register_manual.main(translator)
|
||||||
|
print_menu()
|
||||||
|
elif choice == "4":
|
||||||
import quit_cursor
|
import quit_cursor
|
||||||
quit_cursor.quit_cursor(translator)
|
quit_cursor.quit_cursor(translator)
|
||||||
break
|
print_menu()
|
||||||
elif choice == "4":
|
elif choice == "5":
|
||||||
if select_language():
|
if select_language():
|
||||||
print_menu()
|
print_menu()
|
||||||
continue
|
continue
|
||||||
|
elif choice == "6":
|
||||||
|
import disable_auto_update
|
||||||
|
disable_auto_update.run(translator)
|
||||||
|
print_menu()
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
|
||||||
print_menu()
|
print_menu()
|
||||||
@@ -134,10 +282,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()
|
||||||
238
new_signup.py
238
new_signup.py
@@ -243,7 +243,7 @@ def fill_password(page, password, translator=None):
|
|||||||
if translator:
|
if translator:
|
||||||
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password')}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password')}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print("\n正在设置密码...")
|
print(f"\n{translator.get('register.setting_password')}")
|
||||||
password_input = page.ele("@name=password")
|
password_input = page.ele("@name=password")
|
||||||
if password_input:
|
if password_input:
|
||||||
password_input.input(password)
|
password_input.input(password)
|
||||||
@@ -257,14 +257,14 @@ def fill_password(page, password, translator=None):
|
|||||||
if translator:
|
if translator:
|
||||||
print(f"{Fore.GREEN}✅ {translator.get('register.password_success')}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}✅ {translator.get('register.password_success')}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"密码设置完成: {password}")
|
print(f"{translator.get('register.password_success')}: {password}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if translator:
|
if translator:
|
||||||
print(f"{Fore.RED}❌ {translator.get('register.password_error', error=str(e))}{Style.RESET_ALL}")
|
print(f"{Fore.RED}❌ {translator.get('register.password_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"设置密码时出错: {e}")
|
print(f"{translator.get('register.password_error')}: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def handle_verification_code(browser_tab, email_tab, controller, email, password, translator=None):
|
def handle_verification_code(browser_tab, email_tab, controller, email, password, translator=None):
|
||||||
@@ -273,25 +273,128 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
|
|||||||
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:
|
else:
|
||||||
print("\n等待并获取验证码...")
|
print(f"\n{translator.get('register.waiting_for_verification_code')}")
|
||||||
|
|
||||||
# 添加调试信息
|
# 检查是否使用手动输入验证码
|
||||||
print(f"\n{Fore.CYAN}DEBUG: email_tab exists: {email_tab is not None}{Style.RESET_ALL}")
|
if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式
|
||||||
|
verification_code = controller.get_verification_code()
|
||||||
time.sleep(5) # 等待验证码邮件
|
|
||||||
|
|
||||||
# 使用已有的 email_tab 刷新邮箱
|
|
||||||
email_tab.refresh_inbox()
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
# 检查邮箱是否有验证码邮件
|
|
||||||
if email_tab.check_for_cursor_email():
|
|
||||||
verification_code = email_tab.get_verification_code()
|
|
||||||
if verification_code:
|
if verification_code:
|
||||||
# 在注册页面填写验证码
|
# 在注册页面填写验证码
|
||||||
for i, digit in enumerate(verification_code):
|
for i, digit in enumerate(verification_code):
|
||||||
browser_tab.ele(f"@data-index={i}").input(digit)
|
browser_tab.ele(f"@data-index={i}").input(digit)
|
||||||
time.sleep(random.uniform(0.1, 0.3))
|
time.sleep(random.uniform(0.1, 0.3))
|
||||||
|
|
||||||
|
print(f"{translator.get('register.verification_success')}")
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# 处理最后一次 Turnstile 验证
|
||||||
|
if handle_turnstile(browser_tab, translator):
|
||||||
|
if translator:
|
||||||
|
print(f"{translator.get('register.verification_success')}")
|
||||||
|
else:
|
||||||
|
print(f"{translator.get('register.verification_success')}")
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# 访问设置页面
|
||||||
|
print(f"{translator.get('register.visiting_url')}: https://www.cursor.com/settings")
|
||||||
|
browser_tab.get("https://www.cursor.com/settings")
|
||||||
|
time.sleep(3) # 等待页面加载
|
||||||
|
return True, browser_tab
|
||||||
|
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# 自动获取验证码逻辑
|
||||||
|
elif email_tab:
|
||||||
|
print(f"{translator.get('register.waiting_for_verification_code')}")
|
||||||
|
time.sleep(5) # 等待验证码邮件
|
||||||
|
|
||||||
|
# 使用已有的 email_tab 刷新邮箱
|
||||||
|
email_tab.refresh_inbox()
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# 检查邮箱是否有验证码邮件
|
||||||
|
if email_tab.check_for_cursor_email():
|
||||||
|
verification_code = email_tab.get_verification_code()
|
||||||
|
if verification_code:
|
||||||
|
# 在注册页面填写验证码
|
||||||
|
for i, digit in enumerate(verification_code):
|
||||||
|
browser_tab.ele(f"@data-index={i}").input(digit)
|
||||||
|
time.sleep(random.uniform(0.1, 0.3))
|
||||||
|
if translator:
|
||||||
|
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print("验证码填写完成")
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# 处理最后一次 Turnstile 验证
|
||||||
|
if handle_turnstile(browser_tab, translator):
|
||||||
|
if translator:
|
||||||
|
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print("最后一次验证通过!")
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# 访问设置页面
|
||||||
|
if translator:
|
||||||
|
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")
|
||||||
|
time.sleep(3) # 等待页面加载
|
||||||
|
return True, browser_tab
|
||||||
|
|
||||||
|
else:
|
||||||
|
if translator:
|
||||||
|
print(f"{Fore.RED}❌ {translator.get('register.verification_failed')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print("最后一次验证失败")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# 获取验证码,设置超时
|
||||||
|
verification_code = None
|
||||||
|
max_attempts = 20
|
||||||
|
retry_interval = 10
|
||||||
|
start_time = time.time()
|
||||||
|
timeout = 160
|
||||||
|
|
||||||
|
if translator:
|
||||||
|
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print("开始获取验证码...")
|
||||||
|
|
||||||
|
for attempt in range(max_attempts):
|
||||||
|
# 检查是否超时
|
||||||
|
if time.time() - start_time > timeout:
|
||||||
|
if translator:
|
||||||
|
print(f"{Fore.RED}❌ {translator.get('register.verification_timeout')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print("获取验证码超时...")
|
||||||
|
break
|
||||||
|
|
||||||
|
verification_code = controller.get_verification_code()
|
||||||
|
if verification_code:
|
||||||
|
if translator:
|
||||||
|
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"成功获取验证码: {verification_code}")
|
||||||
|
break
|
||||||
|
|
||||||
|
remaining_time = int(timeout - (time.time() - start_time))
|
||||||
|
if translator:
|
||||||
|
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"第 {attempt + 1} 次尝试获取验证码,剩余时间: {remaining_time}秒...")
|
||||||
|
|
||||||
|
# 刷新邮箱
|
||||||
|
email_tab.refresh_inbox()
|
||||||
|
time.sleep(retry_interval)
|
||||||
|
|
||||||
|
if verification_code:
|
||||||
|
# 在注册页面填写验证码
|
||||||
|
for i, digit in enumerate(verification_code):
|
||||||
|
browser_tab.ele(f"@data-index={i}").input(digit)
|
||||||
|
time.sleep(random.uniform(0.1, 0.3))
|
||||||
|
|
||||||
if translator:
|
if translator:
|
||||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
@@ -306,105 +409,32 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
|
|||||||
print("最后一次验证通过!")
|
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:
|
else:
|
||||||
print("访问设置页面...")
|
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
|
|
||||||
else:
|
|
||||||
print("最后一次验证失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return False
|
# 直接返回成功,让 cursor_register.py 处理账户信息获取
|
||||||
|
return True, browser_tab
|
||||||
# 获取验证码,设置超时
|
|
||||||
verification_code = None
|
|
||||||
max_attempts = 20
|
|
||||||
retry_interval = 10
|
|
||||||
start_time = time.time()
|
|
||||||
timeout = 160
|
|
||||||
|
|
||||||
if translator:
|
|
||||||
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
|
|
||||||
else:
|
|
||||||
print("开始获取验证码...")
|
|
||||||
|
|
||||||
for attempt in range(max_attempts):
|
|
||||||
# 检查是否超时
|
|
||||||
if time.time() - start_time > timeout:
|
|
||||||
if translator:
|
|
||||||
print(f"{Fore.RED}❌ {translator.get('register.verification_timeout')}{Style.RESET_ALL}")
|
|
||||||
else:
|
else:
|
||||||
print("获取验证码超时...")
|
if translator:
|
||||||
break
|
print(f"{Fore.RED}❌ {translator.get('register.verification_failed')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print("最后一次验证失败")
|
||||||
|
return False, None
|
||||||
|
|
||||||
verification_code = controller.get_verification_code()
|
return False, None
|
||||||
if verification_code:
|
|
||||||
if translator:
|
|
||||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
|
||||||
else:
|
|
||||||
print(f"成功获取验证码: {verification_code}")
|
|
||||||
break
|
|
||||||
|
|
||||||
remaining_time = int(timeout - (time.time() - start_time))
|
|
||||||
if translator:
|
|
||||||
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
|
|
||||||
else:
|
|
||||||
print(f"第 {attempt + 1} 次尝试获取验证码,剩余时间: {remaining_time}秒...")
|
|
||||||
|
|
||||||
# 刷新邮箱
|
|
||||||
email_tab.refresh_inbox()
|
|
||||||
time.sleep(retry_interval)
|
|
||||||
|
|
||||||
if verification_code:
|
|
||||||
# 在注册页面填写验证码
|
|
||||||
for i, digit in enumerate(verification_code):
|
|
||||||
browser_tab.ele(f"@data-index={i}").input(digit)
|
|
||||||
time.sleep(random.uniform(0.1, 0.3))
|
|
||||||
|
|
||||||
if translator:
|
|
||||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
|
||||||
else:
|
|
||||||
print("验证码填写完成")
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
# 处理最后一次 Turnstile 验证
|
|
||||||
if handle_turnstile(browser_tab, translator):
|
|
||||||
if translator:
|
|
||||||
print(f"{Fore.GREEN}✅ {translator.get('register.verification_success')}{Style.RESET_ALL}")
|
|
||||||
else:
|
|
||||||
print("最后一次验证通过!")
|
|
||||||
time.sleep(2)
|
|
||||||
|
|
||||||
# 直接访问设置页面
|
|
||||||
if translator:
|
|
||||||
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")
|
|
||||||
time.sleep(3) # 等待页面加载
|
|
||||||
|
|
||||||
# 直接返回成功,让 cursor_register.py 处理账户信息获取
|
|
||||||
return True
|
|
||||||
|
|
||||||
else:
|
|
||||||
if translator:
|
|
||||||
print(f"{Fore.RED}❌ {translator.get('register.verification_failed')}{Style.RESET_ALL}")
|
|
||||||
else:
|
|
||||||
print("最后一次验证失败")
|
|
||||||
return False
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if translator:
|
if translator:
|
||||||
print(f"{Fore.RED}❌ {translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
|
print(f"{Fore.RED}❌ {translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"处理验证码时出错: {e}")
|
print(f"处理验证码时出错: {e}")
|
||||||
return False
|
return False, None
|
||||||
|
|
||||||
def handle_sign_in(browser_tab, email, password, translator=None):
|
def handle_sign_in(browser_tab, email, password, translator=None):
|
||||||
"""处理登录流程"""
|
"""处理登录流程"""
|
||||||
@@ -530,12 +560,8 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
|
|||||||
else:
|
else:
|
||||||
print("\n开始处理验证码...")
|
print("\n开始处理验证码...")
|
||||||
if handle_verification_code(page, email_tab, controller, email, password, translator):
|
if handle_verification_code(page, email_tab, controller, email, password, translator):
|
||||||
if translator:
|
|
||||||
print(f"\n{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
|
|
||||||
else:
|
|
||||||
print("\n注册流程完成!")
|
|
||||||
success = True
|
success = True
|
||||||
return True, page # 返回成功状态和浏览器实例
|
return True, page # 返回浏览器实例
|
||||||
else:
|
else:
|
||||||
print("\n验证码处理失败")
|
print("\n验证码处理失败")
|
||||||
else:
|
else:
|
||||||
|
|||||||
314
new_tempemail.py
314
new_tempemail.py
@@ -3,6 +3,9 @@ import time
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from colorama import Fore, Style, init
|
from colorama import Fore, Style, init
|
||||||
|
import requests
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
# 初始化 colorama
|
# 初始化 colorama
|
||||||
init()
|
init()
|
||||||
@@ -10,95 +13,184 @@ init()
|
|||||||
class NewTempEmail:
|
class NewTempEmail:
|
||||||
def __init__(self, translator=None):
|
def __init__(self, translator=None):
|
||||||
self.translator = translator
|
self.translator = translator
|
||||||
self.page = None
|
# Randomly choose between mail.tm and mail.gw
|
||||||
self.setup_browser()
|
self.services = [
|
||||||
|
{"name": "mail.tm", "api_url": "https://api.mail.tm"},
|
||||||
|
{"name": "mail.gw", "api_url": "https://api.mail.gw"}
|
||||||
|
]
|
||||||
|
self.selected_service = random.choice(self.services)
|
||||||
|
self.api_url = self.selected_service["api_url"]
|
||||||
|
self.token = None
|
||||||
|
self.email = None
|
||||||
|
self.password = None
|
||||||
|
self.blocked_domains = self.get_blocked_domains()
|
||||||
|
|
||||||
def get_extension_block(self):
|
def get_blocked_domains(self):
|
||||||
"""获取插件路径"""
|
"""Get blocked domains list"""
|
||||||
root_dir = os.getcwd()
|
|
||||||
extension_path = os.path.join(root_dir, "uBlock0.chromium")
|
|
||||||
|
|
||||||
if hasattr(sys, "_MEIPASS"):
|
|
||||||
extension_path = os.path.join(sys._MEIPASS, "uBlock0.chromium")
|
|
||||||
|
|
||||||
if not os.path.exists(extension_path):
|
|
||||||
raise FileNotFoundError(f"插件不存在: {extension_path}")
|
|
||||||
|
|
||||||
return extension_path
|
|
||||||
|
|
||||||
def setup_browser(self):
|
|
||||||
"""设置浏览器"""
|
|
||||||
try:
|
try:
|
||||||
if self.translator:
|
block_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/block_domain.txt"
|
||||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.starting_browser')}{Style.RESET_ALL}")
|
response = requests.get(block_url, timeout=5)
|
||||||
else:
|
if response.status_code == 200:
|
||||||
print(f"{Fore.CYAN}ℹ️ 正在启动浏览器...{Style.RESET_ALL}")
|
# Split text and remove empty lines
|
||||||
|
domains = [line.strip() for line in response.text.split('\n') if line.strip()]
|
||||||
# 创建浏览器选项
|
|
||||||
co = ChromiumOptions()
|
|
||||||
co.set_argument("--headless=new")
|
|
||||||
|
|
||||||
co.auto_port() # 自动设置端口
|
|
||||||
|
|
||||||
# 加载 uBlock 插件
|
|
||||||
try:
|
|
||||||
extension_path = self.get_extension_block()
|
|
||||||
co.set_argument("--allow-extensions-in-incognito")
|
|
||||||
co.add_extension(extension_path)
|
|
||||||
except Exception as e:
|
|
||||||
if self.translator:
|
if self.translator:
|
||||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.extension_load_error')}: {str(e)}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.YELLOW}⚠️ 加载插件失败: {str(e)}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}ℹ️ 已加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
|
||||||
|
return domains
|
||||||
self.page = ChromiumPage(co)
|
return []
|
||||||
return True
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if self.translator:
|
if self.translator:
|
||||||
print(f"{Fore.RED}❌ {self.translator.get('email.browser_start_error')}: {str(e)}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}⚠️ 获取被屏蔽域名列表失败: {str(e)}{Style.RESET_ALL}")
|
||||||
return False
|
return []
|
||||||
|
|
||||||
|
def exclude_blocked_domains(self, domains):
|
||||||
|
"""Exclude blocked domains"""
|
||||||
|
if not self.blocked_domains:
|
||||||
|
return domains
|
||||||
|
|
||||||
|
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):
|
||||||
|
"""生成随机用户名和密码"""
|
||||||
|
username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
|
||||||
|
password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=12))
|
||||||
|
return username, password
|
||||||
|
|
||||||
def create_email(self):
|
def create_email(self):
|
||||||
"""创建临时邮箱"""
|
"""创建临时邮箱"""
|
||||||
try:
|
try:
|
||||||
if self.translator:
|
if self.translator:
|
||||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.visiting_site')}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.visiting_site').replace('mail.tm', self.selected_service['name'])}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.CYAN}ℹ️ 正在访问 smailpro.com...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}ℹ️ 正在访问 {self.selected_service['name']}...{Style.RESET_ALL}")
|
||||||
|
|
||||||
# 访问网站
|
# 获取可用域名列表
|
||||||
self.page.get("https://smailpro.com/")
|
try:
|
||||||
time.sleep(2)
|
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}")
|
||||||
create_button = self.page.ele('xpath://button[@title="Create temporary email"]')
|
print(f"{Fore.RED}❌ {self.translator.get('email.domains_list_error', error=domains_response.text)}{Style.RESET_ALL}")
|
||||||
if create_button:
|
raise Exception(f"{self.translator.get('email.failed_to_get_available_domains') if self.translator else 'Failed to get available domains'}")
|
||||||
create_button.click()
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
# 点击弹窗中的 Create 按钮
|
|
||||||
modal_create_button = self.page.ele('xpath://button[contains(text(), "Create")]')
|
|
||||||
if modal_create_button:
|
|
||||||
modal_create_button.click()
|
|
||||||
time.sleep(2)
|
|
||||||
|
|
||||||
# 获取邮箱地址 - 修改选择器
|
domains = domains_response.json()["hydra:member"]
|
||||||
email_div = self.page.ele('xpath://div[@class="text-base sm:text-lg md:text-xl text-gray-700"]')
|
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.available_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
|
||||||
if email_div:
|
|
||||||
email = email_div.text.strip()
|
if not domains:
|
||||||
if '@' in email: # 验证是否是有效的邮箱地址
|
raise Exception(f"{self.translator.get('email.no_available_domains') if self.translator else '没有可用域名'}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}❌ 获取域名列表时出错: {str(e)}{Style.RESET_ALL}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# 排除被屏蔽的域名
|
||||||
|
try:
|
||||||
|
filtered_domains = self.exclude_blocked_domains(domains)
|
||||||
|
if self.translator:
|
||||||
|
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.domains_filtered', count=len(filtered_domains))}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.CYAN}ℹ️ 过滤后剩余 {len(filtered_domains)} 个可用域名{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
if not filtered_domains:
|
||||||
|
if self.translator:
|
||||||
|
print(f"{Fore.RED}❌ {self.translator.get('email.all_domains_blocked')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.RED}❌ 所有域名都被屏蔽了,尝试切换服务{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# 切换到另一个服务
|
||||||
|
for service in self.services:
|
||||||
|
if service["api_url"] != self.api_url:
|
||||||
|
self.selected_service = service
|
||||||
|
self.api_url = service["api_url"]
|
||||||
if self.translator:
|
if self.translator:
|
||||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.switching_service', service=service['name'])}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}ℹ️ 切换到 {service['name']} 服务{Style.RESET_ALL}")
|
||||||
return email
|
return self.create_email() # 递归调用
|
||||||
|
|
||||||
|
raise Exception(f"{self.translator.get('email.no_available_domains_after_filtering') if self.translator else '过滤后没有可用域名'}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}❌ 过滤域名时出错: {str(e)}{Style.RESET_ALL}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# 生成随机用户名和密码
|
||||||
|
try:
|
||||||
|
username, password = self._generate_credentials()
|
||||||
|
self.password = password
|
||||||
|
|
||||||
|
# 创建邮箱账户
|
||||||
|
selected_domain = filtered_domains[0]['domain']
|
||||||
|
email = f"{username}@{selected_domain}"
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# 创建账户
|
||||||
|
try:
|
||||||
|
create_response = requests.post(f"{self.api_url}/accounts", json=account_data, timeout=15)
|
||||||
|
|
||||||
|
if create_response.status_code != 201:
|
||||||
|
print(f"{Fore.RED}❌ 创建账户失败: 状态码 {create_response.status_code}{Style.RESET_ALL}")
|
||||||
|
print(f"{Fore.RED}❌ 响应内容: {create_response.text}{Style.RESET_ALL}")
|
||||||
|
|
||||||
|
# 如果是域名问题,尝试下一个域名
|
||||||
|
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}")
|
||||||
|
# 将当前域名添加到屏蔽列表
|
||||||
|
if selected_domain not in self.blocked_domains:
|
||||||
|
self.blocked_domains.append(selected_domain)
|
||||||
|
# 递归调用自己
|
||||||
|
return self.create_email()
|
||||||
|
|
||||||
|
raise Exception(f"{self.translator.get('email.failed_to_create_account') if self.translator else '创建账户失败'}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"{Fore.RED}❌ 创建账户时出错: {str(e)}{Style.RESET_ALL}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
# 获取访问令牌
|
||||||
|
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:
|
||||||
|
print(f"{Fore.RED}❌ 获取令牌失败: 状态码 {token_response.status_code}{Style.RESET_ALL}")
|
||||||
|
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:
|
if self.translator:
|
||||||
print(f"{Fore.RED}❌ {self.translator.get('email.create_failed')}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}✅ {self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.RED}❌ 创建邮箱失败{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}")
|
||||||
return None
|
return email
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if self.translator:
|
if self.translator:
|
||||||
@@ -120,11 +212,11 @@ class NewTempEmail:
|
|||||||
else:
|
else:
|
||||||
print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}")
|
||||||
|
|
||||||
# 点击刷新按钮
|
# 使用 API 获取最新邮件
|
||||||
refresh_button = self.page.ele('xpath://button[@id="refresh"]')
|
headers = {"Authorization": f"Bearer {self.token}"}
|
||||||
if refresh_button:
|
response = requests.get(f"{self.api_url}/messages", headers=headers)
|
||||||
refresh_button.click()
|
|
||||||
time.sleep(2) # 等待刷新完成
|
if response.status_code == 200:
|
||||||
if self.translator:
|
if self.translator:
|
||||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.refresh_success')}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}✅ {self.translator.get('email.refresh_success')}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
@@ -132,9 +224,9 @@ class NewTempEmail:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
if self.translator:
|
if self.translator:
|
||||||
print(f"{Fore.RED}❌ {self.translator.get('email.refresh_button_not_found')}{Style.RESET_ALL}")
|
print(f"{Fore.RED}❌ {self.translator.get('email.refresh_failed')}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.RED}❌ 未找到刷新按钮{Style.RESET_ALL}")
|
print(f"{Fore.RED}❌ 刷新邮箱失败{Style.RESET_ALL}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -147,17 +239,24 @@ class NewTempEmail:
|
|||||||
def check_for_cursor_email(self):
|
def check_for_cursor_email(self):
|
||||||
"""检查是否有 Cursor 的验证邮件"""
|
"""检查是否有 Cursor 的验证邮件"""
|
||||||
try:
|
try:
|
||||||
# 查找验证邮件 - 使用更精确的选择器
|
# 使用 API 获取邮件列表
|
||||||
email_div = self.page.ele('xpath://div[contains(@class, "p-2") and contains(@class, "cursor-pointer") and contains(@class, "bg-white") and contains(@class, "shadow") and .//b[text()="no-reply@cursor.sh"] and .//span[text()="Verify your email address"]]')
|
headers = {"Authorization": f"Bearer {self.token}"}
|
||||||
if email_div:
|
response = requests.get(f"{self.api_url}/messages", headers=headers)
|
||||||
if self.translator:
|
|
||||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.verification_found')}{Style.RESET_ALL}")
|
if response.status_code == 200:
|
||||||
else:
|
messages = response.json()["hydra:member"]
|
||||||
print(f"{Fore.GREEN}✅ 找到验证邮件{Style.RESET_ALL}")
|
for message in messages:
|
||||||
# 使用 JavaScript 点击元素
|
if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]:
|
||||||
self.page.run_js('arguments[0].click()', email_div)
|
# 获取邮件内容
|
||||||
time.sleep(2) # 等待邮件内容加载
|
message_id = message["id"]
|
||||||
return True
|
message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers)
|
||||||
|
if message_response.status_code == 200:
|
||||||
|
if self.translator:
|
||||||
|
print(f"{Fore.GREEN}✅ {self.translator.get('email.verification_found')}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.GREEN}✅ 找到验证邮件{Style.RESET_ALL}")
|
||||||
|
return True
|
||||||
|
|
||||||
if self.translator:
|
if self.translator:
|
||||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_not_found')}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_not_found')}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
@@ -174,16 +273,33 @@ class NewTempEmail:
|
|||||||
def get_verification_code(self):
|
def get_verification_code(self):
|
||||||
"""获取验证码"""
|
"""获取验证码"""
|
||||||
try:
|
try:
|
||||||
# 查找验证码元素
|
# 使用 API 获取邮件列表
|
||||||
code_element = self.page.ele('xpath://td//div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]')
|
headers = {"Authorization": f"Bearer {self.token}"}
|
||||||
if code_element:
|
response = requests.get(f"{self.api_url}/messages", headers=headers)
|
||||||
code = code_element.text.strip()
|
|
||||||
if code.isdigit() and len(code) == 6:
|
if response.status_code == 200:
|
||||||
if self.translator:
|
messages = response.json()["hydra:member"]
|
||||||
print(f"{Fore.GREEN}✅ {self.translator.get('email.verification_code_found')}: {code}{Style.RESET_ALL}")
|
for message in messages:
|
||||||
else:
|
if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]:
|
||||||
print(f"{Fore.GREEN}✅ 获取验证码成功: {code}{Style.RESET_ALL}")
|
# 获取邮件内容
|
||||||
return code
|
message_id = message["id"]
|
||||||
|
message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers)
|
||||||
|
|
||||||
|
if message_response.status_code == 200:
|
||||||
|
# 从邮件内容中提取验证码
|
||||||
|
email_content = message_response.json()["text"]
|
||||||
|
# 查找6位数字验证码
|
||||||
|
import re
|
||||||
|
code_match = re.search(r'\b\d{6}\b', email_content)
|
||||||
|
|
||||||
|
if code_match:
|
||||||
|
code = code_match.group(0)
|
||||||
|
if self.translator:
|
||||||
|
print(f"{Fore.GREEN}✅ {self.translator.get('email.verification_code_found')}: {code}{Style.RESET_ALL}")
|
||||||
|
else:
|
||||||
|
print(f"{Fore.GREEN}✅ 获取验证码成功: {code}{Style.RESET_ALL}")
|
||||||
|
return code
|
||||||
|
|
||||||
if self.translator:
|
if self.translator:
|
||||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_code_not_found')}{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_code_not_found')}{Style.RESET_ALL}")
|
||||||
else:
|
else:
|
||||||
@@ -223,4 +339,4 @@ def main(translator=None):
|
|||||||
temp_email.close()
|
temp_email.close()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
@@ -4,10 +4,10 @@ from colorama import Fore, Style, init
|
|||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
# 初始化colorama
|
# Initialize colorama
|
||||||
init()
|
init()
|
||||||
|
|
||||||
# 定义emoji常量
|
# Define emoji constants
|
||||||
EMOJI = {
|
EMOJI = {
|
||||||
"PROCESS": "⚙️",
|
"PROCESS": "⚙️",
|
||||||
"SUCCESS": "✅",
|
"SUCCESS": "✅",
|
||||||
@@ -19,15 +19,15 @@ EMOJI = {
|
|||||||
class CursorQuitter:
|
class CursorQuitter:
|
||||||
def __init__(self, timeout=5, translator=None):
|
def __init__(self, timeout=5, translator=None):
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.translator = translator # 使用传入的翻译器
|
self.translator = translator # Use the passed translator
|
||||||
|
|
||||||
def quit_cursor(self):
|
def quit_cursor(self):
|
||||||
"""温和地关闭 Cursor 进程"""
|
"""Gently close Cursor processes"""
|
||||||
try:
|
try:
|
||||||
print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('quit_cursor.start')}...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['PROCESS']} {self.translator.get('quit_cursor.start')}...{Style.RESET_ALL}")
|
||||||
cursor_processes = []
|
cursor_processes = []
|
||||||
|
|
||||||
# 收集所有 Cursor 进程
|
# Collect all Cursor processes
|
||||||
for proc in psutil.process_iter(['pid', 'name']):
|
for proc in psutil.process_iter(['pid', 'name']):
|
||||||
try:
|
try:
|
||||||
if proc.info['name'].lower() in ['cursor.exe', 'cursor']:
|
if proc.info['name'].lower() in ['cursor.exe', 'cursor']:
|
||||||
@@ -39,7 +39,7 @@ class CursorQuitter:
|
|||||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('quit_cursor.no_process')}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('quit_cursor.no_process')}{Style.RESET_ALL}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# 温和地请求进程终止
|
# Gently request processes to terminate
|
||||||
for proc in cursor_processes:
|
for proc in cursor_processes:
|
||||||
try:
|
try:
|
||||||
if proc.is_running():
|
if proc.is_running():
|
||||||
@@ -48,7 +48,7 @@ class CursorQuitter:
|
|||||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 等待进程自然终止
|
# Wait for processes to terminate naturally
|
||||||
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('quit_cursor.waiting')}...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('quit_cursor.waiting')}...{Style.RESET_ALL}")
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
while time.time() - start_time < self.timeout:
|
while time.time() - start_time < self.timeout:
|
||||||
@@ -66,7 +66,7 @@ class CursorQuitter:
|
|||||||
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
# 如果超时后仍有进程在运行
|
# If processes are still running after timeout
|
||||||
if still_running:
|
if still_running:
|
||||||
process_list = ", ".join([str(p.pid) for p in still_running])
|
process_list = ", ".join([str(p.pid) for p in still_running])
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('quit_cursor.timeout', pids=process_list)}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('quit_cursor.timeout', pids=process_list)}{Style.RESET_ALL}")
|
||||||
@@ -79,11 +79,11 @@ class CursorQuitter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def quit_cursor(translator=None, timeout=5):
|
def quit_cursor(translator=None, timeout=5):
|
||||||
"""便捷函数,用于直接调用退出功能"""
|
"""Convenient function for directly calling the quit function"""
|
||||||
quitter = CursorQuitter(timeout, translator)
|
quitter = CursorQuitter(timeout, translator)
|
||||||
return quitter.quit_cursor()
|
return quitter.quit_cursor()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 如果直接运行,使用默认翻译器
|
# If run directly, use the default translator
|
||||||
from main import translator as main_translator
|
from main import translator as main_translator
|
||||||
quit_cursor(main_translator)
|
quit_cursor(main_translator)
|
||||||
@@ -11,10 +11,10 @@ import tempfile
|
|||||||
from colorama import Fore, Style, init
|
from colorama import Fore, Style, init
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
|
||||||
# 初始化colorama
|
# Initialize colorama
|
||||||
init()
|
init()
|
||||||
|
|
||||||
# 定义emoji常量
|
# Define emoji constants
|
||||||
EMOJI = {
|
EMOJI = {
|
||||||
"FILE": "📄",
|
"FILE": "📄",
|
||||||
"BACKUP": "💾",
|
"BACKUP": "💾",
|
||||||
@@ -25,7 +25,7 @@ EMOJI = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
||||||
"""根据不同操作系统获取 Cursor 相关路径"""
|
""" Get Cursor related paths"""
|
||||||
system = platform.system()
|
system = platform.system()
|
||||||
|
|
||||||
paths_map = {
|
paths_map = {
|
||||||
@@ -65,7 +65,7 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool:
|
def version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool:
|
||||||
"""版本号检查"""
|
"""Version number check"""
|
||||||
version_pattern = r"^\d+\.\d+\.\d+$"
|
version_pattern = r"^\d+\.\d+\.\d+$"
|
||||||
try:
|
try:
|
||||||
if not re.match(version_pattern, version):
|
if not re.match(version_pattern, version):
|
||||||
@@ -92,7 +92,7 @@ def version_check(version: str, min_version: str = "", max_version: str = "", tr
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def check_cursor_version(translator) -> bool:
|
def check_cursor_version(translator) -> bool:
|
||||||
"""检查 Cursor 版本"""
|
"""Check Cursor version"""
|
||||||
try:
|
try:
|
||||||
pkg_path, _ = get_cursor_paths(translator)
|
pkg_path, _ = get_cursor_paths(translator)
|
||||||
with open(pkg_path, "r", encoding="utf-8") as f:
|
with open(pkg_path, "r", encoding="utf-8") as f:
|
||||||
@@ -103,7 +103,7 @@ def check_cursor_version(translator) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def modify_main_js(main_path: str, translator) -> bool:
|
def modify_main_js(main_path: str, translator) -> bool:
|
||||||
"""修改 main.js 文件"""
|
"""Modify main.js file"""
|
||||||
try:
|
try:
|
||||||
original_stat = os.stat(main_path)
|
original_stat = os.stat(main_path)
|
||||||
original_mode = original_stat.st_mode
|
original_mode = original_stat.st_mode
|
||||||
@@ -142,14 +142,14 @@ def modify_main_js(main_path: str, translator) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def patch_cursor_get_machine_id(translator) -> bool:
|
def patch_cursor_get_machine_id(translator) -> bool:
|
||||||
"""修补 Cursor getMachineId 函数"""
|
"""Patch Cursor getMachineId function"""
|
||||||
try:
|
try:
|
||||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.start_patching')}...{Style.RESET_ALL}")
|
||||||
|
|
||||||
# 获取路径
|
# Get paths
|
||||||
pkg_path, main_path = get_cursor_paths(translator)
|
pkg_path, main_path = get_cursor_paths(translator)
|
||||||
|
|
||||||
# 检查文件权限
|
# Check file permissions
|
||||||
for file_path in [pkg_path, main_path]:
|
for file_path in [pkg_path, main_path]:
|
||||||
if not os.path.isfile(file_path):
|
if not os.path.isfile(file_path):
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.file_not_found', path=file_path)}{Style.RESET_ALL}")
|
||||||
@@ -158,7 +158,7 @@ def patch_cursor_get_machine_id(translator) -> bool:
|
|||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_write_permission', path=file_path)}{Style.RESET_ALL}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 获取版本号
|
# Get version number
|
||||||
try:
|
try:
|
||||||
with open(pkg_path, "r", encoding="utf-8") as f:
|
with open(pkg_path, "r", encoding="utf-8") as f:
|
||||||
version = json.load(f)["version"]
|
version = json.load(f)["version"]
|
||||||
@@ -167,20 +167,20 @@ def patch_cursor_get_machine_id(translator) -> bool:
|
|||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.read_version_failed', error=str(e))}{Style.RESET_ALL}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 检查版本
|
# Check version
|
||||||
if not version_check(version, min_version="0.45.0", translator=translator):
|
if not version_check(version, min_version="0.45.0", translator=translator):
|
||||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}")
|
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_not_supported')}{Style.RESET_ALL}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
|
||||||
|
|
||||||
# 备份文件
|
# Backup file
|
||||||
backup_path = main_path + ".bak"
|
backup_path = main_path + ".bak"
|
||||||
if not os.path.exists(backup_path):
|
if not os.path.exists(backup_path):
|
||||||
shutil.copy2(main_path, backup_path)
|
shutil.copy2(main_path, backup_path)
|
||||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||||
|
|
||||||
# 修改文件
|
# Modify file
|
||||||
if not modify_main_js(main_path, translator):
|
if not modify_main_js(main_path, translator):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ class MachineIDResetter:
|
|||||||
def __init__(self, translator=None):
|
def __init__(self, translator=None):
|
||||||
self.translator = translator
|
self.translator = translator
|
||||||
|
|
||||||
# 判断操作系统
|
# Check operating system
|
||||||
if sys.platform == "win32": # Windows
|
if sys.platform == "win32": # Windows
|
||||||
appdata = os.getenv("APPDATA")
|
appdata = os.getenv("APPDATA")
|
||||||
if appdata is None:
|
if appdata is None:
|
||||||
@@ -224,17 +224,17 @@ class MachineIDResetter:
|
|||||||
raise NotImplementedError(f"Not Supported OS: {sys.platform}")
|
raise NotImplementedError(f"Not Supported OS: {sys.platform}")
|
||||||
|
|
||||||
def generate_new_ids(self):
|
def generate_new_ids(self):
|
||||||
"""生成新的机器ID"""
|
"""Generate new machine ID"""
|
||||||
# 生成新的UUID
|
# Generate new UUID
|
||||||
dev_device_id = str(uuid.uuid4())
|
dev_device_id = str(uuid.uuid4())
|
||||||
|
|
||||||
# 生成新的machineId (64个字符的十六进制)
|
# Generate new machineId (64 characters of hexadecimal)
|
||||||
machine_id = hashlib.sha256(os.urandom(32)).hexdigest()
|
machine_id = hashlib.sha256(os.urandom(32)).hexdigest()
|
||||||
|
|
||||||
# 生成新的macMachineId (128个字符的十六进制)
|
# Generate new macMachineId (128 characters of hexadecimal)
|
||||||
mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest()
|
mac_machine_id = hashlib.sha512(os.urandom(64)).hexdigest()
|
||||||
|
|
||||||
# 生成新的sqmId
|
# Generate new sqmId
|
||||||
sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
|
sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -242,11 +242,11 @@ class MachineIDResetter:
|
|||||||
"telemetry.macMachineId": mac_machine_id,
|
"telemetry.macMachineId": mac_machine_id,
|
||||||
"telemetry.machineId": machine_id,
|
"telemetry.machineId": machine_id,
|
||||||
"telemetry.sqmId": sqm_id,
|
"telemetry.sqmId": sqm_id,
|
||||||
"storage.serviceMachineId": dev_device_id, # 添加 storage.serviceMachineId
|
"storage.serviceMachineId": dev_device_id, # Add storage.serviceMachineId
|
||||||
}
|
}
|
||||||
|
|
||||||
def update_sqlite_db(self, new_ids):
|
def update_sqlite_db(self, new_ids):
|
||||||
"""更新 SQLite 数据库中的机器ID"""
|
"""Update machine ID in SQLite database"""
|
||||||
try:
|
try:
|
||||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_sqlite')}...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_sqlite')}...{Style.RESET_ALL}")
|
||||||
|
|
||||||
@@ -281,7 +281,7 @@ class MachineIDResetter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def update_system_ids(self, new_ids):
|
def update_system_ids(self, new_ids):
|
||||||
"""更新系统级别的ID"""
|
"""Update system-level IDs"""
|
||||||
try:
|
try:
|
||||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_system_ids')}...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.updating_system_ids')}...{Style.RESET_ALL}")
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ class MachineIDResetter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _update_windows_machine_guid(self):
|
def _update_windows_machine_guid(self):
|
||||||
"""更新Windows MachineGuid"""
|
"""Update Windows MachineGuid"""
|
||||||
try:
|
try:
|
||||||
import winreg
|
import winreg
|
||||||
key = winreg.OpenKey(
|
key = winreg.OpenKey(
|
||||||
@@ -318,11 +318,11 @@ class MachineIDResetter:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def _update_macos_platform_uuid(self, new_ids):
|
def _update_macos_platform_uuid(self, new_ids):
|
||||||
"""更新macOS Platform UUID"""
|
"""Update macOS Platform UUID"""
|
||||||
try:
|
try:
|
||||||
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
|
uuid_file = "/var/root/Library/Preferences/SystemConfiguration/com.apple.platform.uuid.plist"
|
||||||
if os.path.exists(uuid_file):
|
if os.path.exists(uuid_file):
|
||||||
# 使用sudo来执行plutil命令
|
# Use sudo to execute plutil command
|
||||||
cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"'
|
cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"'
|
||||||
result = os.system(cmd)
|
result = os.system(cmd)
|
||||||
if result == 0:
|
if result == 0:
|
||||||
@@ -334,7 +334,7 @@ class MachineIDResetter:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
def reset_machine_ids(self):
|
def reset_machine_ids(self):
|
||||||
"""重置机器ID并备份原文件"""
|
"""Reset machine ID and backup original file"""
|
||||||
try:
|
try:
|
||||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.checking')}...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.checking')}...{Style.RESET_ALL}")
|
||||||
|
|
||||||
@@ -360,26 +360,26 @@ class MachineIDResetter:
|
|||||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
|
||||||
new_ids = self.generate_new_ids()
|
new_ids = self.generate_new_ids()
|
||||||
|
|
||||||
# 更新配置文件
|
# Update configuration file
|
||||||
config.update(new_ids)
|
config.update(new_ids)
|
||||||
|
|
||||||
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.saving_json')}...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['FILE']} {self.translator.get('reset.saving_json')}...{Style.RESET_ALL}")
|
||||||
with open(self.db_path, "w", encoding="utf-8") as f:
|
with open(self.db_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(config, f, indent=4)
|
json.dump(config, f, indent=4)
|
||||||
|
|
||||||
# 更新SQLite数据库
|
# Update SQLite database
|
||||||
self.update_sqlite_db(new_ids)
|
self.update_sqlite_db(new_ids)
|
||||||
|
|
||||||
# 更新系统ID
|
# Update system IDs
|
||||||
self.update_system_ids(new_ids)
|
self.update_system_ids(new_ids)
|
||||||
|
|
||||||
# 检查 Cursor 版本并执行相应的操作
|
# Check Cursor version and perform corresponding actions
|
||||||
greater_than_0_45 = check_cursor_version(self.translator)
|
greater_than_0_45 = check_cursor_version(self.translator)
|
||||||
if greater_than_0_45:
|
if greater_than_0_45:
|
||||||
print(f"{Fore.CYAN}{EMOJI['INFO']} 检测到 Cursor 版本 >= 0.45.0,正在修补 getMachineId...{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.detecting_version')} >= 0.45.0,{self.translator.get('reset.patching_getmachineid')}{Style.RESET_ALL}")
|
||||||
patch_cursor_get_machine_id(self.translator)
|
patch_cursor_get_machine_id(self.translator)
|
||||||
else:
|
else:
|
||||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} Cursor 版本 < 0.45.0,跳过 getMachineId 修补{Style.RESET_ALL}")
|
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.version_less_than_0_45')}{Style.RESET_ALL}")
|
||||||
|
|
||||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.success')}{Style.RESET_ALL}")
|
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.success')}{Style.RESET_ALL}")
|
||||||
print(f"\n{Fore.CYAN}{self.translator.get('reset.new_id')}:{Style.RESET_ALL}")
|
print(f"\n{Fore.CYAN}{self.translator.get('reset.new_id')}:{Style.RESET_ALL}")
|
||||||
@@ -397,12 +397,12 @@ class MachineIDResetter:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def run(translator=None):
|
def run(translator=None):
|
||||||
"""便捷函数,用于直接调用重置功能"""
|
"""Convenient function for directly calling the reset function"""
|
||||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}")
|
||||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
|
||||||
resetter = MachineIDResetter(translator) # 正確傳遞 translator
|
resetter = MachineIDResetter(translator) # Correctly pass translator
|
||||||
resetter.reset_machine_ids()
|
resetter.reset_machine_ids()
|
||||||
|
|
||||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||||
|
|||||||
@@ -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')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user